diff --git a/examples/sandbox/package.json b/examples/sandbox/package.json index 30b9d0dd1..0748401ac 100644 --- a/examples/sandbox/package.json +++ b/examples/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-sandbox", - "version": "1.62.39", + "version": "1.62.40", "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.62.39", - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-gridplus": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey-tcp": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey-webusb": "1.62.39", - "@shapeshiftoss/hdwallet-keplr": "1.62.39", - "@shapeshiftoss/hdwallet-ledger": "1.62.39", - "@shapeshiftoss/hdwallet-ledger-webhid": "1.62.39", - "@shapeshiftoss/hdwallet-ledger-webusb": "1.62.39", - "@shapeshiftoss/hdwallet-metamask-multichain": "1.62.39", - "@shapeshiftoss/hdwallet-native": "1.62.39", - "@shapeshiftoss/hdwallet-phantom": "1.62.39", - "@shapeshiftoss/hdwallet-portis": "1.62.39", - "@shapeshiftoss/hdwallet-trezor": "1.62.39", - "@shapeshiftoss/hdwallet-trezor-connect": "1.62.39", - "@shapeshiftoss/hdwallet-vultisig": "1.62.39", - "@shapeshiftoss/hdwallet-walletconnect": "1.62.39", - "@shapeshiftoss/hdwallet-walletconnectv2": "1.62.39", + "@shapeshiftoss/hdwallet-coinbase": "^1.62.40", + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-gridplus": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey-tcp": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey-webusb": "^1.62.40", + "@shapeshiftoss/hdwallet-keplr": "^1.62.40", + "@shapeshiftoss/hdwallet-ledger": "^1.62.40", + "@shapeshiftoss/hdwallet-ledger-webhid": "^1.62.40", + "@shapeshiftoss/hdwallet-ledger-webusb": "^1.62.40", + "@shapeshiftoss/hdwallet-metamask-multichain": "^1.62.40", + "@shapeshiftoss/hdwallet-native": "^1.62.40", + "@shapeshiftoss/hdwallet-phantom": "^1.62.40", + "@shapeshiftoss/hdwallet-portis": "^1.62.40", + "@shapeshiftoss/hdwallet-trezor": "^1.62.40", + "@shapeshiftoss/hdwallet-trezor-connect": "^1.62.40", + "@shapeshiftoss/hdwallet-vultisig": "^1.62.40", + "@shapeshiftoss/hdwallet-walletconnect": "^1.62.40", + "@shapeshiftoss/hdwallet-walletconnectv2": "^1.62.40", "bip32": "^2.0.4", "eip-712": "^1.0.0", "jquery": "^3.7.1", diff --git a/integration/package.json b/integration/package.json index 8bdef14a2..3f1e604b5 100644 --- a/integration/package.json +++ b/integration/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/integration", - "version": "1.62.39", + "version": "1.62.40", "main": "index.js", "license": "MIT", "private": true, @@ -11,16 +11,16 @@ }, "dependencies": { "@bitcoinerlab/secp256k1": "^1.1.1", - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey-nodewebusb": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey-tcp": "1.62.39", - "@shapeshiftoss/hdwallet-ledger": "1.62.39", - "@shapeshiftoss/hdwallet-metamask-multichain": "1.62.39", - "@shapeshiftoss/hdwallet-native": "1.62.39", - "@shapeshiftoss/hdwallet-portis": "1.62.39", - "@shapeshiftoss/hdwallet-trezor": "1.62.39", - "@shapeshiftoss/hdwallet-vultisig": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey-nodewebusb": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey-tcp": "^1.62.40", + "@shapeshiftoss/hdwallet-ledger": "^1.62.40", + "@shapeshiftoss/hdwallet-metamask-multichain": "^1.62.40", + "@shapeshiftoss/hdwallet-native": "^1.62.40", + "@shapeshiftoss/hdwallet-portis": "^1.62.40", + "@shapeshiftoss/hdwallet-trezor": "^1.62.40", + "@shapeshiftoss/hdwallet-vultisig": "^1.62.40", "fast-json-stable-stringify": "^2.1.0", "msw": "^0.27.1", "whatwg-fetch": "^3.6.2" diff --git a/jest.config.js b/jest.config.js index d5f277a03..ebb978712 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,10 +7,13 @@ module.exports = { reporters: ["default", "jest-junit"], rootDir: "packages", testMatch: ["/**/*.test.ts"], - transformIgnorePatterns: ["node_modules/(?!(@shapeshiftoss/bitcoinjs-lib|valibot)/.*)"], + transformIgnorePatterns: [ + "node_modules/(?!(@shapeshiftoss/bitcoinjs-lib|valibot|@ton/ton|@ton/core|@ton/crypto|axios)/.*)", + ], moduleNameMapper: { "^@shapeshiftoss/hdwallet-(.*)": "/hdwallet-$1/src", "^valibot$": require.resolve("valibot"), + "^@ton/ton$": "/hdwallet-native/__mocks__/@ton/ton.js", }, globals: { "ts-jest": { diff --git a/lerna.json b/lerna.json index 584859f47..d926e24b4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "5.2.0", - "version": "1.62.39", + "version": "1.62.40", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/packages/hdwallet-coinbase/package.json b/packages/hdwallet-coinbase/package.json index 528e54c03..7015cafd7 100644 --- a/packages/hdwallet-coinbase/package.json +++ b/packages/hdwallet-coinbase/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-coinbase", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@coinbase/wallet-sdk": "^3.6.6", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "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 91c89a590..c6465b084 100644 --- a/packages/hdwallet-core/package.json +++ b/packages/hdwallet-core/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-core", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" diff --git a/packages/hdwallet-core/src/index.ts b/packages/hdwallet-core/src/index.ts index 3a08dddfc..ccd57f57f 100644 --- a/packages/hdwallet-core/src/index.ts +++ b/packages/hdwallet-core/src/index.ts @@ -20,6 +20,7 @@ export * from "./solana"; export * from "./starknet"; export * from "./sui"; export * from "./near"; +export * from "./ton"; export * from "./transport"; export * from "./tron"; export * from "./utils"; diff --git a/packages/hdwallet-core/src/ton.ts b/packages/hdwallet-core/src/ton.ts new file mode 100644 index 000000000..de3979b7f --- /dev/null +++ b/packages/hdwallet-core/src/ton.ts @@ -0,0 +1,101 @@ +import { addressNListToBIP32, slip44ByCoin } from "./utils"; +import { BIP32Path, HDWallet, HDWalletInfo, PathDescription } from "./wallet"; + +export interface TonGetAddress { + addressNList: BIP32Path; + showDisplay?: boolean; +} + +export interface TonRawMessage { + targetAddress: string; + sendAmount: string; + payload: string; + stateInit?: string; +} + +export interface TonSignTx { + addressNList: BIP32Path; + /** Raw message bytes to sign (BOC serialized) - used for simple transfers */ + message?: Uint8Array; + /** Raw messages from external protocols like Stonfi - used for complex swaps */ + rawMessages?: TonRawMessage[]; + /** Sequence number for the wallet */ + seqno?: number; + /** Transaction expiration timestamp */ + expireAt?: number; +} + +export interface TonSignedTx { + signature: string; + serialized: string; +} + +export interface TonGetAccountPaths { + accountIdx: number; +} + +export interface TonAccountPath { + addressNList: BIP32Path; +} + +export interface TonWalletInfo extends HDWalletInfo { + readonly _supportsTonInfo: boolean; + + /** + * Returns a list of bip32 paths for a given account index in preferred order + * from most to least preferred. + */ + tonGetAccountPaths(msg: TonGetAccountPaths): Array; + + /** + * Returns the "next" account path, if any. + */ + tonNextAccountPath(msg: TonAccountPath): TonAccountPath | undefined; +} + +export interface TonWallet extends TonWalletInfo, HDWallet { + readonly _supportsTon: boolean; + + tonGetAddress(msg: TonGetAddress): Promise; + tonSignTx(msg: TonSignTx): Promise; +} + +export function tonDescribePath(path: BIP32Path): PathDescription { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { + verbose: pathStr, + coin: "Ton", + isKnown: false, + }; + + // TON uses m/44'/607'/' - a 3-level path compatible with Trust Wallet. + // This differs from some wallets that use m/44'/607'/0' fixed path. + const slip44 = slip44ByCoin("Ton"); + if (slip44 === undefined) return unknown; + if (path.length != 3) return unknown; + if (path[0] != 0x80000000 + 44) return unknown; + if (path[1] != 0x80000000 + slip44) return unknown; + if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) return unknown; + + const index = path[2] & 0x7fffffff; + return { + verbose: `TON Account #${index}`, + accountIdx: index, + wholeAccount: true, + coin: "Ton", + isKnown: true, + }; +} + +// TON uses a 3-level hardened derivation path: m/44'/607'/' +// This follows the same pattern as Stellar (SEP-0005) since TON uses Ed25519 +// https://github.com/satoshilabs/slips/blob/master/slip-0044.md (607 = TON) +export function tonGetAccountPaths(msg: TonGetAccountPaths): Array { + const slip44 = slip44ByCoin("Ton"); + if (slip44 === undefined) return []; + return [ + { + addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx], + }, + ]; +} diff --git a/packages/hdwallet-core/src/utils.ts b/packages/hdwallet-core/src/utils.ts index a12ee3a95..5c4503f9d 100644 --- a/packages/hdwallet-core/src/utils.ts +++ b/packages/hdwallet-core/src/utils.ts @@ -161,6 +161,7 @@ export const slip44Table = Object.freeze({ Tron: 195, Sui: 784, Near: 397, + Ton: 607, // EVM chains all use the same SLIP44 Ethereum: 60, Avalanche: 60, diff --git a/packages/hdwallet-core/src/wallet.ts b/packages/hdwallet-core/src/wallet.ts index c55a629ba..51ddb8ada 100644 --- a/packages/hdwallet-core/src/wallet.ts +++ b/packages/hdwallet-core/src/wallet.ts @@ -17,6 +17,7 @@ import { StarknetWallet, StarknetWalletInfo } from "./starknet"; import { SuiWallet, SuiWalletInfo } from "./sui"; import { TerraWallet, TerraWalletInfo } from "./terra"; import { ThorchainWallet, ThorchainWalletInfo } from "./thorchain"; +import { TonWallet, TonWalletInfo } from "./ton"; import { Transport } from "./transport"; import { TronWallet, TronWalletInfo } from "./tron"; @@ -300,6 +301,14 @@ export function infoSui(info: HDWalletInfo): info is SuiWalletInfo { return isObject(info) && (info as any)._supportsSuiInfo; } +export function supportsTon(wallet: HDWallet): wallet is TonWallet { + return isObject(wallet) && (wallet as any)._supportsTon; +} + +export function infoTon(info: HDWalletInfo): info is TonWalletInfo { + return isObject(info) && (info as any)._supportsTonInfo; +} + export function supportsDebugLink(wallet: HDWallet): wallet is DebugLinkWallet { return isObject(wallet) && (wallet as any)._supportsDebugLink; } diff --git a/packages/hdwallet-gridplus/package.json b/packages/hdwallet-gridplus/package.json index 10ec02484..3c7734b3c 100644 --- a/packages/hdwallet-gridplus/package.json +++ b/packages/hdwallet-gridplus/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-gridplus", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "@ethereumjs/rlp": "5.0.2", "@ethereumjs/tx": "5.4.0", "@metamask/eth-sig-util": "^7.0.0", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "bech32": "^1.1.4", "bs58": "^5.0.0", "bs58check": "^4.0.0", diff --git a/packages/hdwallet-keepkey-chromeusb/package.json b/packages/hdwallet-keepkey-chromeusb/package.json index 02e04eee6..27ede4a1e 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey": "1.62.39" + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40" } } diff --git a/packages/hdwallet-keepkey-electron/package.json b/packages/hdwallet-keepkey-electron/package.json index 9cbb75e8e..1a8e85270 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-keepkey": "1.62.39", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40", "uuid": "^8.3.2" }, "peerDependencies": { diff --git a/packages/hdwallet-keepkey-nodehid/package.json b/packages/hdwallet-keepkey-nodehid/package.json index 0950fa2a9..63ef3b8d0 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,7 +14,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-keepkey": "1.62.39" + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40" }, "peerDependencies": { "node-hid": "^2.1.1" diff --git a/packages/hdwallet-keepkey-nodewebusb/package.json b/packages/hdwallet-keepkey-nodewebusb/package.json index 464f1b0ba..9b93dd537 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey": "1.62.39" + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40" }, "peerDependencies": { "usb": "^2.3.1" diff --git a/packages/hdwallet-keepkey-tcp/package.json b/packages/hdwallet-keepkey-tcp/package.json index c46af9868..9bfb62a5d 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40", "axios": "^0.21.1" } } diff --git a/packages/hdwallet-keepkey-webusb/package.json b/packages/hdwallet-keepkey-webusb/package.json index 7797b73d1..c60ab79bf 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-keepkey": "1.62.39" + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-keepkey": "^1.62.40" }, "devDependencies": { "@types/w3c-web-usb": "^1.0.4" diff --git a/packages/hdwallet-keepkey/package.json b/packages/hdwallet-keepkey/package.json index e1d84b91a..1ed1944d5 100644 --- a/packages/hdwallet-keepkey/package.json +++ b/packages/hdwallet-keepkey/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keepkey", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "@keepkey/device-protocol": "7.13.4", "@metamask/eth-sig-util": "^7.0.0", "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@shapeshiftoss/proto-tx-builder": "0.10.0", "bignumber.js": "^9.0.1", "bnb-javascript-sdk-nobroadcast": "2.16.15", diff --git a/packages/hdwallet-keplr/package.json b/packages/hdwallet-keplr/package.json index e0b0c2d2a..5ffb07ca2 100644 --- a/packages/hdwallet-keplr/package.json +++ b/packages/hdwallet-keplr/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-keplr", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -17,7 +17,7 @@ "@cosmjs/amino": "^0.28.13", "@cosmjs/stargate": "^0.28.13", "@shapeshiftoss/caip": "8.15.0", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@shapeshiftoss/proto-tx-builder": "0.10.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 eb433c572..6114bd467 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -18,8 +18,8 @@ "@ledgerhq/hw-app-eth": "7.0.0", "@ledgerhq/hw-transport": "6.31.13", "@ledgerhq/hw-transport-webhid": "6.30.6", - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-ledger": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-ledger": "^1.62.40", "@types/w3c-web-hid": "^1.0.2" } } diff --git a/packages/hdwallet-ledger-webusb/package.json b/packages/hdwallet-ledger-webusb/package.json index 708eeb49f..ac0a12c8e 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -20,8 +20,8 @@ "@ledgerhq/hw-app-solana": "7.6.0", "@ledgerhq/hw-transport": "6.31.13", "@ledgerhq/hw-transport-webusb": "6.29.13", - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-ledger": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-ledger": "^1.62.40", "@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 0b984d2fb..38238f571 100644 --- a/packages/hdwallet-ledger/package.json +++ b/packages/hdwallet-ledger/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-ledger", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -28,7 +28,7 @@ "@ledgerhq/logs": "6.13.0", "@mysten/ledgerjs-hw-app-sui": "^0.7.0", "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@solana/web3.js": "1.95.8", "base64-js": "^1.5.1", "bchaddrjs": "^0.4.4", diff --git a/packages/hdwallet-metamask-multichain/package.json b/packages/hdwallet-metamask-multichain/package.json index 96237f876..6fec39cfb 100644 --- a/packages/hdwallet-metamask-multichain/package.json +++ b/packages/hdwallet-metamask-multichain/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-metamask-multichain", - "version": "1.62.39", + "version": "1.62.40", "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.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@shapeshiftoss/metamask-snaps-adapter": "^1.0.12", "@shapeshiftoss/metamask-snaps-types": "^1.0.12", "eth-rpc-errors": "^4.0.3", diff --git a/packages/hdwallet-native-vault/package.json b/packages/hdwallet-native-vault/package.json index 3a0b1e5d3..8e26dd71a 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@bitcoinerlab/secp256k1": "^1.1.1", - "@shapeshiftoss/hdwallet-native": "1.62.39", + "@shapeshiftoss/hdwallet-native": "^1.62.40", "bip39": "^3.0.4", "hash-wasm": "^4.11.0", "idb-keyval": "^6.0.3", diff --git a/packages/hdwallet-native/__mocks__/@ton/ton.js b/packages/hdwallet-native/__mocks__/@ton/ton.js new file mode 100644 index 000000000..b80bde029 --- /dev/null +++ b/packages/hdwallet-native/__mocks__/@ton/ton.js @@ -0,0 +1,10 @@ +module.exports = { + WalletContractV4: { + create: jest.fn().mockReturnValue({ + address: { toString: jest.fn().mockReturnValue("mock-address") }, + init: { code: null, data: null }, + createTransfer: jest.fn(), + }), + }, + TonClient: jest.fn(), +}; diff --git a/packages/hdwallet-native/package.json b/packages/hdwallet-native/package.json index 68fb327f4..68ddba966 100644 --- a/packages/hdwallet-native/package.json +++ b/packages/hdwallet-native/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-native", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -18,8 +18,11 @@ "@noble/curves": "^1.4.0", "@scure/starknet": "^1.0.0", "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@shapeshiftoss/proto-tx-builder": "0.10.0", + "@ton/core": "^0.62.1", + "@ton/crypto": "^3.3.0", + "@ton/ton": "^16.1.0", "@zxing/text-encoding": "^0.9.0", "bchaddrjs": "^0.4.9", "bech32": "^1.1.4", diff --git a/packages/hdwallet-native/src/adapter.ts b/packages/hdwallet-native/src/adapter.ts index 9603997bc..48aea20a6 100644 --- a/packages/hdwallet-native/src/adapter.ts +++ b/packages/hdwallet-native/src/adapter.ts @@ -7,16 +7,19 @@ import * as native from "./native"; * NativeAdapter arguments * * Supports two initialization modes: - * 1. From mnemonic: Wallet derives all master keys (secp256k1, ed25519, stark) + * 1. From mnemonic: Wallet derives all master keys (secp256k1, ed25519, stark, ton) * 2. From master keys: Externally-provided master keys for each curve type * * MASTER KEY TYPES: * - secp256k1MasterKey: For Bitcoin, Ethereum, Tron, Cosmos chains - * - ed25519MasterKey: For Solana, Sui chains + * - ed25519MasterKey: For Solana, Sui chains (standard BIP44 Ed25519 derivation) * - starkMasterKey: For Starknet (uses STARK curve, separate from secp256k1) + * - tonMasterKey: For TON (uses custom PBKDF2 derivation with "TON default seed" salt) * - * Note: Starknet uses secp256k1 BIP32 derivation math but STARK curve for signing, - * requiring a separate master key type. See engines/default/stark.ts for details. + * Note: TON requires a special key derivation using PBKDF2 with 100,000 iterations + * and "TON default seed" salt for compatibility with TON ecosystem wallets + * (Tonkeeper, MyTonWallet, official TON SDKs). Standard BIP44 Ed25519 derivation + * is NOT compatible with TON wallets. */ export type NativeAdapterArgs = { deviceId: string; @@ -26,12 +29,14 @@ export type NativeAdapterArgs = { secp256k1MasterKey?: never; ed25519MasterKey?: never; starkMasterKey?: never; + tonMasterKey?: never; } | { mnemonic?: never; secp256k1MasterKey?: Isolation.Core.BIP32.Node; ed25519MasterKey?: Isolation.Core.Ed25519.Node; starkMasterKey?: Isolation.Core.Stark.Node; + tonMasterKey?: Isolation.Core.Ed25519.Node; } ); diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts index 4e77f6c43..66b69dd45 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts @@ -10,4 +10,5 @@ export { default as Solana } from "./solana"; export { default as Starknet } from "./starknet"; export { default as Sui } from "./sui"; export { default as Near } from "./near"; +export { default as Ton } from "./ton"; export { default as Tron } from "./tron"; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/ton.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/ton.ts new file mode 100644 index 000000000..8a1ba3597 --- /dev/null +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/ton.ts @@ -0,0 +1,216 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; +import { Address, beginCell, Cell, internal, MessageRelaxed, SendMode, storeMessage } from "@ton/core"; +import { WalletContractV4 } from "@ton/ton"; + +import { Isolation } from "../.."; + +const ED25519_PUBLIC_KEY_SIZE = 32; + +export interface TonTransactionParams { + from: string; + to: string; + value: string; + seqno: number; + expireAt: number; + memo?: string; + contractAddress?: string; + type?: "transfer" | "jetton_transfer"; +} + +export class TonAdapter { + protected readonly nodeAdapter: Isolation.Adapters.Ed25519; + + constructor(nodeAdapter: Isolation.Adapters.Ed25519) { + this.nodeAdapter = nodeAdapter; + } + + async getAddress(addressNList: core.BIP32Path): Promise { + const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToHardenedBIP32(addressNList)); + const publicKey = await nodeAdapter.getPublicKey(); + + if (publicKey.length !== ED25519_PUBLIC_KEY_SIZE) { + throw new Error(`Invalid Ed25519 public key size: ${publicKey.length}`); + } + + const wallet = WalletContractV4.create({ + workchain: 0, + publicKey: Buffer.from(publicKey), + }); + + return wallet.address.toString({ bounceable: false }); + } + + async getPublicKey(addressNList: core.BIP32Path): Promise { + const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToHardenedBIP32(addressNList)); + const publicKey = await nodeAdapter.getPublicKey(); + return Buffer.from(publicKey).toString("hex"); + } + + async createSignedTransferBoc(params: TonTransactionParams, addressNList: core.BIP32Path): Promise { + const derivedNodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToHardenedBIP32(addressNList)); + const publicKey = await derivedNodeAdapter.getPublicKey(); + + const wallet = WalletContractV4.create({ + workchain: 0, + publicKey: Buffer.from(publicKey), + }); + + const destination = Address.parse(params.to); + + let internalMessage: MessageRelaxed; + + if (params.type === "jetton_transfer" && params.contractAddress) { + const jettonWalletAddress = Address.parse(params.contractAddress); + const forwardPayload = params.memo + ? beginCell().storeUint(0, 32).storeStringTail(params.memo).endCell() + : beginCell().endCell(); + + const jettonTransferBody = beginCell() + .storeUint(0x0f8a7ea5, 32) + .storeUint(0, 64) + .storeCoins(BigInt(params.value)) + .storeAddress(destination) + .storeAddress(Address.parse(params.from)) + .storeBit(false) + .storeCoins(BigInt(1)) + .storeBit(true) + .storeRef(forwardPayload) + .endCell(); + + internalMessage = internal({ + to: jettonWalletAddress, + value: BigInt(100000000), + bounce: true, + body: jettonTransferBody, + }); + } else { + internalMessage = internal({ + to: destination, + value: BigInt(params.value), + bounce: false, + body: params.memo ? beginCell().storeUint(0, 32).storeStringTail(params.memo).endCell() : beginCell().endCell(), + }); + } + + const signer = async (message: Cell): Promise => { + const hash = message.hash(); + const signature = await derivedNodeAdapter.node.sign(hash); + return Buffer.from(signature); + }; + + const transfer = await wallet.createTransfer({ + seqno: params.seqno, + signer, + messages: [internalMessage], + sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, + timeout: params.expireAt, + }); + + const externalMessage = beginCell() + .store( + storeMessage({ + info: { + type: "external-in", + dest: wallet.address, + importFee: BigInt(0), + }, + init: params.seqno === 0 ? wallet.init : null, + body: transfer, + }) + ) + .endCell(); + + return externalMessage.toBoc().toString("base64"); + } + + async signTransaction(message: Uint8Array, addressNList: core.BIP32Path): Promise { + const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToHardenedBIP32(addressNList)); + const signature = await nodeAdapter.node.sign(message); + return Buffer.from(signature).toString("hex"); + } + + async createSignedRawTransferBoc( + rawMessages: core.TonRawMessage[], + seqno: number, + expireAt: number, + addressNList: core.BIP32Path + ): Promise { + const derivedNodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToHardenedBIP32(addressNList)); + const publicKey = await derivedNodeAdapter.getPublicKey(); + + const wallet = WalletContractV4.create({ + workchain: 0, + publicKey: Buffer.from(publicKey), + }); + + const internalMessages: MessageRelaxed[] = rawMessages.map((msg) => { + const destination = Address.parse(msg.targetAddress); + const value = BigInt(msg.sendAmount); + + let body: Cell; + if (msg.payload && msg.payload.length > 0) { + const payloadBuffer = Buffer.from(msg.payload, "hex"); + body = Cell.fromBoc(payloadBuffer)[0]; + } else { + body = beginCell().endCell(); + } + + let init: { code: Cell; data: Cell } | undefined; + if (msg.stateInit && msg.stateInit.length > 0) { + const stateInitBuffer = Buffer.from(msg.stateInit, "hex"); + const stateInitCell = Cell.fromBoc(stateInitBuffer)[0]; + const stateInitSlice = stateInitCell.beginParse(); + const hasCode = stateInitSlice.loadBit(); + const hasData = stateInitSlice.loadBit(); + if (hasCode && hasData) { + init = { + code: stateInitSlice.loadRef(), + data: stateInitSlice.loadRef(), + }; + } else { + console.warn("TON stateInit provided but missing code or data - init will be ignored"); + } + } + + return internal({ + to: destination, + value, + bounce: true, + body, + init, + }); + }); + + const signer = async (message: Cell): Promise => { + const hash = message.hash(); + const signature = await derivedNodeAdapter.node.sign(hash); + return Buffer.from(signature); + }; + + const transfer = await wallet.createTransfer({ + seqno, + signer, + messages: internalMessages, + sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, + timeout: expireAt, + }); + + const externalMessage = beginCell() + .store( + storeMessage({ + info: { + type: "external-in", + dest: wallet.address, + importFee: BigInt(0), + }, + init: seqno === 0 ? wallet.init : null, + body: transfer, + }) + ) + .endCell(); + + return externalMessage.toBoc().toString("base64"); + } +} + +export default TonAdapter; diff --git a/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts b/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts index db765accb..7fb69bef1 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts @@ -1,6 +1,11 @@ import { Revocable } from ".."; import * as BIP32 from "../bip32"; +export interface TonSeed extends Partial { + toTonMasterKey(): Promise; +} + export interface Mnemonic extends Partial { toSeed(passphrase?: string): Promise; + toTonSeed?(password?: string): Promise; } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts index 4d0e0479b..6ff262f5e 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts @@ -211,3 +211,28 @@ export class Seed extends Revocable(class {}) implements Core.BIP32.Seed { return out; } } + +export class TonSeed extends Revocable(class {}) { + readonly #seed: Buffer; + + protected constructor(seed: Uint8Array) { + super(); + this.#seed = safeBufferFrom(seed); + this.addRevoker(() => this.#seed.fill(0)); + } + + static async create(seed: Uint8Array): Promise { + const obj = new TonSeed(seed); + return revocable(obj, (x) => obj.addRevoker(x)); + } + + async toTonMasterKey(): Promise { + const hmacKey = safeBufferFrom(new TextEncoder().encode("ed25519 seed")); + const I = safeBufferFrom(bip32crypto.hmacSHA512(hmacKey, this.#seed)); + const IL = I.subarray(0, 32); + const IR = I.subarray(32, 64); + const out = await Ed25519.Node.create(IL, IR); + this.addRevoker(() => out.revoke?.()); + return out; + } +} diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts index f05512ae1..a31540107 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts @@ -4,7 +4,7 @@ import { createSHA512, pbkdf2 } from "hash-wasm"; import type { Seed as SeedType } from "../../core/bip32"; import type { Mnemonic as Bip39Mnemonic } from "../../core/bip39"; -import { Seed } from "./bip32"; +import { Seed, TonSeed } from "./bip32"; import { Revocable, revocable } from "./revocable"; export * from "../../core/bip39"; @@ -41,4 +41,22 @@ export class Mnemonic extends Revocable(class {}) implements Bip39Mnemonic { this.addRevoker(() => out.revoke?.()); return out; } + + async toTonSeed(password?: string): Promise { + const mnemonic = this.#mnemonic; + const salt = new TextEncoder().encode(`mnemonic${password ?? ""}`.normalize("NFKD")); + + const seed = await pbkdf2({ + password: mnemonic, + salt, + iterations: 2048, + hashLength: 64, + hashFunction: createSHA512(), + outputType: "binary", + }); + + const out = await TonSeed.create(Buffer.from(seed)); + this.addRevoker(() => out.revoke?.()); + return out; + } } diff --git a/packages/hdwallet-native/src/native.ts b/packages/hdwallet-native/src/native.ts index 8b2965ae9..1b833529f 100644 --- a/packages/hdwallet-native/src/native.ts +++ b/packages/hdwallet-native/src/native.ts @@ -21,6 +21,7 @@ import { MixinNativeStarknetWallet, MixinNativeStarknetWalletInfo } from "./star import { MixinNativeSuiWallet, MixinNativeSuiWalletInfo } from "./sui"; import { MixinNativeTerraWallet, MixinNativeTerraWalletInfo } from "./terra"; import { MixinNativeThorchainWallet, MixinNativeThorchainWalletInfo } from "./thorchain"; +import { MixinNativeTonWallet, MixinNativeTonWalletInfo } from "./ton"; import { MixinNativeTronWallet, MixinNativeTronWalletInfo } from "./tron"; export enum NativeEvents { @@ -41,12 +42,14 @@ type LoadDevice = Omit & { secp256k1MasterKey?: never; ed25519MasterKey?: never; starkMasterKey?: never; + tonMasterKey?: never; } | { mnemonic?: never; secp256k1MasterKey: Isolation.Core.BIP32.Node; ed25519MasterKey: Isolation.Core.Ed25519.Node; starkMasterKey: Isolation.Core.Stark.Node; + tonMasterKey?: Isolation.Core.Ed25519.Node; } ); @@ -134,14 +137,16 @@ class NativeHDWalletInfo MixinNativeSolanaWalletInfo( MixinNativeStarknetWalletInfo( MixinNativeTronWalletInfo( - MixinNativeSuiWalletInfo( - MixinNativeNearWalletInfo( - MixinNativeThorchainWalletInfo( - MixinNativeMayachainWalletInfo( - MixinNativeSecretWalletInfo( - MixinNativeTerraWalletInfo( - MixinNativeKavaWalletInfo( - MixinNativeArkeoWalletInfo(MixinNativeOsmosisWalletInfo(NativeHDWalletBase)) + MixinNativeTonWalletInfo( + MixinNativeSuiWalletInfo( + MixinNativeNearWalletInfo( + MixinNativeThorchainWalletInfo( + MixinNativeMayachainWalletInfo( + MixinNativeSecretWalletInfo( + MixinNativeTerraWalletInfo( + MixinNativeKavaWalletInfo( + MixinNativeArkeoWalletInfo(MixinNativeOsmosisWalletInfo(NativeHDWalletBase)) + ) ) ) ) @@ -165,6 +170,7 @@ class NativeHDWalletInfo core.SolanaWalletInfo, core.StarknetWalletInfo, core.TronWalletInfo, + core.TonWalletInfo, core.SuiWalletInfo, core.NearWalletInfo, core.ThorchainWalletInfo, @@ -231,6 +237,8 @@ class NativeHDWalletInfo case "tron": case "trx": return core.tronDescribePath(msg.path); + case "ton": + return core.tonDescribePath(msg.path); default: throw new Error("Unsupported path"); } @@ -245,13 +253,17 @@ export class NativeHDWallet MixinNativeSolanaWallet( MixinNativeStarknetWallet( MixinNativeTronWallet( - MixinNativeSuiWallet( - MixinNativeNearWallet( - MixinNativeThorchainWallet( - MixinNativeMayachainWallet( - MixinNativeSecretWallet( - MixinNativeTerraWallet( - MixinNativeKavaWallet(MixinNativeOsmosisWallet(MixinNativeArkeoWallet(NativeHDWalletInfo))) + MixinNativeTonWallet( + MixinNativeSuiWallet( + MixinNativeNearWallet( + MixinNativeThorchainWallet( + MixinNativeMayachainWallet( + MixinNativeSecretWallet( + MixinNativeTerraWallet( + MixinNativeKavaWallet( + MixinNativeOsmosisWallet(MixinNativeArkeoWallet(NativeHDWalletInfo)) + ) + ) ) ) ) @@ -274,6 +286,7 @@ export class NativeHDWallet core.SolanaWallet, core.StarknetWallet, core.TronWallet, + core.TonWallet, core.SuiWallet, core.NearWallet, core.ThorchainWallet, @@ -291,8 +304,16 @@ export class NativeHDWallet #secp256k1MasterKey: Promise | undefined = undefined; #ed25519MasterKey: Promise | undefined = undefined; #starkMasterKey: Promise | undefined = undefined; - - constructor({ mnemonic, deviceId, secp256k1MasterKey, ed25519MasterKey, starkMasterKey }: NativeAdapterArgs) { + #tonMasterKey: Promise | undefined = undefined; + + constructor({ + mnemonic, + deviceId, + secp256k1MasterKey, + ed25519MasterKey, + starkMasterKey, + tonMasterKey, + }: NativeAdapterArgs) { super(); if (mnemonic) { this.#secp256k1MasterKey = (async () => { @@ -313,10 +334,17 @@ export class NativeHDWallet const seed = await isolatedMnemonic.toSeed(); return await seed.toStarkMasterKey(); })(); + this.#tonMasterKey = (async () => { + const isolatedMnemonic = + typeof mnemonic === "string" ? await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic) : mnemonic; + const tonSeed = await isolatedMnemonic.toTonSeed!(); + return await tonSeed.toTonMasterKey(); + })(); } else { if (secp256k1MasterKey) this.#secp256k1MasterKey = Promise.resolve(secp256k1MasterKey); if (ed25519MasterKey) this.#ed25519MasterKey = Promise.resolve(ed25519MasterKey); if (starkMasterKey) this.#starkMasterKey = Promise.resolve(starkMasterKey); + if (tonMasterKey) this.#tonMasterKey = Promise.resolve(tonMasterKey); } this.#deviceId = deviceId; } @@ -405,7 +433,7 @@ export class NativeHDWallet const starkMasterKey = await this.#starkMasterKey!; try { - await Promise.all([ + const initPromises = [ super.btcInitializeWallet(secp256k1MasterKey), super.ethInitializeWallet(secp256k1MasterKey), super.cosmosInitializeWallet(secp256k1MasterKey), @@ -422,7 +450,14 @@ export class NativeHDWallet super.solanaInitializeWallet(ed25519MasterKey), super.suiInitializeWallet(ed25519MasterKey), super.nearInitializeWallet(ed25519MasterKey), - ]); + ]; + + if (this.#tonMasterKey) { + const tonMasterKey = await this.#tonMasterKey; + initPromises.push(super.tonInitializeWallet(tonMasterKey)); + } + + await Promise.all(initPromises); this.#initialized = true; } catch (e) { @@ -466,6 +501,7 @@ export class NativeHDWallet super.solanaWipe(); super.suiWipe(); super.nearWipe(); + super.tonWipe(); super.btcWipe(); super.ethWipe(); super.cosmosWipe(); @@ -557,6 +593,29 @@ export class NativeHDWallet })(msg?.mnemonic, msg?.starkMasterKey) ); + const tonMasterKeyResult = await (async (mnemonic, tonMasterKey) => { + if (tonMasterKey !== undefined) { + return tonMasterKey; + } else if (mnemonic !== undefined) { + const isolatedMnemonic = await (async () => { + if (isMnemonicInterface(mnemonic)) return mnemonic; + if (typeof mnemonic === "string" && bip39.validateMnemonic(mnemonic)) { + return await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic); + } + throw new Error("Required property [mnemonic] is invalid"); + })(); + const tonSeed = await isolatedMnemonic.toTonSeed!(); + tonSeed.addRevoker?.(() => isolatedMnemonic.revoke?.()); + const out = await tonSeed.toTonMasterKey(); + out.addRevoker?.(() => tonSeed.revoke?.()); + return out; + } + return undefined; + })(msg?.mnemonic, msg?.tonMasterKey); + if (tonMasterKeyResult) { + this.#tonMasterKey = Promise.resolve(tonMasterKeyResult); + } + if (typeof msg?.deviceId === "string") this.#deviceId = msg?.deviceId; this.#initialized = false; diff --git a/packages/hdwallet-native/src/ton.ts b/packages/hdwallet-native/src/ton.ts new file mode 100644 index 000000000..c395967b0 --- /dev/null +++ b/packages/hdwallet-native/src/ton.ts @@ -0,0 +1,94 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; + +import { Isolation } from "./crypto"; +import { TonAdapter, TonTransactionParams } from "./crypto/isolation/adapters/ton"; +import { NativeHDWalletBase } from "./native"; + +export function MixinNativeTonWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow + return class MixinNativeTonWalletInfo extends Base implements core.TonWalletInfo { + readonly _supportsTonInfo = true; + + tonGetAccountPaths(msg: core.TonGetAccountPaths): Array { + return core.tonGetAccountPaths(msg); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + tonNextAccountPath(_msg: core.TonAccountPath): core.TonAccountPath | undefined { + throw new Error("Method not implemented"); + } + }; +} + +export function MixinNativeTonWallet>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow + return class MixinNativeTonWallet extends Base { + readonly _supportsTon = true; + + tonAdapter: TonAdapter | undefined; + + async tonInitializeWallet(ed25519MasterKey: Isolation.Core.Ed25519.Node): Promise { + const nodeAdapter = new Isolation.Adapters.Ed25519(ed25519MasterKey); + this.tonAdapter = new TonAdapter(nodeAdapter); + } + + tonWipe() { + this.tonAdapter = undefined; + } + + async tonGetAddress(msg: core.TonGetAddress): Promise { + return this.needsMnemonic(!!this.tonAdapter, () => { + return this.tonAdapter!.getAddress(msg.addressNList); + }); + } + + async tonSignTx(msg: core.TonSignTx): Promise { + return this.needsMnemonic(!!this.tonAdapter, async () => { + if (msg.rawMessages && msg.rawMessages.length > 0) { + const seqno = msg.seqno ?? 0; + const expireAt = msg.expireAt ?? Math.floor(Date.now() / 1000) + 300; + + const bocBase64 = await this.tonAdapter!.createSignedRawTransferBoc( + msg.rawMessages, + seqno, + expireAt, + msg.addressNList + ); + + // signature is empty because TON embeds the signature inside the BOC (Bag of Cells). + // The serialized field contains the complete signed transaction ready for broadcast. + return { + signature: "", + serialized: bocBase64, + }; + } + + if (!msg.message) { + throw new Error("Either message or rawMessages must be provided"); + } + + const messageJson = new TextDecoder().decode(msg.message); + let txParams: TonTransactionParams; + + try { + txParams = JSON.parse(messageJson) as TonTransactionParams; + } catch (error) { + throw new Error( + `Failed to parse TON transaction message: ${ + error instanceof Error ? error.message : String(error) + }. Message: ${messageJson}` + ); + } + + const bocBase64 = await this.tonAdapter!.createSignedTransferBoc(txParams, msg.addressNList); + + // signature is empty because TON embeds the signature inside the BOC (Bag of Cells). + // The serialized field contains the complete signed transaction ready for broadcast. + return { + signature: "", + serialized: bocBase64, + }; + }); + } + }; +} diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json index d25bcb65f..70dbf1889 100644 --- a/packages/hdwallet-phantom/package.json +++ b/packages/hdwallet-phantom/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-phantom", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ }, "dependencies": { "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@solana/web3.js": "1.95.8", "base64-js": "^1.5.1", "bitcoinjs-message": "^2.0.0", diff --git a/packages/hdwallet-portis/package.json b/packages/hdwallet-portis/package.json index 0166e313a..8459e8802 100644 --- a/packages/hdwallet-portis/package.json +++ b/packages/hdwallet-portis/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-portis", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -16,7 +16,7 @@ "dependencies": { "@portis/web3": "3.0.10", "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "base64-js": "^1.5.1", "bip32": "^2.0.4", "bitcoinjs-message": "^2.1.0", diff --git a/packages/hdwallet-trezor-connect/package.json b/packages/hdwallet-trezor-connect/package.json index 72f0934ab..1a85e649f 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.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -14,8 +14,8 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", - "@shapeshiftoss/hdwallet-trezor": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", + "@shapeshiftoss/hdwallet-trezor": "^1.62.40", "@trezor/connect-web": "^9.6.4" } } diff --git a/packages/hdwallet-trezor/package.json b/packages/hdwallet-trezor/package.json index 4412965c5..b4362d7d4 100644 --- a/packages/hdwallet-trezor/package.json +++ b/packages/hdwallet-trezor/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-trezor", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -17,7 +17,7 @@ "dependencies": { "@ethereumjs/common": "3.2.0", "@ethereumjs/tx": "4.2.0", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "base64-js": "^1.5.1", "bchaddrjs": "^0.4.4", "lodash": "^4.17.21" diff --git a/packages/hdwallet-vultisig/package.json b/packages/hdwallet-vultisig/package.json index e4d2c2f8a..979260dbb 100644 --- a/packages/hdwallet-vultisig/package.json +++ b/packages/hdwallet-vultisig/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-vultisig", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -17,7 +17,7 @@ "@cosmjs/amino": "^0.28.13", "@cosmjs/stargate": "^0.28.13", "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@solana/web3.js": "1.95.8", "base64-js": "^1.5.1", "bitcoinjs-message": "^2.0.0", diff --git a/packages/hdwallet-walletconnect/package.json b/packages/hdwallet-walletconnect/package.json index 11f06750d..21d101921 100644 --- a/packages/hdwallet-walletconnect/package.json +++ b/packages/hdwallet-walletconnect/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-walletconnect", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@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 002d24032..1e316f482 100644 --- a/packages/hdwallet-walletconnectV2/package.json +++ b/packages/hdwallet-walletconnectV2/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/hdwallet-walletconnectv2", - "version": "1.62.39", + "version": "1.62.40", "license": "MIT", "publishConfig": { "access": "public" @@ -15,7 +15,7 @@ "prepublishOnly": "yarn clean && yarn build" }, "dependencies": { - "@shapeshiftoss/hdwallet-core": "1.62.39", + "@shapeshiftoss/hdwallet-core": "^1.62.40", "@walletconnect/ethereum-provider": "^2.20.2", "@walletconnect/modal": "^2.6.2", "ethers": "^5.6.5"