From 507e34271f86facb46494ca0107ff32a14c64c36 Mon Sep 17 00:00:00 2001 From: NeOMakinG <14963751+NeOMakinG@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:51:14 +0200 Subject: [PATCH 01/52] feat: implement phantom --- examples/sandbox/index.html | 1 + examples/sandbox/index.ts | 22 ++ examples/sandbox/package.json | 1 + packages/hdwallet-phantom/package.json | 24 ++ packages/hdwallet-phantom/src/adapter.ts | 57 +++ packages/hdwallet-phantom/src/ethereum.ts | 132 +++++++ packages/hdwallet-phantom/src/index.ts | 2 + packages/hdwallet-phantom/src/phantom.test.ts | 168 ++++++++ packages/hdwallet-phantom/src/phantom.ts | 363 ++++++++++++++++++ packages/hdwallet-phantom/tsconfig.json | 10 + tsconfig.json | 1 + 11 files changed, 781 insertions(+) create mode 100644 packages/hdwallet-phantom/package.json create mode 100644 packages/hdwallet-phantom/src/adapter.ts create mode 100644 packages/hdwallet-phantom/src/ethereum.ts create mode 100644 packages/hdwallet-phantom/src/index.ts create mode 100644 packages/hdwallet-phantom/src/phantom.test.ts create mode 100644 packages/hdwallet-phantom/src/phantom.ts create mode 100644 packages/hdwallet-phantom/tsconfig.json diff --git a/examples/sandbox/index.html b/examples/sandbox/index.html index f70b1a1b5..a24103b82 100644 --- a/examples/sandbox/index.html +++ b/examples/sandbox/index.html @@ -116,6 +116,7 @@

Select

+ diff --git a/examples/sandbox/index.ts b/examples/sandbox/index.ts index 51a3d2875..0065fddfc 100644 --- a/examples/sandbox/index.ts +++ b/examples/sandbox/index.ts @@ -11,6 +11,7 @@ import * as keplr from "@shapeshiftoss/hdwallet-keplr"; import * as ledgerWebHID from "@shapeshiftoss/hdwallet-ledger-webhid"; import * as ledgerWebUSB from "@shapeshiftoss/hdwallet-ledger-webusb"; import * as metaMask from "@shapeshiftoss/hdwallet-metamask"; +import * as phantom from "@shapeshiftoss/hdwallet-phantom"; import * as native from "@shapeshiftoss/hdwallet-native"; import * as portis from "@shapeshiftoss/hdwallet-portis"; import * as tallyHo from "@shapeshiftoss/hdwallet-tallyho"; @@ -127,6 +128,7 @@ const kkbridgeAdapter = keepkeyTcp.TCPKeepKeyAdapter.useKeyring(keyring); const kkemuAdapter = keepkeyTcp.TCPKeepKeyAdapter.useKeyring(keyring); const portisAdapter = portis.PortisAdapter.useKeyring(keyring, { portisAppId }); const metaMaskAdapter = metaMask.MetaMaskAdapter.useKeyring(keyring); +const phantomAdapter = phantom.PhantomAdapter.useKeyring(keyring); const tallyHoAdapter = tallyHo.TallyHoAdapter.useKeyring(keyring); const walletConnectAdapter = walletConnect.WalletConnectAdapter.useKeyring(keyring, walletConnectOptions); const walletConnectV2Adapter = walletConnectv2.WalletConnectV2Adapter.useKeyring(keyring, walletConnectV2Options); @@ -159,6 +161,7 @@ const $ledgerwebhid = $("#ledgerwebhid"); const $portis = $("#portis"); const $native = $("#native"); const $metaMask = $("#metaMask"); +const $phantom = $("#phantom"); const $coinbase = $("#coinbase"); const $tallyHo = $("#tallyHo"); const $walletConnect = $("#walletConnect"); @@ -243,6 +246,19 @@ $metaMask.on("click", async (e) => { } }); +$phantom.on("click", async (e) => { + e.preventDefault(); + wallet = await phantomAdapter.pairDevice(); + window["wallet"] = wallet; + let deviceID = "nothing"; + try { + deviceID = await wallet.getDeviceID(); + $("#keyring select").val(deviceID); + } catch (err) { + console.error(err); + } +}); + $coinbase.on("click", async (e) => { e.preventDefault(); wallet = await coinbaseAdapter.pairDevice(); @@ -405,6 +421,12 @@ async function deviceConnected(deviceId) { console.error("Could not initialize MetaMaskAdapter", e); } + try { + await phantomAdapter.initialize(); + } catch (e) { + console.error("Could not initialize PhantomAdapter", e); + } + try { await tallyHoAdapter.initialize(); } catch (e) { diff --git a/examples/sandbox/package.json b/examples/sandbox/package.json index c90a15065..9e92e8657 100644 --- a/examples/sandbox/package.json +++ b/examples/sandbox/package.json @@ -19,6 +19,7 @@ "@shapeshiftoss/hdwallet-ledger-webhid": "1.54.2", "@shapeshiftoss/hdwallet-ledger-webusb": "1.54.2", "@shapeshiftoss/hdwallet-metamask": "1.54.2", + "@shapeshiftoss/hdwallet-phantom": "1.54.2", "@shapeshiftoss/hdwallet-native": "1.54.2", "@shapeshiftoss/hdwallet-portis": "1.54.2", "@shapeshiftoss/hdwallet-tallyho": "1.54.2", diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json new file mode 100644 index 000000000..3c454984d --- /dev/null +++ b/packages/hdwallet-phantom/package.json @@ -0,0 +1,24 @@ +{ + "name": "@shapeshiftoss/hdwallet-phantom", + "version": "1.54.2", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "dist/index.js", + "source": "src/index.ts", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc --build", + "clean": "rm -rf dist node_modules tsconfig.tsbuildinfo", + "prepublishOnly": "yarn clean && yarn build" + }, + "dependencies": { + "@shapeshiftoss/hdwallet-core": "1.54.2", + "eth-rpc-errors": "^4.0.3", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@types/lodash": "^4.14.168" + } +} diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts new file mode 100644 index 000000000..58d01661b --- /dev/null +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -0,0 +1,57 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; +import { providers } from "ethers"; + +import { PhantomHDWallet } from "./phantom"; + +declare global { + interface Window { + phantom?: { + ethereum?: providers.ExternalProvider; + }; + } +} + +export class PhantomAdapter { + keyring: core.Keyring; + + private constructor(keyring: core.Keyring) { + this.keyring = keyring; + } + + public static useKeyring(keyring: core.Keyring) { + return new PhantomAdapter(keyring); + } + + public async initialize(): Promise { + return Object.keys(this.keyring.wallets).length; + } + + public async pairDevice(): Promise { + const provider = window.phantom?.ethereum; + + if (!provider) { + window.open("https://phantom.app/", "_blank"); + console.error("Please install Phantom!"); + throw new Error("Phantom provider not found"); + } + + try { + await provider.request?.({ method: "eth_requestAccounts" }).catch(() => + provider.request?.({ + method: "wallet_requestPermissions", + params: [{ eth_accounts: {} }], + }) + ); + } catch (error) { + console.error("Could not get Phantom accounts. "); + throw error; + } + const wallet = new PhantomHDWallet(provider); + await wallet.initialize(); + const deviceID = await wallet.getDeviceID(); + this.keyring.add(wallet, deviceID); + this.keyring.emit(["Phantom", deviceID, core.Events.CONNECT], deviceID); + + return wallet; + } +} diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts new file mode 100644 index 000000000..3f910a826 --- /dev/null +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -0,0 +1,132 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; +import { ETHSignedMessage } from "@shapeshiftoss/hdwallet-core"; +import { isHexString } from "ethers/lib/utils"; + +export function describeETHPath(path: core.BIP32Path): core.PathDescription { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { + verbose: pathStr, + coin: "Ethereum", + isKnown: false, + }; + + if (path.length !== 5) return unknown; + + if (path[0] !== 0x80000000 + 44) return unknown; + + if (path[1] !== 0x80000000 + core.slip44ByCoin("Ethereum")) return unknown; + + if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) return unknown; + + if (path[3] !== 0) return unknown; + + if (path[4] !== 0) return unknown; + + const index = path[2] & 0x7fffffff; + return { + verbose: `Ethereum Account #${index}`, + accountIdx: index, + wholeAccount: true, + coin: "Ethereum", + isKnown: true, + }; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export async function ethVerifyMessage(msg: core.ETHVerifyMessage, ethereum: any): Promise { + console.error("Method ethVerifyMessage unsupported for Phantom wallet!"); + return null; +} + +export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { + const slip44 = core.slip44ByCoin(msg.coin); + if (slip44 === undefined) return []; + return [ + { + addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], + hardenedPath: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx], + relPath: [0, 0], + description: "Phantom", + }, + ]; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export async function ethSignTx(msg: core.ETHSignTx, ethereum: any, from: string): Promise { + console.error("Method ethSignTx unsupported for Phantom wallet!"); + return null; +} + +export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string): Promise { + try { + const utxBase = { + from: from, + to: msg.to, + value: msg.value, + chainId: msg.chainId, + data: msg.data && msg.data !== "" ? msg.data : undefined, + // Phantom, like other Web3 libraries, derives its transaction schema from Ethereum's official JSON-RPC API specification + // (https://github.com/ethereum/execution-apis/blob/d63d2a02bcd2a8cef54ae2fc5bbff8b4fac944eb/src/schemas/transaction.json). + // That schema defines the use of the label `gas` to set the transaction's gas limit and not `gasLimit` as used in other + // libraries and as stated in the official yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf). + gas: msg.gasLimit, + }; + + const utx = msg.maxFeePerGas + ? { + ...utxBase, + maxFeePerGas: msg.maxFeePerGas, + maxPriorityFeePerGas: msg.maxPriorityFeePerGas, + } + : { ...utxBase, gasPrice: msg.gasPrice }; + + const signedTx = await ethereum.request({ + method: "eth_sendTransaction", + params: [utx], + }); + + return { + hash: signedTx, + } as core.ETHTxHash; + } catch (error) { + console.error(error); + return null; + } +} + +export async function ethSignMessage( + msg: core.ETHSignMessage, + ethereum: any, + address: string +): Promise { + try { + if (!isHexString(msg.message)) throw new Error("data is not an hex string"); + const signedMsg = await ethereum.request({ + method: "personal_sign", + params: [msg.message, address], + }); + + return { + address: address, + signature: signedMsg, + } as ETHSignedMessage; + } catch (error) { + console.error(error); + return null; + } +} + +export async function ethGetAddress(ethereum: any): Promise { + if (!(ethereum && ethereum.request)) { + return null; + } + try { + const ethAccounts = await ethereum.request({ + method: "eth_accounts", + }); + return ethAccounts[0]; + } catch (error) { + console.error(error); + return null; + } +} diff --git a/packages/hdwallet-phantom/src/index.ts b/packages/hdwallet-phantom/src/index.ts new file mode 100644 index 000000000..ee1fe48d8 --- /dev/null +++ b/packages/hdwallet-phantom/src/index.ts @@ -0,0 +1,2 @@ +export * from "./adapter"; +export * from "./phantom"; diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts new file mode 100644 index 000000000..c363f60a7 --- /dev/null +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -0,0 +1,168 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; + +import { PhantomHDWallet, PhantomHDWalletInfo } from "."; + +describe("HDWalletInfo", () => { + const info = new PhantomHDWalletInfo(); + + it("should have correct metadata", async () => { + expect(info.getVendor()).toBe("Phantom"); + expect(info.hasOnDevicePinEntry()).toBe(false); + expect(info.hasOnDevicePassphrase()).toBe(true); + expect(info.hasOnDeviceDisplay()).toBe(true); + expect(info.hasOnDeviceRecovery()).toBe(true); + expect(await info.ethSupportsNetwork(1)).toBe(true); + expect(await info.ethSupportsSecureTransfer()).toBe(false); + expect(info.ethSupportsNativeShapeShift()).toBe(false); + expect(await info.ethSupportsEIP1559()).toBe(true); + expect(await info.supportsOfflineSigning()).toBe(false); + expect(await info.supportsBroadcast()).toBe(true); + }); +}); + +describe("PhantomHDWallet", () => { + let wallet: PhantomHDWallet; + beforeEach(() => { + wallet = new PhantomHDWallet(core.untouchable("PhantomHDWallet:provider")); + wallet.ethAddress = "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"; + }); + + it("should match the metadata", async () => { + expect(wallet.getVendor()).toBe("Phantom"); + expect(wallet.hasOnDevicePinEntry()).toBe(false); + expect(wallet.hasOnDevicePassphrase()).toBe(true); + expect(wallet.hasOnDeviceDisplay()).toBe(true); + expect(wallet.hasOnDeviceRecovery()).toBe(true); + expect(await wallet.ethSupportsNetwork(1)).toBe(true); + expect(await wallet.ethSupportsSecureTransfer()).toBe(false); + expect(wallet.ethSupportsNativeShapeShift()).toBe(false); + expect(await wallet.ethSupportsEIP1559()).toBe(true); + expect(await wallet.supportsOfflineSigning()).toBe(false); + expect(wallet.supportsBip44Accounts()).toBe(false); + expect(await wallet.supportsBroadcast()).toBe(true); + }); + + it("should test ethSignMessage", async () => { + wallet.provider = { + request: jest.fn().mockReturnValue( + `Object { + "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", + "signature": "0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b", + }` + ), + }; + const msg = "0x737570657220736563726574206d657373616765"; // super secret message + expect( + await wallet.ethSignMessage({ + addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), + message: msg, + }) + ).toMatchInlineSnapshot(` + Object { + "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", + "signature": "Object { + \\"address\\": \\"0x73d0385F4d8E00C5e6504C6030F47BF6212736A8\\", + \\"signature\\": \\"0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b\\", + }", + } + `); + }); + + it("ethSignMessage returns null on error", async () => { + wallet.provider = { + request: jest.fn().mockRejectedValue(new Error("An Error has occurred")), + }; + + const msg = "0x737570657220736563726574206d657373616765"; // super secret message + const sig = await wallet.ethSignMessage({ + addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), + message: msg, + }); + + expect(sig).toBe(null); + }); + + it("ethGetAddress returns a valid address", async () => { + wallet.provider = { + request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]), + }; + + const msg = "0x737570657220736563726574206d657373616765"; // super secret message + const sig = await wallet.ethSignMessage({ + addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), + message: msg, + }); + + expect(sig).toMatchObject({ + address: "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", + signature: ["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"], + }); + }); + it("ethSendTx returns a valid hash", async () => { + wallet.provider = { + request: jest.fn().mockReturnValue("0x123"), + }; + + const hash = await wallet.ethSendTx({ + addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), + nonce: "0xDEADBEEF", + gasPrice: "0xDEADBEEF", + gasLimit: "0xDEADBEEF", + to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + chainId: 1, + }); + expect(wallet.provider.request).toHaveBeenCalled(); + expect(hash).toMatchObject({ hash: "0x123" }); + }); + it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => { + wallet.provider = { + request: jest.fn().mockReturnValue("0x123"), + }; + + const hash = await wallet.ethSendTx({ + addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), + nonce: "0xDEADBEEF", + gasLimit: "0xDEADBEEF", + maxFeePerGas: "0xDEADBEEF", + to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + chainId: 1, + }); + expect(wallet.provider.request).toHaveBeenCalled(); + expect(hash).toMatchObject({ hash: "0x123" }); + }); + it("ethSendTx returns null on error", async () => { + wallet.provider = { + request: jest.fn().mockRejectedValue(new Error("An Error has occurred")), + }; + + const hash = await wallet.ethSendTx({ + addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), + nonce: "0xDEADBEEF", + gasPrice: "0xDEADBEEF", + gasLimit: "0xDEADBEEF", + to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + chainId: 1, + }); + expect(wallet.provider.request).toHaveBeenCalled(); + expect(hash).toBe(null); + }); + it("ethVerifyMessage returns null as its not implemented", async () => { + wallet.provider = { + request: jest.fn().mockReturnValue("0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"), + }; + expect( + await wallet.ethVerifyMessage({ + address: "0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8", + message: "hello world", + signature: + "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b", + }) + ).toEqual(null); + }); +}); diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts new file mode 100644 index 000000000..00c9866a3 --- /dev/null +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -0,0 +1,363 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; +import { AddEthereumChainParameter } from "@shapeshiftoss/hdwallet-core"; +import { ethErrors, serializeError } from "eth-rpc-errors"; +import _ from "lodash"; + +import * as eth from "./ethereum"; + +export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet { + return _.isObject(wallet) && (wallet as any)._isPhantom; +} + +export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo { + readonly _supportsBTCInfo = false; + readonly _supportsETHInfo = true; + readonly _supportsCosmosInfo = false; + readonly _supportsBinanceInfo = false; + readonly _supportsRippleInfo = false; + readonly _supportsEosInfo = false; + readonly _supportsFioInfo = false; + readonly _supportsThorchainInfo = false; + readonly _supportsSecretInfo = false; + readonly _supportsKavaInfo = false; + readonly _supportsTerraInfo = false; + + public getVendor(): string { + return "Phantom"; + } + + public hasOnDevicePinEntry(): boolean { + return false; + } + + public hasOnDevicePassphrase(): boolean { + return true; + } + + public hasOnDeviceDisplay(): boolean { + return true; + } + + public hasOnDeviceRecovery(): boolean { + return true; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { + return false; + } + + public supportsBip44Accounts(): boolean { + return false; + } + + public supportsOfflineSigning(): boolean { + return false; + } + + public supportsBroadcast(): boolean { + return true; + } + + public describePath(msg: core.DescribePath): core.PathDescription { + switch (msg.coin) { + case "Ethereum": + return eth.describeETHPath(msg.path); + default: + throw new Error("Unsupported path"); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + // TODO: What do we do here? + return undefined; + } + + public async ethSupportsNetwork(chainId: number): Promise { + return chainId === 1; + } + + public async ethSupportsSecureTransfer(): Promise { + return false; + } + + public ethSupportsNativeShapeShift(): boolean { + return false; + } + + public async ethSupportsEIP1559(): Promise { + return true; + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { + return eth.ethGetAccountPaths(msg); + } +} + +export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { + readonly _supportsETH = true; + readonly _supportsETHInfo = true; + readonly _supportsBTCInfo = false; + readonly _supportsBTC = false; + readonly _supportsCosmosInfo = false; + readonly _supportsCosmos = false; + readonly _supportsEthSwitchChain = true; + readonly _supportsAvalanche = true; + readonly _supportsOptimism = true; + readonly _supportsBSC = true; + readonly _supportsPolygon = true; + readonly _supportsGnosis = true; + readonly _supportsArbitrum = true; + readonly _supportsArbitrumNova = true; + readonly _supportsBase = true; + readonly _supportsOsmosisInfo = false; + readonly _supportsOsmosis = false; + readonly _supportsBinanceInfo = false; + readonly _supportsBinance = false; + readonly _supportsDebugLink = false; + readonly _isPortis = false; + readonly _isPhantom = true; + readonly _supportsRippleInfo = false; + readonly _supportsRipple = false; + readonly _supportsEosInfo = false; + readonly _supportsEos = false; + readonly _supportsFioInfo = false; + readonly _supportsFio = false; + readonly _supportsThorchainInfo = false; + readonly _supportsThorchain = false; + readonly _supportsSecretInfo = false; + readonly _supportsSecret = false; + readonly _supportsKava = false; + readonly _supportsKavaInfo = false; + readonly _supportsTerra = false; + readonly _supportsTerraInfo = false; + + info: PhantomHDWalletInfo & core.HDWalletInfo; + ethAddress?: string | null; + provider: any; + + constructor(provider: unknown) { + this.info = new PhantomHDWalletInfo(); + this.provider = provider; + } + + async getFeatures(): Promise> { + return {}; + } + + public async isLocked(): Promise { + return !this.provider._phantom.isUnlocked(); + } + + public getVendor(): string { + return "Phantom"; + } + + public async getModel(): Promise { + return "Phantom"; + } + + public async getLabel(): Promise { + return "Phantom"; + } + + public async initialize(): Promise { + // nothing to initialize + } + + public hasOnDevicePinEntry(): boolean { + return this.info.hasOnDevicePinEntry(); + } + + public hasOnDevicePassphrase(): boolean { + return this.info.hasOnDevicePassphrase(); + } + + public hasOnDeviceDisplay(): boolean { + return this.info.hasOnDeviceDisplay(); + } + + public hasOnDeviceRecovery(): boolean { + return this.info.hasOnDeviceRecovery(); + } + + public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { + return this.info.hasNativeShapeShift(srcCoin, dstCoin); + } + + public supportsBip44Accounts(): boolean { + return this.info.supportsBip44Accounts(); + } + + public supportsOfflineSigning(): boolean { + return false; + } + + public supportsBroadcast(): boolean { + return true; + } + + public async clearSession(): Promise { + // TODO: Can we lock Phantom from here? + } + + public async ping(msg: core.Ping): Promise { + // no ping function for Phantom, so just returning Core.Pong + return { msg: msg.msg }; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async sendPin(pin: string): Promise { + // no concept of pin in Phantom + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async sendPassphrase(passphrase: string): Promise { + // cannot send passphrase to Phantom. Could show the widget? + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async sendCharacter(charater: string): Promise { + // no concept of sendCharacter in Phantom + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async sendWord(word: string): Promise { + // no concept of sendWord in Phantom + } + + public async cancel(): Promise { + // no concept of cancel in Phantom + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async wipe(): Promise {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars + public async reset(msg: core.ResetDevice): Promise {} + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async recover(msg: core.RecoverDevice): Promise { + // no concept of recover in Phantom + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async loadDevice(msg: core.LoadDevice): Promise { + // TODO: Does Phantom allow this to be done programatically? + } + + public describePath(msg: core.DescribePath): core.PathDescription { + return this.info.describePath(msg); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async getPublicKeys(msg: Array): Promise> { + // Ethereum public keys are not exposed by the RPC API + return []; + } + + public async isInitialized(): Promise { + return true; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async disconnect(): Promise {} + + public async ethSupportsNetwork(chainId = 1): Promise { + return chainId === 1; + } + + public async ethGetChainId(): Promise { + try { + // chainId as hex string + const chainId: string = await this.provider.request({ method: "eth_chainId" }); + return parseInt(chainId, 16); + } catch (e) { + console.error(e); + return null; + } + } + + public async ethSwitchChain(params: AddEthereumChainParameter): Promise { + try { + // at this point, we know that we're in the context of a valid Phantom provider + await this.provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: params.chainId }] }); + } catch (e: any) { + const error = serializeError(e); + + if (error.code === 4001) { + throw ethErrors.provider.userRejectedRequest(); + } + + throw (error.data as any).originalError as { + code: number; + message: string; + stack: string; + }; + } + } + + public async ethSupportsSecureTransfer(): Promise { + return false; + } + + public ethSupportsNativeShapeShift(): boolean { + return false; + } + + public async ethSupportsEIP1559(): Promise { + return true; + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { + return eth.ethGetAccountPaths(msg); + } + + public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + return this.info.ethNextAccountPath(msg); + } + + // TODO: Respect msg.addressNList! + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async ethGetAddress(msg: core.ETHGetAddress): Promise { + if (this.ethAddress) { + return this.ethAddress; + } + const address = await eth.ethGetAddress(this.provider); + if (address) { + this.ethAddress = address; + return address; + } else { + this.ethAddress = null; + return null; + } + } + + public async ethSignTx(msg: core.ETHSignTx): Promise { + const address = await this.ethGetAddress(this.provider); + return address ? eth.ethSignTx(msg, this.provider, address) : null; + } + + public async ethSendTx(msg: core.ETHSignTx): Promise { + const address = await this.ethGetAddress(this.provider); + return address ? eth.ethSendTx(msg, this.provider, address) : null; + } + + public async ethSignMessage(msg: core.ETHSignMessage): Promise { + const address = await this.ethGetAddress(this.provider); + return address ? eth.ethSignMessage(msg, this.provider, address) : null; + } + + public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { + return eth.ethVerifyMessage(msg, this.provider); + } + + public async getDeviceID(): Promise { + return "phantom:" + (await this.ethGetAddress(this.provider)); + } + + public async getFirmwareVersion(): Promise { + return "phantom"; + } +} diff --git a/packages/hdwallet-phantom/tsconfig.json b/packages/hdwallet-phantom/tsconfig.json new file mode 100644 index 000000000..0c82f8910 --- /dev/null +++ b/packages/hdwallet-phantom/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"], + "references": [{ "path": "../hdwallet-core" }] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index e92968f5c..1060ad06e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,6 +45,7 @@ { "path": "./packages/hdwallet-ledger-webusb" }, { "path": "./packages/hdwallet-metamask" }, { "path": "./packages/hdwallet-metamask-shapeshift-multichain" }, + { "path": "./packages/hdwallet-phantom" }, { "path": "./packages/hdwallet-coinbase" }, { "path": "./packages/hdwallet-native" }, { "path": "./packages/hdwallet-portis" }, From f516a17c03ff916b6d893f7a67a3c2e2764ba5fe Mon Sep 17 00:00:00 2001 From: NeOMakinG <14963751+NeOMakinG@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:12:11 +0200 Subject: [PATCH 02/52] feat: update supports --- packages/hdwallet-phantom/src/phantom.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 00c9866a3..7265997c3 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -99,18 +99,18 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsETH = true; readonly _supportsETHInfo = true; readonly _supportsBTCInfo = false; - readonly _supportsBTC = false; + readonly _supportsBTC = true; readonly _supportsCosmosInfo = false; readonly _supportsCosmos = false; - readonly _supportsEthSwitchChain = true; - readonly _supportsAvalanche = true; - readonly _supportsOptimism = true; - readonly _supportsBSC = true; + readonly _supportsEthSwitchChain = false; + readonly _supportsAvalanche = false; + readonly _supportsOptimism = false; + readonly _supportsBSC = false; readonly _supportsPolygon = true; - readonly _supportsGnosis = true; - readonly _supportsArbitrum = true; - readonly _supportsArbitrumNova = true; - readonly _supportsBase = true; + readonly _supportsGnosis = false; + readonly _supportsArbitrum = false; + readonly _supportsArbitrumNova = false; + readonly _supportsBase = false; readonly _supportsOsmosisInfo = false; readonly _supportsOsmosis = false; readonly _supportsBinanceInfo = false; From 617ea6cb57081d4822bf1e9459d2ee9850aed9b4 Mon Sep 17 00:00:00 2001 From: NeOMakinG <14963751+NeOMakinG@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:24:01 +0200 Subject: [PATCH 03/52] feat: add btc supprot --- packages/hdwallet-phantom/src/adapter.ts | 12 +- packages/hdwallet-phantom/src/bitcoin.ts | 180 ++++++++++++++++++ packages/hdwallet-phantom/src/phantom.test.ts | 25 +-- packages/hdwallet-phantom/src/phantom.ts | 145 ++++++++++---- 4 files changed, 314 insertions(+), 48 deletions(-) create mode 100644 packages/hdwallet-phantom/src/bitcoin.ts diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 58d01661b..ee050b16f 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -7,6 +7,7 @@ declare global { interface Window { phantom?: { ethereum?: providers.ExternalProvider; + bitcoin?: providers.ExternalProvider; }; } } @@ -27,17 +28,18 @@ export class PhantomAdapter { } public async pairDevice(): Promise { - const provider = window.phantom?.ethereum; + const evmProvider = window.phantom?.ethereum; + const bitcoinProvider = window.phantom?.bitcoin; - if (!provider) { + if (!evmProvider || !bitcoinProvider) { window.open("https://phantom.app/", "_blank"); console.error("Please install Phantom!"); throw new Error("Phantom provider not found"); } try { - await provider.request?.({ method: "eth_requestAccounts" }).catch(() => - provider.request?.({ + await evmProvider.request?.({ method: "eth_requestAccounts" }).catch(() => + evmProvider.request?.({ method: "wallet_requestPermissions", params: [{ eth_accounts: {} }], }) @@ -46,7 +48,7 @@ export class PhantomAdapter { console.error("Could not get Phantom accounts. "); throw error; } - const wallet = new PhantomHDWallet(provider); + const wallet = new PhantomHDWallet(evmProvider, bitcoinProvider); await wallet.initialize(); const deviceID = await wallet.getDeviceID(); this.keyring.add(wallet, deviceID); diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts new file mode 100644 index 000000000..f37bef40e --- /dev/null +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -0,0 +1,180 @@ +import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib"; +import * as core from "@shapeshiftoss/hdwallet-core"; + +type BtcAccount = { + address: string; + addressType: "p2tr" | "p2wpkh" | "p2sh" | "p2pkh"; + publicKey: string; + purpose: "payment" | "ordinals"; +}; + +function getNetwork(coin: string): bitcoin.networks.Network { + switch (coin.toLowerCase()) { + case "bitcoin": + return bitcoin.networks.bitcoin; + case "testnet": + return bitcoin.networks.testnet; + default: + throw new Error(`Unsupported coin: ${coin}`); + } +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + // Only support one account for now (like portis). + return undefined; +} + +export async function bitcoinGetPublicKeys(msg: core.BTCGetAddress, provider: any): Promise { + const accounts = await provider.requestAccounts(); + const paymentPublicKey = accounts.find((account: BtcAccount) => account.purpose === "payment")?.publicKey; + + return [paymentPublicKey]; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export async function bitcoinGetAddress(msg: core.BTCGetAddress, provider: any): Promise { + const accounts = await provider.requestAccounts(); + const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; + + return paymentAddress; +} + +async function addInput( + wallet: core.BTCWallet, + psbt: bitcoin.Psbt, + input: core.BTCSignTxInput, + coin: string, + network: bitcoin.networks.Network +): Promise { + const inputData: bitcoin.PsbtTxInput & { + nonWitnessUtxo?: Buffer; + witnessUtxo?: { script: Buffer; value: number }; + } = { + hash: Buffer.from(input.txid, "hex"), + index: input.vout, + }; + + if (input.sequence !== undefined) { + inputData.sequence = input.sequence; + } + + if (input.scriptType) { + switch (input.scriptType) { + case "p2pkh": + inputData.nonWitnessUtxo = Buffer.from(input.hex, "hex"); + break; + case "p2sh-p2wpkh": + case "p2wpkh": { + const inputAddress = await wallet.btcGetAddress({ addressNList: input.addressNList, coin, showDisplay: false }); + + if (!inputAddress) throw new Error("Could not get address from wallet"); + + inputData.witnessUtxo = { + script: bitcoin.address.toOutputScript(inputAddress, network), + value: parseInt(input.amount), + }; + break; + } + default: + throw new Error(`Unsupported script type: ${input.scriptType}`); + } + } + + psbt.addInput(inputData); +} + +async function addOutput( + wallet: core.BTCWallet, + psbt: bitcoin.Psbt, + output: core.BTCSignTxOutput, + coin: string +): Promise { + if ("address" in output && output.address) { + psbt.addOutput({ + address: output.address, + value: parseInt(output.amount), + }); + } else if ("addressNList" in output && output.addressNList) { + const outputAddress = await wallet.btcGetAddress({ addressNList: output.addressNList, coin, showDisplay: false }); + + if (!outputAddress) throw new Error("Could not get address from wallet"); + + psbt.addOutput({ + address: outputAddress, + value: parseInt(output.amount), + }); + } else if ("opReturnData" in output && output.opReturnData) { + const data = Buffer.from(output.opReturnData.toString(), "hex"); + const embed = bitcoin.payments.embed({ data: [data] }); + psbt.addOutput({ + script: embed.output!, + value: 0, + }); + } +} + +export async function bitcoinSignTx( + wallet: core.BTCWallet, + msg: core.BTCSignTx, + provider: any +): Promise { + if (!msg.inputs.length || !msg.outputs.length) { + throw new Error("Invalid input: Empty inputs or outputs"); + } + + const network = getNetwork(msg.coin); + + const psbt = new bitcoin.Psbt({ network }); + + psbt.setVersion(msg.version ?? 2); + if (msg.locktime) { + psbt.setLocktime(msg.locktime); + } + + for (const input of msg.inputs) { + await addInput(wallet, psbt, input, msg.coin, network); + } + + for (const output of msg.outputs) { + await addOutput(wallet, psbt, output, msg.coin); + } + + const inputsToSign = await Promise.all( + msg.inputs.map(async (input, index) => { + const address = await wallet.btcGetAddress({ + addressNList: input.addressNList, + coin: msg.coin, + showDisplay: false, + }); + + return { + address, + signingIndexes: [index], + sigHash: bitcoin.Transaction.SIGHASH_ALL, + }; + }) + ); + + const fromHexString = (hexString: string) => { + const bytes = hexString.match(/.{1,2}/g); + if (!bytes) throw new Error("Invalid hex string"); + + return Uint8Array.from(bytes.map((byte) => parseInt(byte, 16))); + }; + const signedPsbtHex = await provider.signPSBT(fromHexString(psbt.toHex()), { inputsToSign }); + + const signedPsbt = bitcoin.Psbt.fromHex(signedPsbtHex, { network }); + + signedPsbt.finalizeAllInputs(); + const tx = signedPsbt.extractTransaction(); + + const signatures = signedPsbt.data.inputs.map((input) => + input.partialSig ? input.partialSig[0].signature.toString("hex") : "" + ); + + return { + signatures, + serializedTx: tx.toHex(), + }; +} diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index c363f60a7..4a325e3e5 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -23,7 +23,10 @@ describe("HDWalletInfo", () => { describe("PhantomHDWallet", () => { let wallet: PhantomHDWallet; beforeEach(() => { - wallet = new PhantomHDWallet(core.untouchable("PhantomHDWallet:provider")); + wallet = new PhantomHDWallet( + core.untouchable("PhantomHDWallet:provider"), + core.untouchable("PhantomHDWallet:provider") + ); wallet.ethAddress = "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"; }); @@ -43,7 +46,7 @@ describe("PhantomHDWallet", () => { }); it("should test ethSignMessage", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockReturnValue( `Object { "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", @@ -69,7 +72,7 @@ describe("PhantomHDWallet", () => { }); it("ethSignMessage returns null on error", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockRejectedValue(new Error("An Error has occurred")), }; @@ -83,7 +86,7 @@ describe("PhantomHDWallet", () => { }); it("ethGetAddress returns a valid address", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]), }; @@ -99,7 +102,7 @@ describe("PhantomHDWallet", () => { }); }); it("ethSendTx returns a valid hash", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockReturnValue("0x123"), }; @@ -113,11 +116,11 @@ describe("PhantomHDWallet", () => { data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", chainId: 1, }); - expect(wallet.provider.request).toHaveBeenCalled(); + expect(wallet.evmProvider.request).toHaveBeenCalled(); expect(hash).toMatchObject({ hash: "0x123" }); }); it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockReturnValue("0x123"), }; @@ -131,11 +134,11 @@ describe("PhantomHDWallet", () => { data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", chainId: 1, }); - expect(wallet.provider.request).toHaveBeenCalled(); + expect(wallet.evmProvider.request).toHaveBeenCalled(); expect(hash).toMatchObject({ hash: "0x123" }); }); it("ethSendTx returns null on error", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockRejectedValue(new Error("An Error has occurred")), }; @@ -149,11 +152,11 @@ describe("PhantomHDWallet", () => { data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF", chainId: 1, }); - expect(wallet.provider.request).toHaveBeenCalled(); + expect(wallet.evmProvider.request).toHaveBeenCalled(); expect(hash).toBe(null); }); it("ethVerifyMessage returns null as its not implemented", async () => { - wallet.provider = { + wallet.evmProvider = { request: jest.fn().mockReturnValue("0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"), }; expect( diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 7265997c3..ecc397b54 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -1,8 +1,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { AddEthereumChainParameter } from "@shapeshiftoss/hdwallet-core"; -import { ethErrors, serializeError } from "eth-rpc-errors"; import _ from "lodash"; +import * as Btc from "./bitcoin"; import * as eth from "./ethereum"; export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet { @@ -22,6 +22,9 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf readonly _supportsKavaInfo = false; readonly _supportsTerraInfo = false; + bitcoinAddress?: string | null; + ethAddress?: string | null; + public getVendor(): string { return "Phantom"; } @@ -135,11 +138,13 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { info: PhantomHDWalletInfo & core.HDWalletInfo; ethAddress?: string | null; - provider: any; + evmProvider: any; + bitcoinProvider: any; - constructor(provider: unknown) { + constructor(evmProvider: unknown, bitcoinProvider: unknown) { this.info = new PhantomHDWalletInfo(); - this.provider = provider; + this.evmProvider = evmProvider; + this.bitcoinProvider = bitcoinProvider; } async getFeatures(): Promise> { @@ -147,7 +152,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public async isLocked(): Promise { - return !this.provider._phantom.isUnlocked(); + return !this.evmProvider._phantom.isUnlocked(); } public getVendor(): string { @@ -248,7 +253,19 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public describePath(msg: core.DescribePath): core.PathDescription { - return this.info.describePath(msg); + switch (msg.coin) { + case "bitcoin": { + const unknown = core.unknownUTXOPath(msg.path, msg.coin, msg.scriptType); + + if (!msg.scriptType) return unknown; + + return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType); + } + case "ethereum": + return core.describeETHPath(msg.path); + default: + throw new Error("Unsupported path"); + } } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -271,7 +288,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { public async ethGetChainId(): Promise { try { // chainId as hex string - const chainId: string = await this.provider.request({ method: "eth_chainId" }); + const chainId: string = await this.evmProvider.request({ method: "eth_chainId" }); return parseInt(chainId, 16); } catch (e) { console.error(e); @@ -279,23 +296,9 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethSwitchChain(params: AddEthereumChainParameter): Promise { - try { - // at this point, we know that we're in the context of a valid Phantom provider - await this.provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: params.chainId }] }); - } catch (e: any) { - const error = serializeError(e); - - if (error.code === 4001) { - throw ethErrors.provider.userRejectedRequest(); - } - - throw (error.data as any).originalError as { - code: number; - message: string; - stack: string; - }; - } + // no concept of switch chain in phantom } public async ethSupportsSecureTransfer(): Promise { @@ -324,7 +327,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { if (this.ethAddress) { return this.ethAddress; } - const address = await eth.ethGetAddress(this.provider); + const address = await eth.ethGetAddress(this.evmProvider); if (address) { this.ethAddress = address; return address; @@ -335,29 +338,107 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public async ethSignTx(msg: core.ETHSignTx): Promise { - const address = await this.ethGetAddress(this.provider); - return address ? eth.ethSignTx(msg, this.provider, address) : null; + const address = await this.ethGetAddress(this.evmProvider); + return address ? eth.ethSignTx(msg, this.evmProvider, address) : null; } public async ethSendTx(msg: core.ETHSignTx): Promise { - const address = await this.ethGetAddress(this.provider); - return address ? eth.ethSendTx(msg, this.provider, address) : null; + const address = await this.ethGetAddress(this.evmProvider); + return address ? eth.ethSendTx(msg, this.evmProvider, address) : null; } public async ethSignMessage(msg: core.ETHSignMessage): Promise { - const address = await this.ethGetAddress(this.provider); - return address ? eth.ethSignMessage(msg, this.provider, address) : null; + const address = await this.ethGetAddress(this.evmProvider); + return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null; } public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { - return eth.ethVerifyMessage(msg, this.provider); + return eth.ethVerifyMessage(msg, this.evmProvider); } public async getDeviceID(): Promise { - return "phantom:" + (await this.ethGetAddress(this.provider)); + return "phantom:" + (await this.ethGetAddress(this.evmProvider)); } public async getFirmwareVersion(): Promise { return "phantom"; } + + /** BITCOIN */ + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + // TODO: What do we do here? + return undefined; + } + + public async btcSupportsSecureTransfer(): Promise { + return false; + } + + public btcSupportsNativeShapeShift(): boolean { + return false; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array { + throw new Error("Method not implemented."); + } + + public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + return this.bitcoinNextAccountPath(msg); + } + + addressCache: Map = new Map(); + public async btcGetAddress(msg: core.BTCGetAddress): Promise { + const key = JSON.stringify(msg); + const maybeCachedAddress = this.addressCache.get(key); + if (maybeCachedAddress) return maybeCachedAddress; + const value = await (async () => { + switch (msg.coin) { + case "Bitcoin": + return Btc.bitcoinGetAddress(msg, this.bitcoinProvider); + default: + return null; + } + })(); + if (!value || typeof value !== "string") return null; + + this.addressCache.set(key, value); + return value; + } + + public async btcSignTx(msg: core.BTCSignTx): Promise { + const { coin } = msg; + switch (coin) { + case "Bitcoin": + return Btc.bitcoinSignTx(this, msg, this.bitcoinProvider); + default: + return null; + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async btcSignMessage(msg: core.BTCSignMessage): Promise { + throw new Error("Method not implemented."); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { + throw new Error("Method not implemented."); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async btcSupportsScriptType(coin: string, scriptType?: core.BTCInputScriptType | undefined): Promise { + throw new Error("Method not implemented."); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async btcSupportsCoin(coin: core.Coin): Promise { + throw new Error("Method not implemented."); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcIsSameAccount(msg: core.BTCAccountPath[]): boolean { + throw new Error("Method not implemented."); + } } From 014913ded232d74491e908b941e761932701d4dc Mon Sep 17 00:00:00 2001 From: NeOMakinG <14963751+NeOMakinG@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:42:10 +0200 Subject: [PATCH 04/52] feat: lint --- examples/sandbox/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sandbox/index.ts b/examples/sandbox/index.ts index 0065fddfc..02f89017b 100644 --- a/examples/sandbox/index.ts +++ b/examples/sandbox/index.ts @@ -11,8 +11,8 @@ import * as keplr from "@shapeshiftoss/hdwallet-keplr"; import * as ledgerWebHID from "@shapeshiftoss/hdwallet-ledger-webhid"; import * as ledgerWebUSB from "@shapeshiftoss/hdwallet-ledger-webusb"; import * as metaMask from "@shapeshiftoss/hdwallet-metamask"; -import * as phantom from "@shapeshiftoss/hdwallet-phantom"; import * as native from "@shapeshiftoss/hdwallet-native"; +import * as phantom from "@shapeshiftoss/hdwallet-phantom"; import * as portis from "@shapeshiftoss/hdwallet-portis"; import * as tallyHo from "@shapeshiftoss/hdwallet-tallyho"; import * as trezorConnect from "@shapeshiftoss/hdwallet-trezor-connect"; From 69d8892b29448b489a0cf1eaf09824f4dff32025 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 00:01:16 +0200 Subject: [PATCH 05/52] feat: cleanup --- packages/hdwallet-phantom/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json index 4adbd0b1c..5a5b2ff37 100644 --- a/packages/hdwallet-phantom/package.json +++ b/packages/hdwallet-phantom/package.json @@ -15,7 +15,6 @@ }, "dependencies": { "@shapeshiftoss/hdwallet-core": "1.54.2", - "eth-rpc-errors": "^4.0.3", "lodash": "^4.17.21" }, "devDependencies": { From b55e957480c903fd1f4b4e18b5e821b284c39b15 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 00:04:04 +0200 Subject: [PATCH 06/52] feat: revert me --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 1060ad06e..a48be222c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,8 @@ // tsc is insanely drunk with internal resolutions currently, this at least silences it so we can ship things // the installed versions are still the same as before, but the bumped tsc version might have made it stricter "skipLibCheck": true, - "target": "ES2016", - "module": "commonjs", + "target": "ESNext", + "module": "ESNext", "lib": ["es2020", "dom", "es5"], "declaration": true, "declarationMap": true, From d622b47df797d6c405d0858cc46c61d5d939f876 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:40:46 +0200 Subject: [PATCH 07/52] feat: wip window.solana --- packages/hdwallet-phantom/src/adapter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index ee050b16f..3a37151a6 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -8,6 +8,9 @@ declare global { phantom?: { ethereum?: providers.ExternalProvider; bitcoin?: providers.ExternalProvider; + // TODO: update with proper types once implemented + // https://github.com/anza-xyz/wallet-adapter/blob/3761cd8cc867da39da7c0b070bbf8779402cff36/packages/wallets/phantom/src/adapter.ts#L36 + solana?: any; }; } } From af83e76c2f17ad34451b78e5b6b7c1c128ed7b38 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:43:12 +0200 Subject: [PATCH 08/52] feat: fromHexString outside of fn scope --- packages/hdwallet-phantom/src/bitcoin.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index f37bef40e..2ff543f93 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -8,6 +8,13 @@ type BtcAccount = { purpose: "payment" | "ordinals"; }; +const fromHexString = (hexString: string) => { + const bytes = hexString.match(/.{1,2}/g); + if (!bytes) throw new Error("Invalid hex string"); + + return Uint8Array.from(bytes.map((byte) => parseInt(byte, 16))); +}; + function getNetwork(coin: string): bitcoin.networks.Network { switch (coin.toLowerCase()) { case "bitcoin": @@ -156,12 +163,6 @@ export async function bitcoinSignTx( }) ); - const fromHexString = (hexString: string) => { - const bytes = hexString.match(/.{1,2}/g); - if (!bytes) throw new Error("Invalid hex string"); - - return Uint8Array.from(bytes.map((byte) => parseInt(byte, 16))); - }; const signedPsbtHex = await provider.signPSBT(fromHexString(psbt.toHex()), { inputsToSign }); const signedPsbt = bitcoin.Psbt.fromHex(signedPsbtHex, { network }); From 0ff0419a111951cd1769a17eee647e4437ec95d4 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:44:17 +0200 Subject: [PATCH 09/52] feat: rm testnet support --- packages/hdwallet-phantom/src/bitcoin.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 2ff543f93..b0ee3903c 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -15,16 +15,14 @@ const fromHexString = (hexString: string) => { return Uint8Array.from(bytes.map((byte) => parseInt(byte, 16))); }; -function getNetwork(coin: string): bitcoin.networks.Network { +const getNetwork = (coin: string): bitcoin.networks.Network => { switch (coin.toLowerCase()) { case "bitcoin": return bitcoin.networks.bitcoin; - case "testnet": - return bitcoin.networks.testnet; default: throw new Error(`Unsupported coin: ${coin}`); } -} +}; // eslint-disable-next-line @typescript-eslint/no-unused-vars export function bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { From 14fa1807ff395c7f29d9265366af17c64cd00ee8 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:44:55 +0200 Subject: [PATCH 10/52] feat: rm useless fn --- packages/hdwallet-phantom/src/bitcoin.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index b0ee3903c..62b598a30 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -30,13 +30,6 @@ export function bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccoun return undefined; } -export async function bitcoinGetPublicKeys(msg: core.BTCGetAddress, provider: any): Promise { - const accounts = await provider.requestAccounts(); - const paymentPublicKey = accounts.find((account: BtcAccount) => account.purpose === "payment")?.publicKey; - - return [paymentPublicKey]; -} - // eslint-disable-next-line @typescript-eslint/no-unused-vars export async function bitcoinGetAddress(msg: core.BTCGetAddress, provider: any): Promise { const accounts = await provider.requestAccounts(); From bde4f7a8cef4dd79e73995515bc544b75a6edf3b Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:46:46 +0200 Subject: [PATCH 11/52] feat: cleanup --- packages/hdwallet-phantom/src/bitcoin.ts | 6 ------ packages/hdwallet-phantom/src/phantom.ts | 7 +++++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 62b598a30..2ee0d9291 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -24,12 +24,6 @@ const getNetwork = (coin: string): bitcoin.networks.Network => { } }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export function bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - // Only support one account for now (like portis). - return undefined; -} - // eslint-disable-next-line @typescript-eslint/no-unused-vars export async function bitcoinGetAddress(msg: core.BTCGetAddress, provider: any): Promise { const accounts = await provider.requestAccounts(); diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index ecc397b54..46bfae7a2 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -382,11 +382,14 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array { + // Phantom doesn't support BIP44 paths throw new Error("Method not implemented."); } - public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - return this.bitcoinNextAccountPath(msg); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcNextAccountPath(_msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + // Phantom doesn't support BIP44 paths + throw new Error("Method not implemented."); } addressCache: Map = new Map(); From 65c70ff020b66c0f99466f6668fb5ba70f60dd71 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:03:57 +0200 Subject: [PATCH 12/52] fix: isUnlocked --- packages/hdwallet-phantom/src/adapter.ts | 3 ++- packages/hdwallet-phantom/src/bitcoin.ts | 4 ++-- packages/hdwallet-phantom/src/phantom.ts | 20 ++++++++++++++------ packages/hdwallet-phantom/src/types.ts | 7 +++++++ 4 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 packages/hdwallet-phantom/src/types.ts diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 3a37151a6..1d195bd7f 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -2,11 +2,12 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { providers } from "ethers"; import { PhantomHDWallet } from "./phantom"; +import { PhantomEvmProvider } from "./types"; declare global { interface Window { phantom?: { - ethereum?: providers.ExternalProvider; + ethereum?: PhantomEvmProvider; bitcoin?: providers.ExternalProvider; // TODO: update with proper types once implemented // https://github.com/anza-xyz/wallet-adapter/blob/3761cd8cc867da39da7c0b070bbf8779402cff36/packages/wallets/phantom/src/adapter.ts#L36 diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 2ee0d9291..3ac64fc14 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -1,7 +1,7 @@ import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib"; import * as core from "@shapeshiftoss/hdwallet-core"; -type BtcAccount = { +export type BtcAccount = { address: string; addressType: "p2tr" | "p2wpkh" | "p2sh" | "p2pkh"; publicKey: string; @@ -25,7 +25,7 @@ const getNetwork = (coin: string): bitcoin.networks.Network => { }; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function bitcoinGetAddress(msg: core.BTCGetAddress, provider: any): Promise { +export async function bitcoinGetAddress(_msg: core.BTCGetAddress, provider: any): Promise { const accounts = await provider.requestAccounts(); const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 46bfae7a2..3ece27462 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -1,9 +1,12 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { AddEthereumChainParameter } from "@shapeshiftoss/hdwallet-core"; +import { providers } from "ethers"; import _ from "lodash"; import * as Btc from "./bitcoin"; +import { BtcAccount } from "./bitcoin"; import * as eth from "./ethereum"; +import { PhantomEvmProvider } from "./types"; export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet { return _.isObject(wallet) && (wallet as any)._isPhantom; @@ -138,10 +141,10 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { info: PhantomHDWalletInfo & core.HDWalletInfo; ethAddress?: string | null; - evmProvider: any; - bitcoinProvider: any; + evmProvider: PhantomEvmProvider; + bitcoinProvider: providers.ExternalProvider; - constructor(evmProvider: unknown, bitcoinProvider: unknown) { + constructor(evmProvider: PhantomEvmProvider, bitcoinProvider: providers.ExternalProvider) { this.info = new PhantomHDWalletInfo(); this.evmProvider = evmProvider; this.bitcoinProvider = bitcoinProvider; @@ -152,7 +155,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public async isLocked(): Promise { - return !this.evmProvider._phantom.isUnlocked(); + return !this.evmProvider._metamask.isUnlocked(); } public getVendor(): string { @@ -287,6 +290,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { public async ethGetChainId(): Promise { try { + if (!this.evmProvider.request) throw new Error("Provider does not support ethereum.request"); // chainId as hex string const chainId: string = await this.evmProvider.request({ method: "eth_chainId" }); return parseInt(chainId, 16); @@ -399,8 +403,12 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { if (maybeCachedAddress) return maybeCachedAddress; const value = await (async () => { switch (msg.coin) { - case "Bitcoin": - return Btc.bitcoinGetAddress(msg, this.bitcoinProvider); + case "Bitcoin": { + const accounts = await this.bitcoinProvider.requestAccounts(); + const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; + + return paymentAddress; + } default: return null; } diff --git a/packages/hdwallet-phantom/src/types.ts b/packages/hdwallet-phantom/src/types.ts new file mode 100644 index 000000000..e95bbe4eb --- /dev/null +++ b/packages/hdwallet-phantom/src/types.ts @@ -0,0 +1,7 @@ +import { providers } from "ethers"; + +export type PhantomEvmProvider = providers.ExternalProvider & { + _metamask: { + isUnlocked: () => boolean; + }; +}; From 2e6c32c5cf5d896dcc029613a2be2bbb255605de Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:06:19 +0200 Subject: [PATCH 13/52] feat: more cleanunp --- packages/hdwallet-coinbase/src/coinbase.ts | 1 - packages/hdwallet-metamask/src/metamask.ts | 3 +-- packages/hdwallet-phantom/src/phantom.ts | 12 +++++------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/hdwallet-coinbase/src/coinbase.ts b/packages/hdwallet-coinbase/src/coinbase.ts index 2e148c75c..aca7b9431 100644 --- a/packages/hdwallet-coinbase/src/coinbase.ts +++ b/packages/hdwallet-coinbase/src/coinbase.ts @@ -344,7 +344,6 @@ export class CoinbaseHDWallet implements core.HDWallet, core.ETHWallet { return this.info.ethNextAccountPath(msg); } - // TODO: Respect msg.addressNList! // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethGetAddress(msg: core.ETHGetAddress): Promise { if (this.ethAddress) { diff --git a/packages/hdwallet-metamask/src/metamask.ts b/packages/hdwallet-metamask/src/metamask.ts index d305e7285..3592388e2 100644 --- a/packages/hdwallet-metamask/src/metamask.ts +++ b/packages/hdwallet-metamask/src/metamask.ts @@ -345,9 +345,8 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { return this.info.ethNextAccountPath(msg); } - // TODO: Respect msg.addressNList! // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async ethGetAddress(msg: core.ETHGetAddress): Promise { + public async ethGetAddress(_msg: core.ETHGetAddress): Promise { if (this.ethAddress) { return this.ethAddress; } diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 3ece27462..b5d987fe3 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -325,9 +325,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { return this.info.ethNextAccountPath(msg); } - // TODO: Respect msg.addressNList! - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async ethGetAddress(msg: core.ETHGetAddress): Promise { + public async ethGetAddress(): Promise { if (this.ethAddress) { return this.ethAddress; } @@ -342,17 +340,17 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public async ethSignTx(msg: core.ETHSignTx): Promise { - const address = await this.ethGetAddress(this.evmProvider); + const address = await this.ethGetAddress(); return address ? eth.ethSignTx(msg, this.evmProvider, address) : null; } public async ethSendTx(msg: core.ETHSignTx): Promise { - const address = await this.ethGetAddress(this.evmProvider); + const address = await this.ethGetAddress(); return address ? eth.ethSendTx(msg, this.evmProvider, address) : null; } public async ethSignMessage(msg: core.ETHSignMessage): Promise { - const address = await this.ethGetAddress(this.evmProvider); + const address = await this.ethGetAddress(); return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null; } @@ -361,7 +359,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public async getDeviceID(): Promise { - return "phantom:" + (await this.ethGetAddress(this.evmProvider)); + return "phantom:" + (await this.ethGetAddress()); } public async getFirmwareVersion(): Promise { From 9357bdcb325f12130a429ccda4f8b5bb959eed2f Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:18:47 +0200 Subject: [PATCH 14/52] fix: describePath --- packages/hdwallet-phantom/src/phantom.ts | 25 ++++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index b5d987fe3..37cf66896 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -67,8 +67,15 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf public describePath(msg: core.DescribePath): core.PathDescription { switch (msg.coin) { - case "Ethereum": - return eth.describeETHPath(msg.path); + case "bitcoin": { + const unknown = core.unknownUTXOPath(msg.path, msg.coin, msg.scriptType); + + if (!msg.scriptType) return unknown; + + return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType); + } + case "ethereum": + return core.describeETHPath(msg.path); default: throw new Error("Unsupported path"); } @@ -256,19 +263,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public describePath(msg: core.DescribePath): core.PathDescription { - switch (msg.coin) { - case "bitcoin": { - const unknown = core.unknownUTXOPath(msg.path, msg.coin, msg.scriptType); - - if (!msg.scriptType) return unknown; - - return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType); - } - case "ethereum": - return core.describeETHPath(msg.path); - default: - throw new Error("Unsupported path"); - } + return this.info.describePath(msg); } // eslint-disable-next-line @typescript-eslint/no-unused-vars From 29f33c85023678af065c7d9fcdc384a54b907cf6 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:21:20 +0200 Subject: [PATCH 15/52] fix: btcSupportsCoin --- packages/hdwallet-phantom/src/phantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 37cf66896..03a294530 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -438,7 +438,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcSupportsCoin(coin: core.Coin): Promise { - throw new Error("Method not implemented."); + return coin === "bitcoin"; } // eslint-disable-next-line @typescript-eslint/no-unused-vars From 23fa8375a62caf51e75ca2bf86135af21b5318aa Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:30:58 +0200 Subject: [PATCH 16/52] feat: make it compile --- packages/hdwallet-phantom/src/phantom.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 03a294530..543f28abd 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -397,7 +397,8 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { const value = await (async () => { switch (msg.coin) { case "Bitcoin": { - const accounts = await this.bitcoinProvider.requestAccounts(); + // TODO(gomes): type this + const accounts = await (this.bitcoinProvider as any).requestAccounts(); const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; return paymentAddress; From a5b92966838dccc973a749f01f7e549baad6a8da Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:49:48 +0200 Subject: [PATCH 17/52] feat: btcSignMessage --- packages/hdwallet-phantom/src/phantom.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 543f28abd..c5b95dd4c 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -425,7 +425,12 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcSignMessage(msg: core.BTCSignMessage): Promise { - throw new Error("Method not implemented."); + const address = await this.btcGetAddress({ coin: "Bitcoin" } as core.BTCGetAddress); + const message = new TextEncoder().encode(msg.message); + + // TODO(gomes): type bitcoinpovider + const result = await (this.bitcoinProvider as any).signMessage(address, message); + return result; } // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { From e5b646ad530c74dce8c967eb4b57418f2981c79b Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:51:37 +0200 Subject: [PATCH 18/52] feat: bitcoin requestAccounts() --- packages/hdwallet-phantom/src/adapter.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 1d195bd7f..909e73c2f 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -48,6 +48,8 @@ export class PhantomAdapter { params: [{ eth_accounts: {} }], }) ); + // TODO(gomes): type bitcoinprovider + await (bitcoinProvider as any).requestAccounts(); } catch (error) { console.error("Could not get Phantom accounts. "); throw error; From 2d1645dbacd48504f91e1cf860c073fe6bdd2280 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:55:01 +0200 Subject: [PATCH 19/52] feat: safety --- packages/hdwallet-phantom/src/phantom.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index c5b95dd4c..e2752f4d3 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -426,11 +426,12 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcSignMessage(msg: core.BTCSignMessage): Promise { const address = await this.btcGetAddress({ coin: "Bitcoin" } as core.BTCGetAddress); + if (!address) throw new Error("Could not get Bitcoin address"); const message = new TextEncoder().encode(msg.message); // TODO(gomes): type bitcoinpovider - const result = await (this.bitcoinProvider as any).signMessage(address, message); - return result; + const { signature } = await (this.bitcoinProvider as any).signMessage(address, message); + return { signature, address }; } // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { From 76a77d611185a4348600006d84ac9a784c8929ad Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:05:56 +0200 Subject: [PATCH 20/52] Revert "feat: bitcoin requestAccounts()" This reverts commit e5b646ad530c74dce8c967eb4b57418f2981c79b. --- packages/hdwallet-phantom/src/adapter.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 909e73c2f..1d195bd7f 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -48,8 +48,6 @@ export class PhantomAdapter { params: [{ eth_accounts: {} }], }) ); - // TODO(gomes): type bitcoinprovider - await (bitcoinProvider as any).requestAccounts(); } catch (error) { console.error("Could not get Phantom accounts. "); throw error; From a80e1e1497a22b2b893db02d206d5efbf873cf67 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:14:03 +0200 Subject: [PATCH 21/52] feat: only p2wpkh effectively supported --- packages/hdwallet-phantom/src/bitcoin.ts | 17 +++++++++++------ packages/hdwallet-phantom/src/phantom.ts | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 3ac64fc14..436ea49a5 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -1,9 +1,12 @@ import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib"; import * as core from "@shapeshiftoss/hdwallet-core"; +import { BTCInputScriptType } from "@shapeshiftoss/hdwallet-core"; export type BtcAccount = { address: string; - addressType: "p2tr" | "p2wpkh" | "p2sh" | "p2pkh"; + // Phantom supposedly supports more scriptTypes but in effect, doesn't (currently) + // https://github.com/orgs/phantom/discussions/173 + addressType: BTCInputScriptType.SpendWitness; publicKey: string; purpose: "payment" | "ordinals"; }; @@ -53,11 +56,13 @@ async function addInput( if (input.scriptType) { switch (input.scriptType) { - case "p2pkh": - inputData.nonWitnessUtxo = Buffer.from(input.hex, "hex"); - break; - case "p2sh-p2wpkh": - case "p2wpkh": { + // Phantom supposedly supports more scriptTypes but in effect, doesn't (currently) + // https://github.com/orgs/phantom/discussions/173 + // case "p2pkh": + // inputData.nonWitnessUtxo = Buffer.from(input.hex, "hex"); + // break; + // case "p2sh-p2wpkh": + case BTCInputScriptType.SpendWitness: { const inputAddress = await wallet.btcGetAddress({ addressNList: input.addressNList, coin, showDisplay: false }); if (!inputAddress) throw new Error("Could not get address from wallet"); diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index e2752f4d3..14f3ff948 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -399,6 +399,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { case "Bitcoin": { // TODO(gomes): type this const accounts = await (this.bitcoinProvider as any).requestAccounts(); + console.log({ accounts }); const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; return paymentAddress; From e8633087b2acb984f73835f6f5e9a53e5bbf1053 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:21:53 +0200 Subject: [PATCH 22/52] feat: getPublicKeys --- packages/hdwallet-phantom/src/phantom.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 14f3ff948..e92cccb18 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -1,5 +1,5 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import { AddEthereumChainParameter } from "@shapeshiftoss/hdwallet-core"; +import { AddEthereumChainParameter, BTCInputScriptType } from "@shapeshiftoss/hdwallet-core"; import { providers } from "ethers"; import _ from "lodash"; @@ -268,8 +268,14 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async getPublicKeys(msg: Array): Promise> { + // Only p2wpkh effectively supported for now + if (msg[0].scriptType !== BTCInputScriptType.SpendWitness) return []; + + // Note this is a pubKey, not an xpub + const pubKey = await this.btcGetAddress({ coin: "Bitcoin" } as core.BTCGetAddress); + if (!pubKey) return []; // Ethereum public keys are not exposed by the RPC API - return []; + return [{ xpub: pubKey }]; } public async isInitialized(): Promise { @@ -399,7 +405,6 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { case "Bitcoin": { // TODO(gomes): type this const accounts = await (this.bitcoinProvider as any).requestAccounts(); - console.log({ accounts }); const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; return paymentAddress; From d14e51959d7247904e8ef708dd389346e596df95 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:50:19 +0200 Subject: [PATCH 23/52] feat: Bitcoin coin only --- packages/hdwallet-phantom/src/phantom.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index e92cccb18..4e6aa6480 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -269,6 +269,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async getPublicKeys(msg: Array): Promise> { // Only p2wpkh effectively supported for now + if (msg[0].coin !== "Bitcoin") return []; if (msg[0].scriptType !== BTCInputScriptType.SpendWitness) return []; // Note this is a pubKey, not an xpub From d227432f20a7285ff6f4e665425fda56b46599b9 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:36:51 +0200 Subject: [PATCH 24/52] fix: gasLimit over gas --- packages/hdwallet-phantom/src/ethereum.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts index 3f910a826..2e59d5647 100644 --- a/packages/hdwallet-phantom/src/ethereum.ts +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -65,11 +65,7 @@ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string value: msg.value, chainId: msg.chainId, data: msg.data && msg.data !== "" ? msg.data : undefined, - // Phantom, like other Web3 libraries, derives its transaction schema from Ethereum's official JSON-RPC API specification - // (https://github.com/ethereum/execution-apis/blob/d63d2a02bcd2a8cef54ae2fc5bbff8b4fac944eb/src/schemas/transaction.json). - // That schema defines the use of the label `gas` to set the transaction's gas limit and not `gasLimit` as used in other - // libraries and as stated in the official yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf). - gas: msg.gasLimit, + gasLimit: msg.gasLimit, }; const utx = msg.maxFeePerGas From 2094a1f8c1a50c14a32c6b8c4d17e539edf24e69 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:42:34 +0200 Subject: [PATCH 25/52] feat: add gasPrice --- packages/hdwallet-phantom/src/ethereum.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts index 2e59d5647..7f49839f5 100644 --- a/packages/hdwallet-phantom/src/ethereum.ts +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -66,6 +66,7 @@ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string chainId: msg.chainId, data: msg.data && msg.data !== "" ? msg.data : undefined, gasLimit: msg.gasLimit, + gasPrice: msg.gasPrice, }; const utx = msg.maxFeePerGas @@ -76,6 +77,7 @@ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string } : { ...utxBase, gasPrice: msg.gasPrice }; + console.log({ utx }); const signedTx = await ethereum.request({ method: "eth_sendTransaction", params: [utx], From df0d141322fe3f836b50e977cf2aeb5a0edffb6f Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:45:17 +0200 Subject: [PATCH 26/52] fix: psbt --- packages/hdwallet-phantom/src/bitcoin.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 436ea49a5..796e3d6b7 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -42,11 +42,13 @@ async function addInput( coin: string, network: bitcoin.networks.Network ): Promise { - const inputData: bitcoin.PsbtTxInput & { + const inputData: Omit & { nonWitnessUtxo?: Buffer; witnessUtxo?: { script: Buffer; value: number }; + hash: string | Buffer; } = { - hash: Buffer.from(input.txid, "hex"), + nonWitnessUtxo: input.hex ? Buffer.from(input.hex, "hex") : undefined, + hash: input.txid, index: input.vout, }; From 860373cdbc6e650549c7f509b97f1ed49090cc7e Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:49:50 +0200 Subject: [PATCH 27/52] feat: cleanup --- packages/hdwallet-phantom/src/ethereum.ts | 7 ------- packages/hdwallet-phantom/src/phantom.ts | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts index 7f49839f5..15907ab1c 100644 --- a/packages/hdwallet-phantom/src/ethereum.ts +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -51,12 +51,6 @@ export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { - console.error("Method ethSignTx unsupported for Phantom wallet!"); - return null; -} - export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string): Promise { try { const utxBase = { @@ -77,7 +71,6 @@ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string } : { ...utxBase, gasPrice: msg.gasPrice }; - console.log({ utx }); const signedTx = await ethereum.request({ method: "eth_sendTransaction", params: [utx], diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 4e6aa6480..6e2a9ed54 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -341,9 +341,9 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethSignTx(msg: core.ETHSignTx): Promise { - const address = await this.ethGetAddress(); - return address ? eth.ethSignTx(msg, this.evmProvider, address) : null; + throw new Error("unimplemented"); } public async ethSendTx(msg: core.ETHSignTx): Promise { From f620a165254797b76a712d06f03d930c5b516b25 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:52:07 +0200 Subject: [PATCH 28/52] feat: more cleanup --- packages/hdwallet-phantom/src/ethereum.ts | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts index 15907ab1c..0c9beef3a 100644 --- a/packages/hdwallet-phantom/src/ethereum.ts +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -2,6 +2,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { ETHSignedMessage } from "@shapeshiftoss/hdwallet-core"; import { isHexString } from "ethers/lib/utils"; +import { PhantomEvmProvider } from "./types"; + export function describeETHPath(path: core.BIP32Path): core.PathDescription { const pathStr = core.addressNListToBIP32(path); const unknown: core.PathDescription = { @@ -32,9 +34,13 @@ export function describeETHPath(path: core.BIP32Path): core.PathDescription { }; } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function ethVerifyMessage(msg: core.ETHVerifyMessage, ethereum: any): Promise { - console.error("Method ethVerifyMessage unsupported for Phantom wallet!"); +export async function ethVerifyMessage( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + msg: core.ETHVerifyMessage, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + phantom: PhantomEvmProvider +): Promise { + console.error("Unimplemented"); return null; } @@ -51,7 +57,11 @@ export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { +export async function ethSendTx( + msg: core.ETHSignTx, + phantom: PhantomEvmProvider, + from: string +): Promise { try { const utxBase = { from: from, @@ -71,7 +81,7 @@ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string } : { ...utxBase, gasPrice: msg.gasPrice }; - const signedTx = await ethereum.request({ + const signedTx = await phantom.request?.({ method: "eth_sendTransaction", params: [utx], }); @@ -87,12 +97,12 @@ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string export async function ethSignMessage( msg: core.ETHSignMessage, - ethereum: any, + phantom: PhantomEvmProvider, address: string ): Promise { try { if (!isHexString(msg.message)) throw new Error("data is not an hex string"); - const signedMsg = await ethereum.request({ + const signedMsg = await phantom.request?.({ method: "personal_sign", params: [msg.message, address], }); @@ -107,12 +117,12 @@ export async function ethSignMessage( } } -export async function ethGetAddress(ethereum: any): Promise { - if (!(ethereum && ethereum.request)) { +export async function ethGetAddress(phantom: PhantomEvmProvider): Promise { + if (!(phantom && phantom.request)) { return null; } try { - const ethAccounts = await ethereum.request({ + const ethAccounts = await phantom.request({ method: "eth_accounts", }); return ethAccounts[0]; From 6193e58c3e93077a374987794fd6f682d35e3149 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:53:03 +0200 Subject: [PATCH 29/52] feat: more more cleanup --- packages/hdwallet-phantom/src/phantom.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 6e2a9ed54..ce2cabfae 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -83,8 +83,7 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf // eslint-disable-next-line @typescript-eslint/no-unused-vars public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - // TODO: What do we do here? - return undefined; + throw new Error("Unimplemented"); } public async ethSupportsNetwork(chainId: number): Promise { From b82d05073a424e072406e2f3882029762e8605ab Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:53:37 +0200 Subject: [PATCH 30/52] feat: more more more cleanup --- packages/hdwallet-phantom/src/phantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index ce2cabfae..8295e90d2 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -87,7 +87,7 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf } public async ethSupportsNetwork(chainId: number): Promise { - return chainId === 1; + return chainId === 1 || chainId === 137; } public async ethSupportsSecureTransfer(): Promise { From d6e3e9668fbf628b447f6a21a92a51a1d8a8cfde Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:54:47 +0200 Subject: [PATCH 31/52] feat: more more more more cleanup --- packages/hdwallet-phantom/src/ethereum.ts | 10 ---------- packages/hdwallet-phantom/src/phantom.ts | 3 ++- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts index 0c9beef3a..15286f2ba 100644 --- a/packages/hdwallet-phantom/src/ethereum.ts +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -34,16 +34,6 @@ export function describeETHPath(path: core.BIP32Path): core.PathDescription { }; } -export async function ethVerifyMessage( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - msg: core.ETHVerifyMessage, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - phantom: PhantomEvmProvider -): Promise { - console.error("Unimplemented"); - return null; -} - export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { const slip44 = core.slip44ByCoin(msg.coin); if (slip44 === undefined) return []; diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 8295e90d2..e960d07d7 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -355,8 +355,9 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { - return eth.ethVerifyMessage(msg, this.evmProvider); + throw new Error("Method not implemented."); } public async getDeviceID(): Promise { From 128b40787f93e5c4710adb1d742f85730d28c1ac Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:55:14 +0200 Subject: [PATCH 32/52] feat: more more more more more cleanup --- packages/hdwallet-phantom/src/phantom.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index e960d07d7..7ba809d77 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -372,8 +372,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - // TODO: What do we do here? - return undefined; + throw new Error("Method not implemented."); } public async btcSupportsSecureTransfer(): Promise { From e810ac00b2ac95739fe3ba73546074c0524a7994 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:42:30 +0200 Subject: [PATCH 33/52] feat: rm address cache --- packages/hdwallet-phantom/src/phantom.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 7ba809d77..b1814e551 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -395,11 +395,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { throw new Error("Method not implemented."); } - addressCache: Map = new Map(); public async btcGetAddress(msg: core.BTCGetAddress): Promise { - const key = JSON.stringify(msg); - const maybeCachedAddress = this.addressCache.get(key); - if (maybeCachedAddress) return maybeCachedAddress; const value = await (async () => { switch (msg.coin) { case "Bitcoin": { @@ -415,7 +411,6 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { })(); if (!value || typeof value !== "string") return null; - this.addressCache.set(key, value); return value; } From de602be9c59ff9f91895915feb0b715f70ad0429 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:47:41 +0200 Subject: [PATCH 34/52] fix: tests types --- packages/hdwallet-phantom/src/phantom.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index 4a325e3e5..4c50a422a 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -47,6 +47,9 @@ describe("PhantomHDWallet", () => { it("should test ethSignMessage", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockReturnValue( `Object { "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", @@ -73,6 +76,9 @@ describe("PhantomHDWallet", () => { it("ethSignMessage returns null on error", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockRejectedValue(new Error("An Error has occurred")), }; @@ -87,6 +93,9 @@ describe("PhantomHDWallet", () => { it("ethGetAddress returns a valid address", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]), }; @@ -103,6 +112,9 @@ describe("PhantomHDWallet", () => { }); it("ethSendTx returns a valid hash", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockReturnValue("0x123"), }; @@ -121,6 +133,9 @@ describe("PhantomHDWallet", () => { }); it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockReturnValue("0x123"), }; @@ -139,6 +154,9 @@ describe("PhantomHDWallet", () => { }); it("ethSendTx returns null on error", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockRejectedValue(new Error("An Error has occurred")), }; @@ -157,6 +175,9 @@ describe("PhantomHDWallet", () => { }); it("ethVerifyMessage returns null as its not implemented", async () => { wallet.evmProvider = { + _metamask: { + isUnlocked: () => true, + }, request: jest.fn().mockReturnValue("0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"), }; expect( From 53a5cd0422a44e9db4c509a9cace177275bd3522 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:53:47 +0200 Subject: [PATCH 35/52] fix: test --- packages/hdwallet-phantom/src/phantom.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index 4c50a422a..7530cf3cc 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -173,7 +173,7 @@ describe("PhantomHDWallet", () => { expect(wallet.evmProvider.request).toHaveBeenCalled(); expect(hash).toBe(null); }); - it("ethVerifyMessage returns null as its not implemented", async () => { + it("ethVerifyMessage throws as its not implemented", async () => { wallet.evmProvider = { _metamask: { isUnlocked: () => true, @@ -187,6 +187,6 @@ describe("PhantomHDWallet", () => { signature: "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b", }) - ).toEqual(null); + ).toThrow(); }); }); From 8aee937000f7febf21365f3cec4217649f2f43c1 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:01:18 +0200 Subject: [PATCH 36/52] Revert "fix: test" This reverts commit 53a5cd0422a44e9db4c509a9cace177275bd3522. --- packages/hdwallet-phantom/src/phantom.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index 7530cf3cc..4c50a422a 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -173,7 +173,7 @@ describe("PhantomHDWallet", () => { expect(wallet.evmProvider.request).toHaveBeenCalled(); expect(hash).toBe(null); }); - it("ethVerifyMessage throws as its not implemented", async () => { + it("ethVerifyMessage returns null as its not implemented", async () => { wallet.evmProvider = { _metamask: { isUnlocked: () => true, @@ -187,6 +187,6 @@ describe("PhantomHDWallet", () => { signature: "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b", }) - ).toThrow(); + ).toEqual(null); }); }); From 5637977f02a822e4a2ce3e2863b82a282909eca3 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:01:36 +0200 Subject: [PATCH 37/52] fix: actually fix test --- packages/hdwallet-phantom/src/phantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index b1814e551..a696ef398 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -357,7 +357,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { - throw new Error("Method not implemented."); + return null; } public async getDeviceID(): Promise { From a9d3035a76c3397a655b00506362ddcb0b7d895a Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:03:23 +0200 Subject: [PATCH 38/52] feat: add window.ethereum --- packages/hdwallet-phantom/src/adapter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 1d195bd7f..928367ee7 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -6,6 +6,7 @@ import { PhantomEvmProvider } from "./types"; declare global { interface Window { + ethereum?: PhantomEvmProvider; phantom?: { ethereum?: PhantomEvmProvider; bitcoin?: providers.ExternalProvider; From 389d7e68f6ca9479c26fc60f5083c77275ae44a4 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:13:35 +0200 Subject: [PATCH 39/52] feat: cleanup --- packages/hdwallet-phantom/src/phantom.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index a696ef398..2730939d1 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -25,9 +25,6 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf readonly _supportsKavaInfo = false; readonly _supportsTerraInfo = false; - bitcoinAddress?: string | null; - ethAddress?: string | null; - public getVendor(): string { return "Phantom"; } From 8e1fff9d86217956cf3fffabaef7a689fe14486f Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:14:24 +0200 Subject: [PATCH 40/52] feat: more cleanup --- packages/hdwallet-phantom/src/phantom.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 2730939d1..171f09540 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -209,9 +209,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { return true; } - public async clearSession(): Promise { - // TODO: Can we lock Phantom from here? - } + public async clearSession(): Promise {} public async ping(msg: core.Ping): Promise { // no ping function for Phantom, so just returning Core.Pong From a5078c5fb5db4533fd47c61f3051e59aa863c65d Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:14:35 +0200 Subject: [PATCH 41/52] feat: more more cleaup --- packages/hdwallet-phantom/src/phantom.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 171f09540..6e7f9799b 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -252,9 +252,7 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async loadDevice(msg: core.LoadDevice): Promise { - // TODO: Does Phantom allow this to be done programatically? - } + public async loadDevice(msg: core.LoadDevice): Promise {} public describePath(msg: core.DescribePath): core.PathDescription { return this.info.describePath(msg); From 248372b06571c3e43336e441abef41608f80f967 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:18:59 +0200 Subject: [PATCH 42/52] feat: more cleanup --- packages/hdwallet-phantom/src/adapter.ts | 5 ++--- packages/hdwallet-phantom/src/phantom.ts | 18 +++++++----------- packages/hdwallet-phantom/src/types.ts | 12 ++++++++++++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 928367ee7..4f9d75fe9 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -1,15 +1,14 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import { providers } from "ethers"; import { PhantomHDWallet } from "./phantom"; -import { PhantomEvmProvider } from "./types"; +import { PhantomEvmProvider, PhantomUtxoProvider } from "./types"; declare global { interface Window { ethereum?: PhantomEvmProvider; phantom?: { ethereum?: PhantomEvmProvider; - bitcoin?: providers.ExternalProvider; + bitcoin?: PhantomUtxoProvider; // TODO: update with proper types once implemented // https://github.com/anza-xyz/wallet-adapter/blob/3761cd8cc867da39da7c0b070bbf8779402cff36/packages/wallets/phantom/src/adapter.ts#L36 solana?: any; diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 6e7f9799b..a62322ecf 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -1,12 +1,10 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { AddEthereumChainParameter, BTCInputScriptType } from "@shapeshiftoss/hdwallet-core"; -import { providers } from "ethers"; import _ from "lodash"; import * as Btc from "./bitcoin"; -import { BtcAccount } from "./bitcoin"; import * as eth from "./ethereum"; -import { PhantomEvmProvider } from "./types"; +import { PhantomEvmProvider, PhantomUtxoProvider } from "./types"; export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet { return _.isObject(wallet) && (wallet as any)._isPhantom; @@ -145,9 +143,9 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { info: PhantomHDWalletInfo & core.HDWalletInfo; ethAddress?: string | null; evmProvider: PhantomEvmProvider; - bitcoinProvider: providers.ExternalProvider; + bitcoinProvider: PhantomUtxoProvider; - constructor(evmProvider: PhantomEvmProvider, bitcoinProvider: providers.ExternalProvider) { + constructor(evmProvider: PhantomEvmProvider, bitcoinProvider: PhantomUtxoProvider) { this.info = new PhantomHDWalletInfo(); this.evmProvider = evmProvider; this.bitcoinProvider = bitcoinProvider; @@ -392,9 +390,8 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { const value = await (async () => { switch (msg.coin) { case "Bitcoin": { - // TODO(gomes): type this - const accounts = await (this.bitcoinProvider as any).requestAccounts(); - const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; + const accounts = await this.bitcoinProvider.requestAccounts(); + const paymentAddress = accounts.find((account) => account.purpose === "payment")?.address; return paymentAddress; } @@ -423,9 +420,8 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { if (!address) throw new Error("Could not get Bitcoin address"); const message = new TextEncoder().encode(msg.message); - // TODO(gomes): type bitcoinpovider - const { signature } = await (this.bitcoinProvider as any).signMessage(address, message); - return { signature, address }; + const { signature } = await this.bitcoinProvider.signMessage(address, message); + return { signature: core.toHexString(signature), address }; } // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { diff --git a/packages/hdwallet-phantom/src/types.ts b/packages/hdwallet-phantom/src/types.ts index e95bbe4eb..b823dbc51 100644 --- a/packages/hdwallet-phantom/src/types.ts +++ b/packages/hdwallet-phantom/src/types.ts @@ -1,7 +1,19 @@ import { providers } from "ethers"; +import { BtcAccount } from "./bitcoin"; + export type PhantomEvmProvider = providers.ExternalProvider & { _metamask: { isUnlocked: () => boolean; }; }; + +export type PhantomUtxoProvider = providers.ExternalProvider & { + requestAccounts: () => Promise; + signMessage: ( + address: string, + message: Uint8Array + ) => Promise<{ + signature: Uint8Array; + }>; +}; From e149db1ddc0ca05da76c717cc6eac6ddf6144aa6 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:53:15 +0200 Subject: [PATCH 43/52] fix: OP_RETURN_DATA :fridaydog: --- packages/hdwallet-phantom/src/bitcoin.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 796e3d6b7..39b1ad98d 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -103,13 +103,6 @@ async function addOutput( address: outputAddress, value: parseInt(output.amount), }); - } else if ("opReturnData" in output && output.opReturnData) { - const data = Buffer.from(output.opReturnData.toString(), "hex"); - const embed = bitcoin.payments.embed({ data: [data] }); - psbt.addOutput({ - script: embed.output!, - value: 0, - }); } } @@ -139,6 +132,15 @@ export async function bitcoinSignTx( await addOutput(wallet, psbt, output, msg.coin); } + if (msg.opReturnData) { + const data = Buffer.from(msg.opReturnData, "utf-8"); + const embed = bitcoin.payments.embed({ data: [data] }); + psbt.addOutput({ + script: embed.output!, + value: 0, + }); + } + const inputsToSign = await Promise.all( msg.inputs.map(async (input, index) => { const address = await wallet.btcGetAddress({ From f528f10610e0a30f9573268b9f74f5849db6619a Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:26:18 +0200 Subject: [PATCH 44/52] feat: more cleanupy --- packages/hdwallet-phantom/src/phantom.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index a62322ecf..689199d46 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -141,7 +141,6 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsTerraInfo = false; info: PhantomHDWalletInfo & core.HDWalletInfo; - ethAddress?: string | null; evmProvider: PhantomEvmProvider; bitcoinProvider: PhantomUtxoProvider; @@ -318,17 +317,8 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { } public async ethGetAddress(): Promise { - if (this.ethAddress) { - return this.ethAddress; - } const address = await eth.ethGetAddress(this.evmProvider); - if (address) { - this.ethAddress = address; - return address; - } else { - this.ethAddress = null; - return null; - } + return address; } // eslint-disable-next-line @typescript-eslint/no-unused-vars From 66b2348fa2aacccc0a51c0ab9069c09bb8c709c5 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:43:33 +0200 Subject: [PATCH 45/52] feat: saner tests --- packages/hdwallet-phantom/src/phantom.test.ts | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index 4c50a422a..6882ddd8b 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -1,6 +1,7 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { PhantomHDWallet, PhantomHDWalletInfo } from "."; +import { PhantomUtxoProvider } from "./types"; describe("HDWalletInfo", () => { const info = new PhantomHDWalletInfo(); @@ -27,7 +28,6 @@ describe("PhantomHDWallet", () => { core.untouchable("PhantomHDWallet:provider"), core.untouchable("PhantomHDWallet:provider") ); - wallet.ethAddress = "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"; }); it("should match the metadata", async () => { @@ -64,14 +64,14 @@ describe("PhantomHDWallet", () => { message: msg, }) ).toMatchInlineSnapshot(` - Object { - "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", - "signature": "Object { - \\"address\\": \\"0x73d0385F4d8E00C5e6504C6030F47BF6212736A8\\", - \\"signature\\": \\"0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b\\", - }", - } - `); + Object { + "address": "O", + "signature": "Object { + \\"address\\": \\"0x73d0385F4d8E00C5e6504C6030F47BF6212736A8\\", + \\"signature\\": \\"0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b\\", + }", + } + `); }); it("ethSignMessage returns null on error", async () => { @@ -99,17 +99,27 @@ describe("PhantomHDWallet", () => { request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]), }; - const msg = "0x737570657220736563726574206d657373616765"; // super secret message - const sig = await wallet.ethSignMessage({ - addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), - message: msg, - }); + const address = await wallet.ethGetAddress(); - expect(sig).toMatchObject({ - address: "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", - signature: ["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"], - }); + expect(address).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"); }); + it("btcGetAddress returns a valid address", async () => { + wallet.bitcoinProvider = { + requestAccounts: jest.fn().mockReturnValue([ + { + purpose: "payment", + address: "bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg", + }, + ]), + } as unknown as PhantomUtxoProvider; + + const address = await wallet.btcGetAddress({ + coin: "Bitcoin", + } as core.BTCGetAddress); + + expect(address).toEqual("bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg"); + }); + it("ethSendTx returns a valid hash", async () => { wallet.evmProvider = { _metamask: { From 0f648fd73ec0f9f3f41a66ffda4d64e9b09d859e Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:30:05 +0200 Subject: [PATCH 46/52] feat: up-to-date hdwallet deps --- packages/hdwallet-phantom/package.json | 2 +- yarn.lock | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json index 5a5b2ff37..7345023fd 100644 --- a/packages/hdwallet-phantom/package.json +++ b/packages/hdwallet-phantom/package.json @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.54.2", + "@shapeshiftoss/hdwallet-core": "1.55.4", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 19f5ca127..d032ca968 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4646,18 +4646,6 @@ web-encoding "^1.1.0" wif "^2.0.6" -"@shapeshiftoss/hdwallet-core@1.54.2": - version "1.54.2" - resolved "https://registry.yarnpkg.com/@shapeshiftoss/hdwallet-core/-/hdwallet-core-1.54.2.tgz#0bcc9fe56bd869b7942ffd36735190d5692bb97d" - integrity sha512-bJx0vgpudvq+k1ZRG5+xGx7w5w7sJDyDQcdHtoILhB9htgt36ILjsmCJSrpc+0LSIwP2P/LLw3cg88beqahVNw== - dependencies: - "@shapeshiftoss/proto-tx-builder" "^0.8.0" - eip-712 "^1.0.0" - eventemitter2 "^5.0.1" - lodash "^4.17.21" - rxjs "^6.4.0" - type-assertions "^1.1.0" - "@shapeshiftoss/hdwallet-core@latest": version "1.55.2" resolved "https://registry.yarnpkg.com/@shapeshiftoss/hdwallet-core/-/hdwallet-core-1.55.2.tgz#1b153d3a1edaf4f38ea7295ff9363a76f02c2f43" From dd5b0f17479255e6cbdac0974ecf52d64dd1ec77 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:14:18 -0600 Subject: [PATCH 47/52] cleanup --- .../ethereum/OpenSea-ethSignTypedDataV4.json | 2 +- examples/sandbox/json/ethereum/ethTx.json | 3 +- examples/sandbox/package.json | 2 +- packages/hdwallet-core/package.json | 1 + packages/hdwallet-core/src/ethereum.ts | 14 + .../src/crypto/isolation/adapters/ethereum.ts | 3 +- packages/hdwallet-native/src/ethereum.ts | 3 +- packages/hdwallet-native/src/util.ts | 14 - packages/hdwallet-phantom/package.json | 4 +- packages/hdwallet-phantom/src/bitcoin.ts | 22 +- packages/hdwallet-phantom/src/ethereum.ts | 58 ++- packages/hdwallet-phantom/src/phantom.test.ts | 25 +- packages/hdwallet-phantom/src/phantom.ts | 370 +++++++----------- 13 files changed, 213 insertions(+), 308 deletions(-) diff --git a/examples/sandbox/json/ethereum/OpenSea-ethSignTypedDataV4.json b/examples/sandbox/json/ethereum/OpenSea-ethSignTypedDataV4.json index 44b3a7400..bd20f0def 100644 --- a/examples/sandbox/json/ethereum/OpenSea-ethSignTypedDataV4.json +++ b/examples/sandbox/json/ethereum/OpenSea-ethSignTypedDataV4.json @@ -118,7 +118,7 @@ "domain": { "name": "Seaport", "version": "1.5", - "chainId": 137, + "chainId": 1, "verifyingContract": "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC" }, "message": { diff --git a/examples/sandbox/json/ethereum/ethTx.json b/examples/sandbox/json/ethereum/ethTx.json index c43243e07..7420ccdd6 100644 --- a/examples/sandbox/json/ethereum/ethTx.json +++ b/examples/sandbox/json/ethereum/ethTx.json @@ -156,7 +156,8 @@ "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "chainId": 1 }, - "primaryType": "EIP712Domain" + "primaryType": "EIP712Domain", + "message": {} } } }, diff --git a/examples/sandbox/package.json b/examples/sandbox/package.json index c33cdd541..e46ebeef8 100644 --- a/examples/sandbox/package.json +++ b/examples/sandbox/package.json @@ -22,7 +22,7 @@ "@shapeshiftoss/hdwallet-ledger-webhid": "1.55.5", "@shapeshiftoss/hdwallet-ledger-webusb": "1.55.5", "@shapeshiftoss/hdwallet-metamask": "1.55.5", - "@shapeshiftoss/hdwallet-phantom": "1.55.4", + "@shapeshiftoss/hdwallet-phantom": "1.55.5", "@shapeshiftoss/hdwallet-native": "1.55.5", "@shapeshiftoss/hdwallet-portis": "1.55.5", "@shapeshiftoss/hdwallet-tallyho": "1.55.5", diff --git a/packages/hdwallet-core/package.json b/packages/hdwallet-core/package.json index 0655e3ae6..3e73592dc 100644 --- a/packages/hdwallet-core/package.json +++ b/packages/hdwallet-core/package.json @@ -16,6 +16,7 @@ "dependencies": { "@shapeshiftoss/proto-tx-builder": "^0.8.0", "eip-712": "^1.0.0", + "ethers": "5.7.2", "eventemitter2": "^5.0.1", "lodash": "^4.17.21", "rxjs": "^6.4.0", diff --git a/packages/hdwallet-core/src/ethereum.ts b/packages/hdwallet-core/src/ethereum.ts index 16d15a465..22bf307f2 100644 --- a/packages/hdwallet-core/src/ethereum.ts +++ b/packages/hdwallet-core/src/ethereum.ts @@ -1,5 +1,6 @@ import { Bytes } from "@ethersproject/bytes"; import { TypedData } from "eip-712"; +import { ethers } from "ethers"; import { addressNListToBIP32, slip44ByCoin } from "./utils"; import { BIP32Path, HDWallet, HDWalletInfo, PathDescription } from "./wallet"; @@ -240,3 +241,16 @@ export function describeETHPath(path: BIP32Path): PathDescription { isPrefork: false, }; } + +export function buildMessage(message: ethers.utils.BytesLike): Uint8Array { + const messageBytes = + typeof message === "string" && !ethers.utils.isHexString(message) + ? ethers.utils.toUtf8Bytes(message) + : ethers.utils.arrayify(message); + + return ethers.utils.concat([ + ethers.utils.toUtf8Bytes("\x19Ethereum Signed Message:\n"), + ethers.utils.toUtf8Bytes(String(messageBytes.length)), + messageBytes, + ]); +} diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts index f6e4c284a..e547ea1c7 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts @@ -12,7 +12,6 @@ import { splitSignature, } from "ethers/lib/utils.js"; -import { buildMessage } from "../../../util"; import { Isolation } from "../.."; import { SecP256K1 } from "../core"; @@ -80,7 +79,7 @@ export class SignerAdapter { } async signMessage(messageData: BytesLike, addressNList: core.BIP32Path): Promise { - const messageBuf = buildMessage(messageData); + const messageBuf = core.buildMessage(messageData); const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList)); const rawSig = await SecP256K1.RecoverableSignature.signCanonically(nodeAdapter.node, "keccak256", messageBuf); return joinSignature(ethSigFromRecoverableSig(rawSig)); diff --git a/packages/hdwallet-native/src/ethereum.ts b/packages/hdwallet-native/src/ethereum.ts index ff35ea5ff..f70a5b18b 100644 --- a/packages/hdwallet-native/src/ethereum.ts +++ b/packages/hdwallet-native/src/ethereum.ts @@ -4,7 +4,6 @@ import { keccak256, parseTransaction, recoverAddress } from "ethers/lib/utils.js import * as Isolation from "./crypto/isolation"; import SignerAdapter from "./crypto/isolation/adapters/ethereum"; import { NativeHDWalletBase } from "./native"; -import { buildMessage } from "./util"; export function MixinNativeETHWalletInfo>(Base: TBase) { // eslint-disable-next-line @typescript-eslint/no-shadow @@ -142,7 +141,7 @@ export function MixinNativeETHWallet { if (!signature.startsWith("0x")) signature = `0x${signature}`; - const digest = keccak256(buildMessage(message)); + const digest = keccak256(core.buildMessage(message)); return recoverAddress(digest, signature) === address; } }; diff --git a/packages/hdwallet-native/src/util.ts b/packages/hdwallet-native/src/util.ts index 1a7c7862f..a6a0c4e46 100644 --- a/packages/hdwallet-native/src/util.ts +++ b/packages/hdwallet-native/src/util.ts @@ -1,5 +1,4 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import { ethers } from "ethers"; import { BTCScriptType } from "./bitcoin"; import * as Isolation from "./crypto/isolation"; @@ -16,16 +15,3 @@ export async function getKeyPair( const path = core.addressNListToBIP32(addressNList); return await wallet.derivePath(path); } - -export function buildMessage(message: ethers.utils.BytesLike): Uint8Array { - const messageBytes = - typeof message === "string" && !ethers.utils.isHexString(message) - ? ethers.utils.toUtf8Bytes(message) - : ethers.utils.arrayify(message); - - return ethers.utils.concat([ - ethers.utils.toUtf8Bytes("\x19Ethereum Signed Message:\n"), - ethers.utils.toUtf8Bytes(String(messageBytes.length)), - messageBytes, - ]); -} diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json index 2804f6d7f..460f07687 100644 --- a/packages/hdwallet-phantom/package.json +++ b/packages/hdwallet-phantom/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-phantom", - "version": "1.55.4", + "version": "1.55.5", "license": "MIT", "publishConfig": { "access": "public" @@ -15,6 +15,8 @@ }, "dependencies": { "@shapeshiftoss/hdwallet-core": "1.55.5", + "base64-js": "^1.5.1", + "bitcoinjs-message": "^2.0.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/hdwallet-phantom/src/bitcoin.ts b/packages/hdwallet-phantom/src/bitcoin.ts index 39b1ad98d..aa03ed6d7 100644 --- a/packages/hdwallet-phantom/src/bitcoin.ts +++ b/packages/hdwallet-phantom/src/bitcoin.ts @@ -27,7 +27,27 @@ const getNetwork = (coin: string): bitcoin.networks.Network => { } }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const btcGetAccountPaths = (msg: core.BTCGetAccountPaths): Array => { + const slip44 = core.slip44ByCoin(msg.coin); + if (slip44 === undefined) return []; + + const bip84 = core.segwitNativeAccount(msg.coin, slip44, msg.accountIdx); + + const coinPaths = { + bitcoin: [bip84], + } as Partial>>; + + let paths: Array = coinPaths[msg.coin.toLowerCase()] || []; + + if (msg.scriptType !== undefined) { + paths = paths.filter((path) => { + return path.scriptType === msg.scriptType; + }); + } + + return paths; +}; + export async function bitcoinGetAddress(_msg: core.BTCGetAddress, provider: any): Promise { const accounts = await provider.requestAccounts(); const paymentAddress = accounts.find((account: BtcAccount) => account.purpose === "payment")?.address; diff --git a/packages/hdwallet-phantom/src/ethereum.ts b/packages/hdwallet-phantom/src/ethereum.ts index 15286f2ba..ee483f872 100644 --- a/packages/hdwallet-phantom/src/ethereum.ts +++ b/packages/hdwallet-phantom/src/ethereum.ts @@ -4,36 +4,6 @@ import { isHexString } from "ethers/lib/utils"; import { PhantomEvmProvider } from "./types"; -export function describeETHPath(path: core.BIP32Path): core.PathDescription { - const pathStr = core.addressNListToBIP32(path); - const unknown: core.PathDescription = { - verbose: pathStr, - coin: "Ethereum", - isKnown: false, - }; - - if (path.length !== 5) return unknown; - - if (path[0] !== 0x80000000 + 44) return unknown; - - if (path[1] !== 0x80000000 + core.slip44ByCoin("Ethereum")) return unknown; - - if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) return unknown; - - if (path[3] !== 0) return unknown; - - if (path[4] !== 0) return unknown; - - const index = path[2] & 0x7fffffff; - return { - verbose: `Ethereum Account #${index}`, - accountIdx: index, - wholeAccount: true, - coin: "Ethereum", - isKnown: true, - }; -} - export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { const slip44 = core.slip44ByCoin(msg.coin); if (slip44 === undefined) return []; @@ -58,9 +28,8 @@ export async function ethSendTx( to: msg.to, value: msg.value, chainId: msg.chainId, - data: msg.data && msg.data !== "" ? msg.data : undefined, + data: msg.data, gasLimit: msg.gasLimit, - gasPrice: msg.gasPrice, }; const utx = msg.maxFeePerGas @@ -76,9 +45,7 @@ export async function ethSendTx( params: [utx], }); - return { - hash: signedTx, - } as core.ETHTxHash; + return { hash: signedTx } as core.ETHTxHash; } catch (error) { console.error(error); return null; @@ -107,6 +74,27 @@ export async function ethSignMessage( } } +export async function ethSignTypedData( + msg: core.ETHSignTypedData, + phantom: PhantomEvmProvider, + address: string +): Promise { + try { + const signedMsg = await phantom.request?.({ + method: "eth_signTypedData_v4", + params: [address, JSON.stringify(msg.typedData)], + }); + + return { + address: address, + signature: signedMsg, + } as ETHSignedMessage; + } catch (error) { + console.error(error); + return null; + } +} + export async function ethGetAddress(phantom: PhantomEvmProvider): Promise { if (!(phantom && phantom.request)) { return null; diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index 6882ddd8b..ed089165f 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -1,28 +1,11 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import { PhantomHDWallet, PhantomHDWalletInfo } from "."; +import { PhantomHDWallet } from "."; import { PhantomUtxoProvider } from "./types"; -describe("HDWalletInfo", () => { - const info = new PhantomHDWalletInfo(); - - it("should have correct metadata", async () => { - expect(info.getVendor()).toBe("Phantom"); - expect(info.hasOnDevicePinEntry()).toBe(false); - expect(info.hasOnDevicePassphrase()).toBe(true); - expect(info.hasOnDeviceDisplay()).toBe(true); - expect(info.hasOnDeviceRecovery()).toBe(true); - expect(await info.ethSupportsNetwork(1)).toBe(true); - expect(await info.ethSupportsSecureTransfer()).toBe(false); - expect(info.ethSupportsNativeShapeShift()).toBe(false); - expect(await info.ethSupportsEIP1559()).toBe(true); - expect(await info.supportsOfflineSigning()).toBe(false); - expect(await info.supportsBroadcast()).toBe(true); - }); -}); - describe("PhantomHDWallet", () => { let wallet: PhantomHDWallet; + beforeEach(() => { wallet = new PhantomHDWallet( core.untouchable("PhantomHDWallet:provider"), @@ -40,9 +23,9 @@ describe("PhantomHDWallet", () => { expect(await wallet.ethSupportsSecureTransfer()).toBe(false); expect(wallet.ethSupportsNativeShapeShift()).toBe(false); expect(await wallet.ethSupportsEIP1559()).toBe(true); - expect(await wallet.supportsOfflineSigning()).toBe(false); + expect(wallet.supportsOfflineSigning()).toBe(false); expect(wallet.supportsBip44Accounts()).toBe(false); - expect(await wallet.supportsBroadcast()).toBe(true); + expect(wallet.supportsBroadcast()).toBe(true); }); it("should test ethSignMessage", async () => { diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index 689199d46..30c001106 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -1,8 +1,11 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import { AddEthereumChainParameter, BTCInputScriptType } from "@shapeshiftoss/hdwallet-core"; +import { BTCInputScriptType } from "@shapeshiftoss/hdwallet-core"; +import Base64 from "base64-js"; +import * as bitcoinMsg from "bitcoinjs-message"; +import { keccak256, recoverAddress } from "ethers/lib/utils.js"; import _ from "lodash"; -import * as Btc from "./bitcoin"; +import * as btc from "./bitcoin"; import * as eth from "./ethereum"; import { PhantomEvmProvider, PhantomUtxoProvider } from "./types"; @@ -10,18 +13,15 @@ export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet { return _.isObject(wallet) && (wallet as any)._isPhantom; } -export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo { - readonly _supportsBTCInfo = false; +export class PhantomHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo, core.ETHWalletInfo { + readonly _supportsBTCInfo = true; readonly _supportsETHInfo = true; - readonly _supportsCosmosInfo = false; - readonly _supportsBinanceInfo = false; - readonly _supportsRippleInfo = false; - readonly _supportsEosInfo = false; - readonly _supportsFioInfo = false; - readonly _supportsThorchainInfo = false; - readonly _supportsSecretInfo = false; - readonly _supportsKavaInfo = false; - readonly _supportsTerraInfo = false; + + evmProvider: PhantomEvmProvider; + + constructor(evmProvider: PhantomEvmProvider) { + this.evmProvider = evmProvider; + } public getVendor(): string { return "Phantom"; @@ -61,11 +61,13 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf } public describePath(msg: core.DescribePath): core.PathDescription { - switch (msg.coin) { + switch (msg.coin.toLowerCase()) { case "bitcoin": { const unknown = core.unknownUTXOPath(msg.path, msg.coin, msg.scriptType); if (!msg.scriptType) return unknown; + if (!this.btcSupportsCoin(msg.coin)) return unknown; + if (!this.btcSupportsScriptType(msg.coin, msg.scriptType)) return unknown; return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType); } @@ -76,13 +78,22 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - throw new Error("Unimplemented"); - } + /** Ethereum */ public async ethSupportsNetwork(chainId: number): Promise { - return chainId === 1 || chainId === 137; + return chainId === 1; + } + + public async ethGetChainId(): Promise { + try { + if (!this.evmProvider.request) throw new Error("Provider does not support ethereum.request"); + // chainId as hex string + const chainId: string = await this.evmProvider.request({ method: "eth_chainId" }); + return parseInt(chainId, 16); + } catch (e) { + console.error(e); + return null; + } } public async ethSupportsSecureTransfer(): Promise { @@ -100,66 +111,86 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInf public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { return eth.ethGetAccountPaths(msg); } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + throw new Error("Method not implemented"); + } + + /** Bitcoin */ + + public async btcSupportsCoin(coin: core.Coin): Promise { + return coin === "bitcoin"; + } + + public async btcSupportsScriptType(coin: string, scriptType?: core.BTCInputScriptType | undefined): Promise { + if (!this.btcSupportsCoin(coin)) return false; + + switch (scriptType) { + case core.BTCInputScriptType.SpendWitness: + return true; + default: + return false; + } + } + + public async btcSupportsSecureTransfer(): Promise { + return false; + } + + public btcSupportsNativeShapeShift(): boolean { + return false; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array { + return btc.btcGetAccountPaths(msg); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcIsSameAccount(msg: core.BTCAccountPath[]): boolean { + throw new Error("Method not implemented."); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + throw new Error("Method not implemented"); + } } -export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { - readonly _supportsETH = true; - readonly _supportsETHInfo = true; - readonly _supportsBTCInfo = false; +export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWallet, core.BTCWallet, core.ETHWallet { readonly _supportsBTC = true; - readonly _supportsCosmosInfo = false; - readonly _supportsCosmos = false; + readonly _supportsETH = true; readonly _supportsEthSwitchChain = false; readonly _supportsAvalanche = false; readonly _supportsOptimism = false; - readonly _supportsBSC = false; readonly _supportsPolygon = true; readonly _supportsGnosis = false; readonly _supportsArbitrum = false; readonly _supportsArbitrumNova = false; readonly _supportsBase = false; - readonly _supportsOsmosisInfo = false; - readonly _supportsOsmosis = false; - readonly _supportsBinanceInfo = false; - readonly _supportsBinance = false; - readonly _supportsDebugLink = false; - readonly _isPortis = false; + readonly _supportsBSC = false; readonly _isPhantom = true; - readonly _supportsRippleInfo = false; - readonly _supportsRipple = false; - readonly _supportsEosInfo = false; - readonly _supportsEos = false; - readonly _supportsFioInfo = false; - readonly _supportsFio = false; - readonly _supportsThorchainInfo = false; - readonly _supportsThorchain = false; - readonly _supportsSecretInfo = false; - readonly _supportsSecret = false; - readonly _supportsKava = false; - readonly _supportsKavaInfo = false; - readonly _supportsTerra = false; - readonly _supportsTerraInfo = false; - - info: PhantomHDWalletInfo & core.HDWalletInfo; + evmProvider: PhantomEvmProvider; bitcoinProvider: PhantomUtxoProvider; constructor(evmProvider: PhantomEvmProvider, bitcoinProvider: PhantomUtxoProvider) { - this.info = new PhantomHDWalletInfo(); + super(evmProvider); this.evmProvider = evmProvider; this.bitcoinProvider = bitcoinProvider; } - async getFeatures(): Promise> { - return {}; + public async getDeviceID(): Promise { + return "phantom:" + (await this.ethGetAddress()); } - public async isLocked(): Promise { - return !this.evmProvider._metamask.isUnlocked(); + async getFeatures(): Promise> { + return {}; } - public getVendor(): string { - return "Phantom"; + public async getFirmwareVersion(): Promise { + return "phantom"; } public async getModel(): Promise { @@ -170,160 +201,76 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { return "Phantom"; } - public async initialize(): Promise { - // nothing to initialize - } - - public hasOnDevicePinEntry(): boolean { - return this.info.hasOnDevicePinEntry(); - } - - public hasOnDevicePassphrase(): boolean { - return this.info.hasOnDevicePassphrase(); - } - - public hasOnDeviceDisplay(): boolean { - return this.info.hasOnDeviceDisplay(); - } - - public hasOnDeviceRecovery(): boolean { - return this.info.hasOnDeviceRecovery(); - } - - public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { - return this.info.hasNativeShapeShift(srcCoin, dstCoin); - } - - public supportsBip44Accounts(): boolean { - return this.info.supportsBip44Accounts(); - } - - public supportsOfflineSigning(): boolean { - return false; + public async isInitialized(): Promise { + return true; } - public supportsBroadcast(): boolean { - return true; + public async isLocked(): Promise { + return !this.evmProvider._metamask.isUnlocked(); } public async clearSession(): Promise {} + public async initialize(): Promise {} + public async ping(msg: core.Ping): Promise { - // no ping function for Phantom, so just returning Core.Pong return { msg: msg.msg }; } // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async sendPin(pin: string): Promise { - // no concept of pin in Phantom - } + public async sendPin(pin: string): Promise {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async sendPassphrase(passphrase: string): Promise { - // cannot send passphrase to Phantom. Could show the widget? - } + public async sendPassphrase(passphrase: string): Promise {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async sendCharacter(charater: string): Promise { - // no concept of sendCharacter in Phantom - } + public async sendCharacter(charater: string): Promise {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async sendWord(word: string): Promise { - // no concept of sendWord in Phantom - } + public async sendWord(word: string): Promise {} - public async cancel(): Promise { - // no concept of cancel in Phantom - } + public async cancel(): Promise {} - // eslint-disable-next-line @typescript-eslint/no-empty-function public async wipe(): Promise {} - // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async reset(msg: core.ResetDevice): Promise {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async recover(msg: core.RecoverDevice): Promise { - // no concept of recover in Phantom - } + public async recover(msg: core.RecoverDevice): Promise {} // eslint-disable-next-line @typescript-eslint/no-unused-vars public async loadDevice(msg: core.LoadDevice): Promise {} - public describePath(msg: core.DescribePath): core.PathDescription { - return this.info.describePath(msg); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async getPublicKeys(msg: Array): Promise> { - // Only p2wpkh effectively supported for now - if (msg[0].coin !== "Bitcoin") return []; - if (msg[0].scriptType !== BTCInputScriptType.SpendWitness) return []; - - // Note this is a pubKey, not an xpub - const pubKey = await this.btcGetAddress({ coin: "Bitcoin" } as core.BTCGetAddress); - if (!pubKey) return []; - // Ethereum public keys are not exposed by the RPC API - return [{ xpub: pubKey }]; - } - - public async isInitialized(): Promise { - return true; - } - - // eslint-disable-next-line @typescript-eslint/no-empty-function public async disconnect(): Promise {} - public async ethSupportsNetwork(chainId = 1): Promise { - return chainId === 1; - } - - public async ethGetChainId(): Promise { - try { - if (!this.evmProvider.request) throw new Error("Provider does not support ethereum.request"); - // chainId as hex string - const chainId: string = await this.evmProvider.request({ method: "eth_chainId" }); - return parseInt(chainId, 16); - } catch (e) { - console.error(e); - return null; - } - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async ethSwitchChain(params: AddEthereumChainParameter): Promise { - // no concept of switch chain in phantom - } - - public async ethSupportsSecureTransfer(): Promise { - return false; - } - - public ethSupportsNativeShapeShift(): boolean { - return false; - } - - public async ethSupportsEIP1559(): Promise { - return true; - } + public async getPublicKeys(msg: Array): Promise> { + return await Promise.all( + msg.map(async (getPublicKey) => { + const { coin, scriptType } = getPublicKey; + + // Only p2wpkh effectively supported for now + if (coin === "Bitcoin" && scriptType === BTCInputScriptType.SpendWitness) { + // Note this is a pubKey, not an xpub, however phantom does not support utxo derivation, + // so this functions as an account (xpub) for all intents and purposes + const pubKey = await this.btcGetAddress({ coin: "Bitcoin" } as core.BTCGetAddress); + return { xpub: pubKey } as core.PublicKey; + } - public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { - return eth.ethGetAccountPaths(msg); + return null; + }) + ); } - public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - return this.info.ethNextAccountPath(msg); - } + /** Ethereum */ public async ethGetAddress(): Promise { - const address = await eth.ethGetAddress(this.evmProvider); - return address; + return eth.ethGetAddress(this.evmProvider); } // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethSignTx(msg: core.ETHSignTx): Promise { - throw new Error("unimplemented"); + throw new Error("Method not implemented"); } public async ethSendTx(msg: core.ETHSignTx): Promise { @@ -336,45 +283,18 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { - return null; - } - - public async getDeviceID(): Promise { - return "phantom:" + (await this.ethGetAddress()); - } - - public async getFirmwareVersion(): Promise { - return "phantom"; - } - - /** BITCOIN */ - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public bitcoinNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - throw new Error("Method not implemented."); - } - - public async btcSupportsSecureTransfer(): Promise { - return false; + async ethSignTypedData(msg: core.ETHSignTypedData): Promise { + const address = await this.ethGetAddress(); + return address ? eth.ethSignTypedData(msg, this.evmProvider, address) : null; } - public btcSupportsNativeShapeShift(): boolean { - return false; + public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { + if (!msg.signature.startsWith("0x")) msg.signature = `0x${msg.signature}`; + const digest = keccak256(core.buildMessage(msg.message)); + return recoverAddress(digest, msg.signature) === msg.address; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array { - // Phantom doesn't support BIP44 paths - throw new Error("Method not implemented."); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public btcNextAccountPath(_msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - // Phantom doesn't support BIP44 paths - throw new Error("Method not implemented."); - } + /** Bitcoin */ public async btcGetAddress(msg: core.BTCGetAddress): Promise { const value = await (async () => { @@ -398,38 +318,30 @@ export class PhantomHDWallet implements core.HDWallet, core.ETHWallet { const { coin } = msg; switch (coin) { case "Bitcoin": - return Btc.bitcoinSignTx(this, msg, this.bitcoinProvider); + return btc.bitcoinSignTx(this, msg, this.bitcoinProvider); default: return null; } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcSignMessage(msg: core.BTCSignMessage): Promise { - const address = await this.btcGetAddress({ coin: "Bitcoin" } as core.BTCGetAddress); - if (!address) throw new Error("Could not get Bitcoin address"); - const message = new TextEncoder().encode(msg.message); - - const { signature } = await this.bitcoinProvider.signMessage(address, message); - return { signature: core.toHexString(signature), address }; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { - throw new Error("Method not implemented."); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async btcSupportsScriptType(coin: string, scriptType?: core.BTCInputScriptType | undefined): Promise { - throw new Error("Method not implemented."); - } + const { coin } = msg; + switch (coin) { + case "Bitcoin": { + const address = await this.btcGetAddress({ coin } as core.BTCGetAddress); + if (!address) throw new Error(`Could not get ${coin} address`); + const message = new TextEncoder().encode(msg.message); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async btcSupportsCoin(coin: core.Coin): Promise { - return coin === "bitcoin"; + const { signature } = await this.bitcoinProvider.signMessage(address, message); + return { signature: core.toHexString(signature), address }; + } + default: + return null; + } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public btcIsSameAccount(msg: core.BTCAccountPath[]): boolean { - throw new Error("Method not implemented."); + public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { + const signature = Base64.fromByteArray(core.fromHexString(msg.signature)); + return bitcoinMsg.verify(msg.message, msg.address, signature); } } From 6b89f946af61820787a9d2d6afe9b34d4901ab8a Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:29:47 -0600 Subject: [PATCH 48/52] fix test --- packages/hdwallet-phantom/src/phantom.test.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/hdwallet-phantom/src/phantom.test.ts b/packages/hdwallet-phantom/src/phantom.test.ts index ed089165f..202df1f20 100644 --- a/packages/hdwallet-phantom/src/phantom.test.ts +++ b/packages/hdwallet-phantom/src/phantom.test.ts @@ -166,20 +166,14 @@ describe("PhantomHDWallet", () => { expect(wallet.evmProvider.request).toHaveBeenCalled(); expect(hash).toBe(null); }); - it("ethVerifyMessage returns null as its not implemented", async () => { - wallet.evmProvider = { - _metamask: { - isUnlocked: () => true, - }, - request: jest.fn().mockReturnValue("0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"), - }; + it("ethVerifyMessage returns true for a valid signature", async () => { expect( await wallet.ethVerifyMessage({ - address: "0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8", - message: "hello world", + address: "0x2068dD92B6690255553141Dfcf00dF308281f763", + message: "Hello World", signature: - "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b", + "0x61f1dda82e9c3800e960894396c9ce8164fd1526fccb136c71b88442405f7d09721725629915d10bc7cecfca2818fe76bc5816ed96a1b0cebee9b03b052980131b", }) - ).toEqual(null); + ).toEqual(true); }); }); From 7cb54cb86958e170586ad6fb3337af07a04cffff Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:54:15 -0600 Subject: [PATCH 49/52] cleanup bitcoin --- packages/hdwallet-core/package.json | 1 + packages/hdwallet-core/src/bitcoin.ts | 48 ++++++++++ packages/hdwallet-native/src/bitcoin.ts | 49 +--------- packages/hdwallet-phantom/package.json | 1 + packages/hdwallet-phantom/src/bitcoin.ts | 110 +++++++++-------------- packages/hdwallet-phantom/src/types.ts | 4 + 6 files changed, 97 insertions(+), 116 deletions(-) diff --git a/packages/hdwallet-core/package.json b/packages/hdwallet-core/package.json index 3e73592dc..08eb9e6f2 100644 --- a/packages/hdwallet-core/package.json +++ b/packages/hdwallet-core/package.json @@ -14,6 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { + "@shapeshiftoss/bitcoinjs-lib": "5.2.0-shapeshift.2", "@shapeshiftoss/proto-tx-builder": "^0.8.0", "eip-712": "^1.0.0", "ethers": "5.7.2", diff --git a/packages/hdwallet-core/src/bitcoin.ts b/packages/hdwallet-core/src/bitcoin.ts index 49c9971fb..5725d42a5 100644 --- a/packages/hdwallet-core/src/bitcoin.ts +++ b/packages/hdwallet-core/src/bitcoin.ts @@ -1,3 +1,4 @@ +import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib"; import * as ta from "type-assertions"; import { addressNListToBIP32, slip44ByCoin } from "./utils"; @@ -488,3 +489,50 @@ export function segwitNativeAccount(coin: Coin, slip44: number, accountIdx: numb addressNList: [0x80000000 + 84, 0x80000000 + slip44, 0x80000000 + accountIdx], }; } + +export function validateVoutOrdering(msg: BTCSignTxNative, tx: bitcoin.Transaction): boolean { + // From THORChain specification: + /* ignoreTx checks if we can already ignore a tx according to preset rules + + we expect array of "vout" for a BTC to have this format + OP_RETURN is mandatory only on inbound tx + vout:0 is our vault + vout:1 is any any change back to themselves + vout:2 is OP_RETURN (first 80 bytes) + vout:3 is OP_RETURN (next 80 bytes) + + Rules to ignore a tx are: + - vout:0 doesn't have coins (value) + - vout:0 doesn't have address + - count vouts > 4 + - count vouts with coins (value) > 2 + */ + + // Check that vout:0 contains the vault address + if (bitcoin.address.fromOutputScript(tx.outs[0].script) != msg.vaultAddress) { + console.error("Vout:0 does not contain vault address."); + return false; + } + + // TODO: Can we check and make sure vout:1 is our address? + + // Check and make sure vout:2 exists + if (tx.outs.length < 3) { + console.error("Not enough outputs found in transaction.", msg); + return false; + } + // Check and make sure vout:2 has OP_RETURN data + const opcode = bitcoin.script.decompile(tx.outs[2].script)?.[0]; + if (Object.keys(bitcoin.script.OPS).find((k) => bitcoin.script.OPS[k] === opcode) != "OP_RETURN") { + console.error("OP_RETURN output not found for transaction."); + return false; + } + + // Make sure vout:3 does not exist + if (tx.outs[3]) { + console.error("Illegal second op_return output found."); + return false; + } + + return true; +} diff --git a/packages/hdwallet-native/src/bitcoin.ts b/packages/hdwallet-native/src/bitcoin.ts index 182ea6bbd..7275d9af4 100644 --- a/packages/hdwallet-native/src/bitcoin.ts +++ b/packages/hdwallet-native/src/bitcoin.ts @@ -171,53 +171,6 @@ export function MixinNativeBTCWallet 4 - - count vouts with coins (value) > 2 - */ - - // Check that vout:0 contains the vault address - if (bitcoin.address.fromOutputScript(tx.outs[0].script) != msg.vaultAddress) { - console.error("Vout:0 does not contain vault address."); - return false; - } - - // TODO: Can we check and make sure vout:1 is our address? - - // Check and make sure vout:2 exists - if (tx.outs.length < 3) { - console.error("Not enough outputs found in transaction.", msg); - return false; - } - // Check and make sure vout:2 has OP_RETURN data - const opcode = bitcoin.script.decompile(tx.outs[2].script)?.[0]; - if (Object.keys(bitcoin.script.OPS).find((k) => bitcoin.script.OPS[k] === opcode) != "OP_RETURN") { - console.error("OP_RETURN output not found for transaction."); - return false; - } - - // Make sure vout:3 does not exist - if (tx.outs[3]) { - console.error("Illegal second op_return output found."); - return false; - } - - return true; - } - async buildInput(coin: core.Coin, input: core.BTCSignTxInputNative): Promise { return this.needsMnemonic(!!this.#masterKey, async () => { const { addressNList, amount, hex, scriptType } = input; @@ -355,7 +308,7 @@ export function MixinNativeBTCWallet { - const inputData: Omit & { - nonWitnessUtxo?: Buffer; - witnessUtxo?: { script: Buffer; value: number }; - hash: string | Buffer; - } = { - nonWitnessUtxo: input.hex ? Buffer.from(input.hex, "hex") : undefined, - hash: input.txid, - index: input.vout, - }; - - if (input.sequence !== undefined) { - inputData.sequence = input.sequence; - } +async function addInput(psbt: bitcoin.Psbt, input: core.BTCSignTxInput): Promise { + switch (input.scriptType) { + // Phantom supposedly supports more scriptTypes but in effect, doesn't (currently) + // https://github.com/orgs/phantom/discussions/173 + case BTCInputScriptType.SpendWitness: { + psbt.addInput({ + hash: input.txid, + index: input.vout, + nonWitnessUtxo: Buffer.from(input.hex, "hex"), + }); - if (input.scriptType) { - switch (input.scriptType) { - // Phantom supposedly supports more scriptTypes but in effect, doesn't (currently) - // https://github.com/orgs/phantom/discussions/173 - // case "p2pkh": - // inputData.nonWitnessUtxo = Buffer.from(input.hex, "hex"); - // break; - // case "p2sh-p2wpkh": - case BTCInputScriptType.SpendWitness: { - const inputAddress = await wallet.btcGetAddress({ addressNList: input.addressNList, coin, showDisplay: false }); - - if (!inputAddress) throw new Error("Could not get address from wallet"); - - inputData.witnessUtxo = { - script: bitcoin.address.toOutputScript(inputAddress, network), - value: parseInt(input.amount), - }; - break; - } - default: - throw new Error(`Unsupported script type: ${input.scriptType}`); + break; } + default: + throw new Error(`Unsupported script type: ${input.scriptType}`); } - - psbt.addInput(inputData); } async function addOutput( @@ -109,32 +81,28 @@ async function addOutput( output: core.BTCSignTxOutput, coin: string ): Promise { - if ("address" in output && output.address) { - psbt.addOutput({ - address: output.address, - value: parseInt(output.amount), - }); - } else if ("addressNList" in output && output.addressNList) { - const outputAddress = await wallet.btcGetAddress({ addressNList: output.addressNList, coin, showDisplay: false }); + if (!output.amount) throw new Error("Invalid output - missing amount."); - if (!outputAddress) throw new Error("Could not get address from wallet"); + const address = await (async () => { + if (output.address) return output.address; - psbt.addOutput({ - address: outputAddress, - value: parseInt(output.amount), - }); - } + if (output.addressNList) { + const outputAddress = await wallet.btcGetAddress({ addressNList: output.addressNList, coin, showDisplay: false }); + if (!outputAddress) throw new Error("Could not get address from wallet"); + return outputAddress; + } + })(); + + if (!address) throw new Error("Invalid output - no address"); + + psbt.addOutput({ address, value: parseInt(output.amount) }); } export async function bitcoinSignTx( wallet: core.BTCWallet, msg: core.BTCSignTx, - provider: any + provider: PhantomUtxoProvider ): Promise { - if (!msg.inputs.length || !msg.outputs.length) { - throw new Error("Invalid input: Empty inputs or outputs"); - } - const network = getNetwork(msg.coin); const psbt = new bitcoin.Psbt({ network }); @@ -145,7 +113,7 @@ export async function bitcoinSignTx( } for (const input of msg.inputs) { - await addInput(wallet, psbt, input, msg.coin, network); + await addInput(psbt, input); } for (const output of msg.outputs) { @@ -155,10 +123,9 @@ export async function bitcoinSignTx( if (msg.opReturnData) { const data = Buffer.from(msg.opReturnData, "utf-8"); const embed = bitcoin.payments.embed({ data: [data] }); - psbt.addOutput({ - script: embed.output!, - value: 0, - }); + const script = embed.output; + if (!script) throw new Error("unable to build OP_RETURN script"); + psbt.addOutput({ script, value: 0 }); } const inputsToSign = await Promise.all( @@ -169,6 +136,8 @@ export async function bitcoinSignTx( showDisplay: false, }); + if (!address) throw new Error("Could not get address from wallet"); + return { address, signingIndexes: [index], @@ -178,12 +147,17 @@ export async function bitcoinSignTx( ); const signedPsbtHex = await provider.signPSBT(fromHexString(psbt.toHex()), { inputsToSign }); - - const signedPsbt = bitcoin.Psbt.fromHex(signedPsbtHex, { network }); + const signedPsbt = bitcoin.Psbt.fromBuffer(Buffer.from(signedPsbtHex), { network }); signedPsbt.finalizeAllInputs(); + const tx = signedPsbt.extractTransaction(); + // If this is a THORChain transaction, validate the vout ordering + if (msg.vaultAddress && !core.validateVoutOrdering(msg, tx)) { + throw new Error("Improper vout ordering for BTC Thorchain transaction"); + } + const signatures = signedPsbt.data.inputs.map((input) => input.partialSig ? input.partialSig[0].signature.toString("hex") : "" ); diff --git a/packages/hdwallet-phantom/src/types.ts b/packages/hdwallet-phantom/src/types.ts index b823dbc51..ceaa936f9 100644 --- a/packages/hdwallet-phantom/src/types.ts +++ b/packages/hdwallet-phantom/src/types.ts @@ -16,4 +16,8 @@ export type PhantomUtxoProvider = providers.ExternalProvider & { ) => Promise<{ signature: Uint8Array; }>; + signPSBT( + psbt: Uint8Array, + options: { inputsToSign: { sigHash?: number | undefined; address: string; signingIndexes: number[] }[] } + ): Promise; }; From 1107098f0ac7fa609cde99b57cb7bce18d405652 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:34:47 -0600 Subject: [PATCH 50/52] fix test take 2 --- packages/hdwallet-phantom/src/adapter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/hdwallet-phantom/src/adapter.ts b/packages/hdwallet-phantom/src/adapter.ts index 4f9d75fe9..1a65a5480 100644 --- a/packages/hdwallet-phantom/src/adapter.ts +++ b/packages/hdwallet-phantom/src/adapter.ts @@ -5,7 +5,6 @@ import { PhantomEvmProvider, PhantomUtxoProvider } from "./types"; declare global { interface Window { - ethereum?: PhantomEvmProvider; phantom?: { ethereum?: PhantomEvmProvider; bitcoin?: PhantomUtxoProvider; From e18b090c33c6f8dc920140a64707c9efaf9aca05 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:56:02 +0200 Subject: [PATCH 51/52] feat: cleanup tsconfig.json --- tsconfig.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index a48be222c..e92968f5c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,8 @@ // tsc is insanely drunk with internal resolutions currently, this at least silences it so we can ship things // the installed versions are still the same as before, but the bumped tsc version might have made it stricter "skipLibCheck": true, - "target": "ESNext", - "module": "ESNext", + "target": "ES2016", + "module": "commonjs", "lib": ["es2020", "dom", "es5"], "declaration": true, "declarationMap": true, @@ -45,7 +45,6 @@ { "path": "./packages/hdwallet-ledger-webusb" }, { "path": "./packages/hdwallet-metamask" }, { "path": "./packages/hdwallet-metamask-shapeshift-multichain" }, - { "path": "./packages/hdwallet-phantom" }, { "path": "./packages/hdwallet-coinbase" }, { "path": "./packages/hdwallet-native" }, { "path": "./packages/hdwallet-portis" }, From 1e04575f773046f7778b7d1965f139b80a9aa7fd Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:56:21 +0200 Subject: [PATCH 52/52] chore(release): publish 1.55.6 --- examples/sandbox/package.json | 40 +++++++++---------- integration/package.json | 20 +++++----- lerna.json | 2 +- packages/hdwallet-coinbase/package.json | 4 +- packages/hdwallet-core/package.json | 2 +- .../hdwallet-keepkey-chromeusb/package.json | 6 +-- .../hdwallet-keepkey-electron/package.json | 4 +- .../hdwallet-keepkey-nodehid/package.json | 4 +- .../hdwallet-keepkey-nodewebusb/package.json | 6 +-- packages/hdwallet-keepkey-tcp/package.json | 6 +-- packages/hdwallet-keepkey-webusb/package.json | 6 +-- packages/hdwallet-keepkey/package.json | 4 +- packages/hdwallet-keplr/package.json | 4 +- packages/hdwallet-ledger-webhid/package.json | 6 +-- packages/hdwallet-ledger-webusb/package.json | 6 +-- packages/hdwallet-ledger/package.json | 4 +- .../package.json | 4 +- packages/hdwallet-metamask/package.json | 4 +- packages/hdwallet-native-vault/package.json | 4 +- packages/hdwallet-native/package.json | 4 +- packages/hdwallet-phantom/package.json | 4 +- packages/hdwallet-portis/package.json | 4 +- packages/hdwallet-tallyho/package.json | 4 +- packages/hdwallet-trezor-connect/package.json | 6 +-- packages/hdwallet-trezor/package.json | 4 +- packages/hdwallet-walletconnect/package.json | 4 +- .../hdwallet-walletconnectV2/package.json | 4 +- packages/hdwallet-xdefi/package.json | 4 +- 28 files changed, 87 insertions(+), 87 deletions(-) diff --git a/examples/sandbox/package.json b/examples/sandbox/package.json index e46ebeef8..ba5d2143e 100644 --- a/examples/sandbox/package.json +++ b/examples/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-sandbox", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "private": true, "browserslist": "> 0.5%, last 2 versions, not dead", @@ -12,25 +12,25 @@ "dependencies": { "@esm2cjs/p-queue": "^7.3.0", "@metamask/eth-sig-util": "^7.0.0", - "@shapeshiftoss/hdwallet-coinbase": "1.55.5", - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey-tcp": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.5", - "@shapeshiftoss/hdwallet-keplr": "1.55.5", - "@shapeshiftoss/hdwallet-ledger": "1.55.5", - "@shapeshiftoss/hdwallet-ledger-webhid": "1.55.5", - "@shapeshiftoss/hdwallet-ledger-webusb": "1.55.5", - "@shapeshiftoss/hdwallet-metamask": "1.55.5", - "@shapeshiftoss/hdwallet-phantom": "1.55.5", - "@shapeshiftoss/hdwallet-native": "1.55.5", - "@shapeshiftoss/hdwallet-portis": "1.55.5", - "@shapeshiftoss/hdwallet-tallyho": "1.55.5", - "@shapeshiftoss/hdwallet-trezor": "1.55.5", - "@shapeshiftoss/hdwallet-trezor-connect": "1.55.5", - "@shapeshiftoss/hdwallet-walletconnect": "1.55.5", - "@shapeshiftoss/hdwallet-walletconnectv2": "1.55.5", - "@shapeshiftoss/hdwallet-xdefi": "1.55.5", + "@shapeshiftoss/hdwallet-coinbase": "1.55.6", + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey-tcp": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.6", + "@shapeshiftoss/hdwallet-keplr": "1.55.6", + "@shapeshiftoss/hdwallet-ledger": "1.55.6", + "@shapeshiftoss/hdwallet-ledger-webhid": "1.55.6", + "@shapeshiftoss/hdwallet-ledger-webusb": "1.55.6", + "@shapeshiftoss/hdwallet-metamask": "1.55.6", + "@shapeshiftoss/hdwallet-native": "1.55.6", + "@shapeshiftoss/hdwallet-phantom": "1.55.6", + "@shapeshiftoss/hdwallet-portis": "1.55.6", + "@shapeshiftoss/hdwallet-tallyho": "1.55.6", + "@shapeshiftoss/hdwallet-trezor": "1.55.6", + "@shapeshiftoss/hdwallet-trezor-connect": "1.55.6", + "@shapeshiftoss/hdwallet-walletconnect": "1.55.6", + "@shapeshiftoss/hdwallet-walletconnectv2": "1.55.6", + "@shapeshiftoss/hdwallet-xdefi": "1.55.6", "bip32": "^2.0.4", "eip-712": "^1.0.0", "jquery": "^3.7.1", diff --git a/integration/package.json b/integration/package.json index 8a964fe63..5e1b8b031 100644 --- a/integration/package.json +++ b/integration/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/integration", - "version": "1.55.5", + "version": "1.55.6", "main": "index.js", "license": "MIT", "private": true, @@ -10,15 +10,15 @@ "dev": "lerna run test --scope integration --parallel --include-filtered-dependencies" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey-nodewebusb": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey-tcp": "1.55.5", - "@shapeshiftoss/hdwallet-ledger": "1.55.5", - "@shapeshiftoss/hdwallet-native": "1.55.5", - "@shapeshiftoss/hdwallet-portis": "1.55.5", - "@shapeshiftoss/hdwallet-trezor": "1.55.5", - "@shapeshiftoss/hdwallet-xdefi": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey-nodewebusb": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey-tcp": "1.55.6", + "@shapeshiftoss/hdwallet-ledger": "1.55.6", + "@shapeshiftoss/hdwallet-native": "1.55.6", + "@shapeshiftoss/hdwallet-portis": "1.55.6", + "@shapeshiftoss/hdwallet-trezor": "1.55.6", + "@shapeshiftoss/hdwallet-xdefi": "1.55.6", "fast-json-stable-stringify": "^2.1.0", "msw": "^0.27.1", "whatwg-fetch": "^3.6.2" diff --git a/lerna.json b/lerna.json index d474d95b4..6b41bc3e9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "5.2.0", - "version": "1.55.5", + "version": "1.55.6", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/packages/hdwallet-coinbase/package.json b/packages/hdwallet-coinbase/package.json index f93eab7bd..baa8a2acb 100644 --- a/packages/hdwallet-coinbase/package.json +++ b/packages/hdwallet-coinbase/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-coinbase", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@coinbase/wallet-sdk": "^3.6.6", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "eth-rpc-errors": "^4.0.3", "lodash": "^4.17.21" }, diff --git a/packages/hdwallet-core/package.json b/packages/hdwallet-core/package.json index 08eb9e6f2..53c5a67e5 100644 --- a/packages/hdwallet-core/package.json +++ b/packages/hdwallet-core/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-core", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" diff --git a/packages/hdwallet-keepkey-chromeusb/package.json b/packages/hdwallet-keepkey-chromeusb/package.json index d7391cf82..52e79af52 100644 --- a/packages/hdwallet-keepkey-chromeusb/package.json +++ b/packages/hdwallet-keepkey-chromeusb/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey-chromeusb", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey": "1.55.5" + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6" } } diff --git a/packages/hdwallet-keepkey-electron/package.json b/packages/hdwallet-keepkey-electron/package.json index ac024a86a..27849cefe 100644 --- a/packages/hdwallet-keepkey-electron/package.json +++ b/packages/hdwallet-keepkey-electron/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey-electron", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-keepkey": "1.55.5", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6", "uuid": "^8.3.2" }, "peerDependencies": { diff --git a/packages/hdwallet-keepkey-nodehid/package.json b/packages/hdwallet-keepkey-nodehid/package.json index 3d3b638e4..9ed6ce3e4 100644 --- a/packages/hdwallet-keepkey-nodehid/package.json +++ b/packages/hdwallet-keepkey-nodehid/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey-nodehid", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-keepkey": "1.55.5" + "@shapeshiftoss/hdwallet-keepkey": "1.55.6" }, "peerDependencies": { "node-hid": "^2.1.1" diff --git a/packages/hdwallet-keepkey-nodewebusb/package.json b/packages/hdwallet-keepkey-nodewebusb/package.json index 5b662d7bd..25822cb38 100644 --- a/packages/hdwallet-keepkey-nodewebusb/package.json +++ b/packages/hdwallet-keepkey-nodewebusb/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey-nodewebusb", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey": "1.55.5" + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6" }, "peerDependencies": { "usb": "^2.3.1" diff --git a/packages/hdwallet-keepkey-tcp/package.json b/packages/hdwallet-keepkey-tcp/package.json index af259f49a..6e4d800f5 100644 --- a/packages/hdwallet-keepkey-tcp/package.json +++ b/packages/hdwallet-keepkey-tcp/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey-tcp", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6", "axios": "^0.21.1" } } diff --git a/packages/hdwallet-keepkey-webusb/package.json b/packages/hdwallet-keepkey-webusb/package.json index 3fc429e7d..7c08eccc4 100644 --- a/packages/hdwallet-keepkey-webusb/package.json +++ b/packages/hdwallet-keepkey-webusb/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey-webusb", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-keepkey": "1.55.5" + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-keepkey": "1.55.6" }, "devDependencies": { "@types/w3c-web-usb": "^1.0.4" diff --git a/packages/hdwallet-keepkey/package.json b/packages/hdwallet-keepkey/package.json index f9fbb8722..ea0ca354f 100644 --- a/packages/hdwallet-keepkey/package.json +++ b/packages/hdwallet-keepkey/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "@keepkey/device-protocol": "^7.12.2", "@metamask/eth-sig-util": "^7.0.0", "@shapeshiftoss/bitcoinjs-lib": "5.2.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "@shapeshiftoss/proto-tx-builder": "^0.8.0", "bignumber.js": "^9.0.1", "bnb-javascript-sdk-nobroadcast": "^2.16.14", diff --git a/packages/hdwallet-keplr/package.json b/packages/hdwallet-keplr/package.json index 5867ef595..88b4a7434 100644 --- a/packages/hdwallet-keplr/package.json +++ b/packages/hdwallet-keplr/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keplr", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@shapeshiftoss/caip": "8.15.0", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "@shapeshiftoss/proto-tx-builder": "^0.8.0", "@shapeshiftoss/types": "3.1.3", "base64-js": "^1.5.1", diff --git a/packages/hdwallet-ledger-webhid/package.json b/packages/hdwallet-ledger-webhid/package.json index 9302ca9c3..b71ecd952 100644 --- a/packages/hdwallet-ledger-webhid/package.json +++ b/packages/hdwallet-ledger-webhid/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-ledger-webhid", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -19,8 +19,8 @@ "@ledgerhq/hw-transport": "^6.31.2", "@ledgerhq/hw-transport-webhid": "^6.29.2", "@ledgerhq/live-common": "^21.8.2", - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-ledger": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-ledger": "1.55.6", "@types/w3c-web-hid": "^1.0.2" }, "devDependencies": { diff --git a/packages/hdwallet-ledger-webusb/package.json b/packages/hdwallet-ledger-webusb/package.json index 1bb60f3b0..cb39bdbba 100644 --- a/packages/hdwallet-ledger-webusb/package.json +++ b/packages/hdwallet-ledger-webusb/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-ledger-webusb", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -20,8 +20,8 @@ "@ledgerhq/hw-transport-webusb": "^6.29.2", "@ledgerhq/live-common": "^21.8.2", "@ledgerhq/logs": "^6.10.1", - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-ledger": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-ledger": "1.55.6", "@types/w3c-web-usb": "^1.0.4", "p-queue": "^7.4.1" }, diff --git a/packages/hdwallet-ledger/package.json b/packages/hdwallet-ledger/package.json index bd838a6b7..fd547d79c 100644 --- a/packages/hdwallet-ledger/package.json +++ b/packages/hdwallet-ledger/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-ledger", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ "@ethereumjs/tx": "^3.3.0", "@ledgerhq/hw-app-cosmos": "^6.29.1", "@shapeshiftoss/bitcoinjs-lib": "5.2.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "base64-js": "^1.5.1", "bchaddrjs": "^0.4.4", "bitcoinjs-message": "^2.0.0", diff --git a/packages/hdwallet-metamask-shapeshift-multichain/package.json b/packages/hdwallet-metamask-shapeshift-multichain/package.json index f16c724c3..66734796b 100644 --- a/packages/hdwallet-metamask-shapeshift-multichain/package.json +++ b/packages/hdwallet-metamask-shapeshift-multichain/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-shapeshift-multichain", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -17,7 +17,7 @@ "@metamask/detect-provider": "^1.2.0", "@metamask/onboarding": "^1.0.1", "@shapeshiftoss/common-api": "^9.3.0", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "@shapeshiftoss/metamask-snaps-adapter": "^1.0.10", "@shapeshiftoss/metamask-snaps-types": "^1.0.10", "eth-rpc-errors": "^4.0.3", diff --git a/packages/hdwallet-metamask/package.json b/packages/hdwallet-metamask/package.json index 5927fc4e3..528a3d837 100644 --- a/packages/hdwallet-metamask/package.json +++ b/packages/hdwallet-metamask/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-metamask", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -16,7 +16,7 @@ "dependencies": { "@metamask/detect-provider": "^1.2.0", "@metamask/onboarding": "^1.0.1", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "eth-rpc-errors": "^4.0.3", "lodash": "^4.17.21" }, diff --git a/packages/hdwallet-native-vault/package.json b/packages/hdwallet-native-vault/package.json index 1bec662a3..67fe8c531 100644 --- a/packages/hdwallet-native-vault/package.json +++ b/packages/hdwallet-native-vault/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-native-vault", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-native": "1.55.5", + "@shapeshiftoss/hdwallet-native": "1.55.6", "bip39": "^3.0.4", "hash-wasm": "^4.11.0", "idb-keyval": "^6.0.3", diff --git a/packages/hdwallet-native/package.json b/packages/hdwallet-native/package.json index 7043d1b29..72d8c5677 100644 --- a/packages/hdwallet-native/package.json +++ b/packages/hdwallet-native/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-native", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -16,7 +16,7 @@ "dependencies": { "@shapeshiftoss/bitcoinjs-lib": "5.2.0-shapeshift.2", "@shapeshiftoss/fiosdk": "1.2.1-shapeshift.6", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "@shapeshiftoss/proto-tx-builder": "^0.8.0", "@zxing/text-encoding": "^0.9.0", "bchaddrjs": "^0.4.9", diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json index ba5fb4fae..238194a75 100644 --- a/packages/hdwallet-phantom/package.json +++ b/packages/hdwallet-phantom/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-phantom", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@shapeshiftoss/bitcoinjs-lib": "5.2.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "base64-js": "^1.5.1", "bitcoinjs-message": "^2.0.0", "lodash": "^4.17.21" diff --git a/packages/hdwallet-portis/package.json b/packages/hdwallet-portis/package.json index 05429184e..08b422d30 100644 --- a/packages/hdwallet-portis/package.json +++ b/packages/hdwallet-portis/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-portis", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@portis/web3": "3.0.10", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "base64-js": "^1.5.1", "bip32": "^2.0.4", "bitcoinjs-lib": "^5.1.6", diff --git a/packages/hdwallet-tallyho/package.json b/packages/hdwallet-tallyho/package.json index 4218e5c5c..6f2383f1b 100644 --- a/packages/hdwallet-tallyho/package.json +++ b/packages/hdwallet-tallyho/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-tallyho", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "lodash": "^4.17.21", "tallyho-onboarding": "^1.0.2" }, diff --git a/packages/hdwallet-trezor-connect/package.json b/packages/hdwallet-trezor-connect/package.json index 8ce6ca263..bc7d772a6 100644 --- a/packages/hdwallet-trezor-connect/package.json +++ b/packages/hdwallet-trezor-connect/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-trezor-connect", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", - "@shapeshiftoss/hdwallet-trezor": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", + "@shapeshiftoss/hdwallet-trezor": "1.55.6", "@trezor/rollout": "^1.2.0", "trezor-connect": "^8.2.1" } diff --git a/packages/hdwallet-trezor/package.json b/packages/hdwallet-trezor/package.json index 868635e0c..004e3575e 100644 --- a/packages/hdwallet-trezor/package.json +++ b/packages/hdwallet-trezor/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-trezor", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -17,7 +17,7 @@ "dependencies": { "@ethereumjs/common": "^2.4.0", "@ethereumjs/tx": "^3.3.0", - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "base64-js": "^1.5.1", "lodash": "^4.17.21" }, diff --git a/packages/hdwallet-walletconnect/package.json b/packages/hdwallet-walletconnect/package.json index b8ce276e2..7e62e6dcf 100644 --- a/packages/hdwallet-walletconnect/package.json +++ b/packages/hdwallet-walletconnect/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-walletconnect", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "@walletconnect/qrcode-modal": "^1.7.8", "@walletconnect/web3-provider": "^1.7.8", "ethers": "^5.6.5" diff --git a/packages/hdwallet-walletconnectV2/package.json b/packages/hdwallet-walletconnectV2/package.json index 20622130f..ccc929665 100644 --- a/packages/hdwallet-walletconnectV2/package.json +++ b/packages/hdwallet-walletconnectV2/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-walletconnectv2", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "@walletconnect/ethereum-provider": "^2.10.1", "@walletconnect/modal": "^2.6.2", "ethers": "^5.6.5" diff --git a/packages/hdwallet-xdefi/package.json b/packages/hdwallet-xdefi/package.json index 57f24e1fe..c1e9b06d8 100644 --- a/packages/hdwallet-xdefi/package.json +++ b/packages/hdwallet-xdefi/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-xdefi", - "version": "1.55.5", + "version": "1.55.6", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.55.5", + "@shapeshiftoss/hdwallet-core": "1.55.6", "lodash": "^4.17.21" }, "devDependencies": {