diff --git a/apps/recovery-relay/components/WithdrawModal/CreateTransaction/index.tsx b/apps/recovery-relay/components/WithdrawModal/CreateTransaction/index.tsx index 08ab9852..ccf7990b 100644 --- a/apps/recovery-relay/components/WithdrawModal/CreateTransaction/index.tsx +++ b/apps/recovery-relay/components/WithdrawModal/CreateTransaction/index.tsx @@ -428,7 +428,7 @@ export const CreateTransaction = ({ asset, inboundRelayParams, setSignTxResponse theme.palette.error.main}> Insufficient balance for transaction - ) : prepareQuery.data?.insufficientFeeBalance === true && prepareQuery.data?.insufficientBalance !== true ? ( + ) : prepareQuery.data?.insufficientBalanceForTokenTransfer === true && prepareQuery.data?.insufficientBalance !== true ? ( theme.palette.error.main}> Insufficient fee asset balance for token transaction @@ -445,8 +445,8 @@ export const CreateTransaction = ({ asset, inboundRelayParams, setSignTxResponse prepareQuery.data?.insufficientBalance !== undefined && prepareQuery.data.insufficientBalance) || (prepareQuery.data && - prepareQuery.data?.insufficientFeeBalance !== undefined && - prepareQuery.data.insufficientFeeBalance) + prepareQuery.data?.insufficientBalanceForTokenTransfer !== undefined && + prepareQuery.data.insufficientBalanceForTokenTransfer) } > Prepare Transaction diff --git a/apps/recovery-relay/lib/wallets/Jetton/index.ts b/apps/recovery-relay/lib/wallets/Jetton/index.ts index 24895532..63773500 100644 --- a/apps/recovery-relay/lib/wallets/Jetton/index.ts +++ b/apps/recovery-relay/lib/wallets/Jetton/index.ts @@ -1,6 +1,6 @@ import { Ton as BaseTon } from '@fireblocks/wallet-derivation'; import { JettonMaster, TonClient, WalletContractV4 } from '@ton/ton'; -import { Address, beginCell, Cell, fromNano, toNano } from '@ton/core'; +import { Address, beginCell, Cell, toNano } from '@ton/core'; import { AccountData } from '../types'; import { defaultTonWalletV4R2code } from '../TON/tonParams'; import axios from 'axios'; @@ -28,7 +28,7 @@ export class Jetton extends BaseTon implements LateInitConnectedWallet { } public getLateInitLabel(): string { - return 'Ton client wallet'; + return 'Ton wallet client'; } public setRPCUrl(url: string): void { @@ -136,7 +136,7 @@ export class Jetton extends BaseTon implements LateInitConnectedWallet { feeRate, extraParams, insufficientBalance: jettonBalance <= 0, - insufficientFeeBalance: tonBalance < feeRate, + insufficientBalanceForTokenTransfer: tonBalance < feeRate, } as AccountData; return preperedData; diff --git a/apps/recovery-relay/lib/wallets/index.ts b/apps/recovery-relay/lib/wallets/index.ts index e29ad57e..4d13eedf 100644 --- a/apps/recovery-relay/lib/wallets/index.ts +++ b/apps/recovery-relay/lib/wallets/index.ts @@ -1,3 +1,4 @@ +import { getAllJettons } from '@fireblocks/asset-config'; import { Cardano } from './ADA'; import { Cosmos } from './ATOM'; import { Bitcoin, BitcoinCash, BitcoinSV, DASH, DogeCoin, LiteCoin, ZCash } from './BTCBased'; @@ -40,6 +41,19 @@ import { Ton } from './TON'; import { Jetton } from './Jetton'; export { ConnectedWallet } from './ConnectedWallet'; +const fillJettons = () => { + const jettonsList = getAllJettons(); + const jettons = jettonsList.reduce( + (prev, curr) => ({ + ...prev, + [curr]: Jetton, + }), + {}, + ) as any; + Object.keys(jettons).forEach((key) => (jettons[key] === undefined ? delete jettons[key] : {})); + return jettons; +}; + export const WalletClasses = { ALGO: Algorand, ALGO_TEST: Algorand, @@ -122,9 +136,7 @@ export const WalletClasses = { CELESTIA_TEST: Celestia, TON: Ton, TON_TEST: Ton, - USDT_TON: Jetton, - NOTCOIN_TON: Jetton, - DOGS_TON: Jetton, + ...fillJettons(), } as const; type WalletClass = (typeof WalletClasses)[keyof typeof WalletClasses]; diff --git a/apps/recovery-relay/lib/wallets/types.ts b/apps/recovery-relay/lib/wallets/types.ts index 2afd0cef..9ada6b11 100644 --- a/apps/recovery-relay/lib/wallets/types.ts +++ b/apps/recovery-relay/lib/wallets/types.ts @@ -28,7 +28,7 @@ export type AccountData = { extraParams?: Map; endpoint?: string; insufficientBalance?: boolean; - insufficientFeeBalance?: boolean; + insufficientBalanceForTokenTransfer?: boolean; }; export type TxPayload = { diff --git a/apps/recovery-utility/renderer/components/Modals/WithdrawModal/SignTransaction/index.tsx b/apps/recovery-utility/renderer/components/Modals/WithdrawModal/SignTransaction/index.tsx index cd2e1077..0c70b7a0 100644 --- a/apps/recovery-utility/renderer/components/Modals/WithdrawModal/SignTransaction/index.tsx +++ b/apps/recovery-utility/renderer/components/Modals/WithdrawModal/SignTransaction/index.tsx @@ -12,6 +12,7 @@ import { getLogger, sanatize, useWrappedState, + getDerivationMapKey, } from '@fireblocks/recovery-shared'; import { AssetConfig } from '@fireblocks/asset-config'; import { CallMade, CallReceived, LeakAdd, Toll } from '@mui/icons-material'; @@ -75,7 +76,7 @@ export const SignTransaction = ({ txId, account, asset, inboundRelayParams }: Pr const derivation = account.wallets .get(asset.id) - ?.derivations.get(`${inboundRelayParams?.unsignedTx.assetId}-${inboundRelayParams?.unsignedTx.from}`); + ?.derivations.get(getDerivationMapKey(inboundRelayParams?.unsignedTx.assetId, inboundRelayParams?.unsignedTx.from)); if (!derivation) { throw new Error('Derivation not found'); diff --git a/apps/recovery-utility/renderer/context/Workspace.tsx b/apps/recovery-utility/renderer/context/Workspace.tsx index f901a940..b33dbae8 100644 --- a/apps/recovery-utility/renderer/context/Workspace.tsx +++ b/apps/recovery-utility/renderer/context/Workspace.tsx @@ -68,8 +68,9 @@ export const WorkspaceProvider = ({ children }: Props) => { deriveWallet: (input) => { let transferableToken = false; const config = getAssetConfig(input.assetId); - if (config?.address && isTransferableToken(input.assetId)) { + if (isTransferableToken(input.assetId)) { transferableToken = true; + logger.info('Found a transferable token:', input.assetId); } const nativeAssetId = ( transferableToken ? input.assetId : config?.nativeAsset ?? input.assetId diff --git a/apps/recovery-utility/renderer/lib/wallets/Jetton/index.ts b/apps/recovery-utility/renderer/lib/wallets/Jetton/index.ts index 4328439d..71b22c9e 100644 --- a/apps/recovery-utility/renderer/lib/wallets/Jetton/index.ts +++ b/apps/recovery-utility/renderer/lib/wallets/Jetton/index.ts @@ -14,7 +14,7 @@ export class Jetton extends BaseTon implements SigningWallet { throw new Error('Jetton: Missing jetton parameters'); } - const JettonTransferOpcode = 0x0f8a7ea5; + const jettonTransferOpcode = 0x0f8a7ea5; const decimals = extraParams?.get('decimals'); const normalizingFactor = 10 ** decimals; const amountToWithdraw = amount * normalizingFactor; // amount is the wallet balance @@ -22,7 +22,7 @@ export class Jetton extends BaseTon implements SigningWallet { let internalMessageMemo = undefined; // create the tx payload const internalMessageBody = beginCell() - .storeUint(JettonTransferOpcode, 32) // opcode for jetton transfer + .storeUint(jettonTransferOpcode, 32) // opcode for jetton transfer .storeUint(0, 64) // query id .storeCoins(amountToWithdraw) // jetton balance .storeAddress(Address.parse(to)) // tx destination diff --git a/apps/recovery-utility/renderer/lib/wallets/index.ts b/apps/recovery-utility/renderer/lib/wallets/index.ts index 9ee11d4f..c33e0125 100644 --- a/apps/recovery-utility/renderer/lib/wallets/index.ts +++ b/apps/recovery-utility/renderer/lib/wallets/index.ts @@ -1,5 +1,5 @@ // import { Bitcoin } from './BTC'; -import { assets } from '@fireblocks/asset-config'; +import { assets, getAllJettons } from '@fireblocks/asset-config'; import { ERC20, ETC } from '@fireblocks/wallet-derivation'; import { Ripple } from './XRP'; import { Cosmos } from './ATOM'; @@ -41,14 +41,11 @@ const fillEVMs = () => { }; const fillJettons = () => { - const jettons = Object.keys(assets).reduce( - (o, assetId) => ({ - ...o, - [assets[assetId].id]: assets[assetId].protocol === 'TON' && assets[assetId].address ? Jetton : undefined, - }), - {}, - ) as any; - Object.keys(jettons).forEach((key) => (jettons[key] === undefined ? delete jettons[key] : {})); + const jettonsList = getAllJettons(); + const jettons: { [key: string]: any } = {}; + for (const jetton of jettonsList) { + jettons[jetton] = Jetton; + } return jettons; }; @@ -111,9 +108,6 @@ export const WalletClasses = { HBAR_TEST: Hedera, TON: Ton, TON_TEST: Ton, - // USDT_TON: Jetton, - // NOTCOIN_TON: Jetton, - // DOGS_TON: Jetton, ...fillJettons(), ...fillEVMs(), diff --git a/packages/asset-config/README.md b/packages/asset-config/README.md index af6ddfbe..883aee2e 100644 --- a/packages/asset-config/README.md +++ b/packages/asset-config/README.md @@ -50,6 +50,11 @@ For your convinience we have provided base methods for common types of chains: - `evm(baseExplorerUrl: string, rpcUrl?: string)` to create a basic EVM chain, simply provide the `baseExplorerUrl` (the URL of an explorer) and optionally `rpcUrl` as the URL of the RPC to communicate with - `btc(baseExplorerUrl: string, segwit: boolean)` to create a basic BTC chain (ZCash, LTC, etc are all considered such) simply provide the `baseExplorerUrl` (the URL of an explorer) and optionally `segwit` should be false, as only BTC is relevant for this field +### Add a new Jetton token + +To add support for withdrawals of a listed Jetton, make sure the token is listed in [globalAssets](/Users/tomerhorviz/Documents/recovery/packages/asset-config/data/globalAssets.ts) and in [patches](packages/asset-config/config/patches.ts). +The Jetton master contract address must be present in the 'globalAssets' list as the 'address' parameter. + ### Token or new Base Asset Support In case a token has bad data, alternatively a token is missing or you want to add a new base asset, it can be added by performing the following steps: diff --git a/packages/asset-config/assets.ts b/packages/asset-config/assets.ts index 584c44b1..9f377548 100644 --- a/packages/asset-config/assets.ts +++ b/packages/asset-config/assets.ts @@ -12,3 +12,13 @@ export const assets = globalAssets.reduce( }), {}, ); + +export function getAllJettons(): string[] { + const jettons = []; + for (const asset of globalAssets) { + if (asset.protocol === 'TON' && asset.address) { + jettons.push(asset.id); + } + } + return jettons; +} diff --git a/packages/asset-config/index.ts b/packages/asset-config/index.ts index 7c0e0a5a..06c48055 100644 --- a/packages/asset-config/index.ts +++ b/packages/asset-config/index.ts @@ -1,10 +1,10 @@ import { orderId, orderAssetById } from './config/sort'; import { isNativeAssetId, isDerivableAssetId, isTestnetAsset } from './util'; -import { assets } from './assets'; +import { assets, getAllJettons } from './assets'; export { getAssetConfig, getNativeAssetConfig, getDerivableAssetConfig, isExplorerUrl } from './util'; -export { assets }; +export { assets, getAllJettons }; export type * from './types';