import {z} from 'zod' export type MigrationPrepareRequest = {currency?: string | undefined, from: unknown, gas_jetton_master?: unknown | undefined, gas_payer?: ('self' | 'battery' | 'gasless') | undefined, public_key?: string | undefined, to: unknown}; export type CreateApiV2MigrationPrepareArgs = {body: MigrationPrepareRequest}; export type CreateApiV2MigrationPrepareOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const accountAddress = z.object({address: z.string(), icon: z.string().optional(), is_scam: z.boolean(), is_wallet: z.boolean(), name: z.string().optional()}); export const addExtensionAction = z.object({extension: z.string(), wallet: accountAddress}); export const currencyType = z.enum(["native", "extra_currency", "jetton", "fiat"]); export const trustType = z.enum(["whitelist", "graylist", "blacklist", "none"]); export const price = z.object({currency_type: currencyType, decimals: z.number().int(), image: z.string(), jetton: z.string().optional(), token_name: z.string(), value: z.string(), verification: trustType}); export const nftApprovedBy = z.array(z.enum(["getgems", "tonkeeper"])); export const imagePreview = z.object({resolution: z.string(), url: z.string()}); export const sale = z.object({address: z.string(), market: accountAddress, owner: accountAddress.optional(), price: price}); export const nftItem = z.object({address: z.string(), approved_by: nftApprovedBy, code_hash: z.string().optional(), collection: z.object({address: z.string(), description: z.string(), name: z.string()}).optional(), data_hash: z.string().optional(), dns: z.string().optional(), include_cnft: z.boolean().optional(), index: z.number().int(), metadata: z.record(z.string(), z.unknown()), owner: accountAddress.optional(), previews: z.array(imagePreview).optional(), sale: sale.optional(), trust: trustType, verified: z.boolean()}); export const auctionBidAction = z.object({amount: price, auction: accountAddress, auction_type: z.enum(["DNS.ton", "DNS.tg", "NUMBER.tg", "getgems"]), bidder: accountAddress, nft: nftItem.optional()}); export const buyXTRAction = z.object({amount: z.string(), recipient: accountAddress}); export const contractDeployAction = z.object({address: z.string(), interfaces: z.array(z.string())}); export const poolImplementationType = z.enum(["whales", "tf", "liquidTF", "ffvault"]); export const depositStakeAction = z.object({amount: z.number().int(), implementation: poolImplementationType, pool: accountAddress, stake_meta: price.optional(), staker: accountAddress}); export const protocol = z.object({image: z.string().optional(), name: z.string()}); export const depositTokenStakeAction = z.object({protocol: protocol, stake_meta: price.optional(), staker: accountAddress}); export const depositXTRAction = z.object({amount: z.string(), recipient: accountAddress}); export const domainRenewAction = z.object({contract_address: z.string(), domain: z.string(), renewer: accountAddress}); export const electionsDepositStakeAction = z.object({amount: z.number().int(), staker: accountAddress}); export const electionsRecoverStakeAction = z.object({amount: z.number().int(), staker: accountAddress}); export const ecPreview = z.object({decimals: z.number().int(), id: z.number().int(), image: z.string(), symbol: z.string()}); export const encryptedComment = z.object({cipher_text: z.string(), encryption_type: z.string()}); export const extraCurrencyTransferAction = z.object({amount: z.string(), comment: z.string().optional(), currency: ecPreview, encrypted_comment: encryptedComment.optional(), recipient: accountAddress, sender: accountAddress}); export const scaledUI = z.object({denominator: z.string(), numerator: z.string()}); export const jettonVerificationType = z.enum(["whitelist", "graylist", "blacklist", "none"]); export const jettonPreview = z.object({address: z.string(), custom_payload_api_uri: z.string().optional(), decimals: z.number().int(), description: z.string().optional(), image: z.string(), name: z.string(), scaled_ui: scaledUI.optional(), score: z.number().int(), symbol: z.string(), verification: jettonVerificationType}); export const refund = z.object({origin: z.string(), type: z.enum(["DNS.ton", "DNS.tg", "GetGems"])}); export const flawedJettonTransferAction = z.object({comment: z.string().optional(), encrypted_comment: encryptedComment.optional(), jetton: jettonPreview, received_amount: z.string(), recipient: accountAddress.optional(), recipients_wallet: z.string(), refund: refund.optional(), sender: accountAddress.optional(), senders_wallet: z.string(), sent_amount: z.string()}); export const gasRelayAction = z.object({amount: z.number().int(), relayer: accountAddress, target: accountAddress}); export const jettonBurnAction = z.object({amount: z.string(), jetton: jettonPreview, sender: accountAddress, senders_wallet: z.string()}); export const jettonMintAction = z.object({amount: z.string(), jetton: jettonPreview, recipient: accountAddress, recipients_wallet: z.string()}); export const jettonSwapAction = z.object({amount_in: z.string(), amount_out: z.string(), dex: z.string(), gram_in: z.number().int().optional(), gram_out: z.number().int().optional(), jetton_master_in: jettonPreview.optional(), jetton_master_out: jettonPreview.optional(), router: accountAddress, ton_in: z.number().int().optional(), ton_out: z.number().int().optional(), user_wallet: accountAddress}); export const jettonTransferAction = z.object({amount: z.string(), comment: z.string().optional(), encrypted_comment: encryptedComment.optional(), jetton: jettonPreview, recipient: accountAddress.optional(), recipients_wallet: z.string(), refund: refund.optional(), sender: accountAddress.optional(), senders_wallet: z.string()}); export const vaultDepositInfo = z.object({price: price, vault: z.string()}); export const liquidityDepositAction = z.object({from: accountAddress, protocol: protocol, tokens: z.array(vaultDepositInfo)}); export const nftItemTransferAction = z.object({comment: z.string().optional(), encrypted_comment: encryptedComment.optional(), nft: z.string(), payload: z.string().optional(), recipient: accountAddress.optional(), refund: refund.optional(), sender: accountAddress.optional()}); export const nftPurchaseAction = z.object({amount: price, auction_type: z.enum(["DNS.ton", "DNS.tg", "NUMBER.tg", "getgems"]), buyer: accountAddress, nft: nftItem, seller: accountAddress}); export const oraclePriceFeed = z.object({display_symbol: z.string(), id: z.string(), rate: z.number().optional()}); export const oracleRequestAction = z.object({price_feeds: z.array(oraclePriceFeed), requester: accountAddress, response_to: accountAddress}); export const metadata = z.object({decryption_key: z.string().optional(), encrypted_binary: z.string()}); export const purchaseAction = z.object({amount: price, destination: accountAddress, invoice_id: z.string(), metadata: metadata, source: accountAddress}); export const removeExtensionAction = z.object({extension: z.string(), wallet: accountAddress}); export const setSignatureAllowedAction = z.object({allowed: z.boolean(), wallet: accountAddress}); export const smartContractAction = z.object({contract: accountAddress, executor: accountAddress, gram_attached: z.number().int(), operation: z.string(), payload: z.string().optional(), refund: refund.optional(), ton_attached: z.number().int().optional()}); export const subscriptionAction = z.object({admin: accountAddress, amount: z.number().int().optional(), beneficiary: accountAddress, initial: z.boolean(), price: price, subscriber: accountAddress, subscription: z.string()}); export const tonTransferAction = z.object({amount: z.number().int(), comment: z.string().optional(), encrypted_comment: encryptedComment.optional(), recipient: accountAddress, refund: refund.optional(), sender: accountAddress}); export const unSubscriptionAction = z.object({admin: accountAddress, beneficiary: accountAddress, subscriber: accountAddress, subscription: z.string()}); export const withdrawStakeAction = z.object({amount: z.number().int(), implementation: poolImplementationType, pool: accountAddress, staker: accountAddress}); export const withdrawStakeRequestAction = z.object({amount: z.number().int().optional(), implementation: poolImplementationType, pool: accountAddress, stake_meta: price.optional(), staker: accountAddress}); export const withdrawTokenStakeRequestAction = z.object({protocol: protocol, stake_meta: price.optional(), staker: accountAddress}); export const withdrawXTRAction = z.object({amount: z.string(), user: accountAddress}); export const actionSimplePreview = z.object({accounts: z.array(accountAddress), action_image: z.string().optional(), description: z.string(), fiat_value: z.string().optional(), name: z.string(), value: z.string().optional(), value_image: z.string().optional()}); export const action = z.object({AddExtension: addExtensionAction.optional(), AuctionBid: auctionBidAction.optional(), BuyXTR: buyXTRAction.optional(), ContractDeploy: contractDeployAction.optional(), DepositStake: depositStakeAction.optional(), DepositTokenStake: depositTokenStakeAction.optional(), DepositXTR: depositXTRAction.optional(), DomainRenew: domainRenewAction.optional(), ElectionsDepositStake: electionsDepositStakeAction.optional(), ElectionsRecoverStake: electionsRecoverStakeAction.optional(), ExtraCurrencyTransfer: extraCurrencyTransferAction.optional(), FlawedJettonTransfer: flawedJettonTransferAction.optional(), GasRelay: gasRelayAction.optional(), JettonBurn: jettonBurnAction.optional(), JettonMint: jettonMintAction.optional(), JettonSwap: jettonSwapAction.optional(), JettonTransfer: jettonTransferAction.optional(), LiquidityDeposit: liquidityDepositAction.optional(), NftItemTransfer: nftItemTransferAction.optional(), NftPurchase: nftPurchaseAction.optional(), OracleRequest: oracleRequestAction.optional(), Purchase: purchaseAction.optional(), RemoveExtension: removeExtensionAction.optional(), SetSignatureAllowedAction: setSignatureAllowedAction.optional(), SmartContractExec: smartContractAction.optional(), Subscribe: subscriptionAction.optional(), TonTransfer: tonTransferAction.optional(), UnSubscribe: unSubscriptionAction.optional(), WithdrawStake: withdrawStakeAction.optional(), WithdrawStakeRequest: withdrawStakeRequestAction.optional(), WithdrawTokenStakeRequest: withdrawTokenStakeRequestAction.optional(), WithdrawXTR: withdrawXTRAction.optional(), base_transactions: z.array(z.string()), simple_preview: actionSimplePreview, status: z.enum(["ok", "failed"]), type: z.enum(["TonTransfer", "ExtraCurrencyTransfer", "ContractDeploy", "JettonTransfer", "FlawedJettonTransfer", "JettonBurn", "JettonMint", "NftItemTransfer", "Subscribe", "UnSubscribe", "AuctionBid", "NftPurchase", "DepositStake", "WithdrawStake", "WithdrawStakeRequest", "ElectionsDepositStake", "ElectionsRecoverStake", "JettonSwap", "SmartContractExec", "DomainRenew", "Purchase", "AddExtension", "RemoveExtension", "SetSignatureAllowedAction", "GasRelay", "DepositTokenStake", "WithdrawTokenStakeRequest", "LiquidityDeposit", "OracleRequest", "BuyXTR", "DepositXTR", "WithdrawXTR", "Unknown"])}); export const accountEvent = z.object({account: accountAddress, actions: z.array(action), event_id: z.string(), ext_msg_hash: z.string().optional(), extra: z.number().int(), in_progress: z.boolean(), is_scam: z.boolean(), lt: z.number().int(), progress: z.number().gte(0).lte(1), timestamp: z.number().int()}); export const jettonQuantity = z.object({jetton: jettonPreview, quantity: z.string(), wallet_address: accountAddress}); export const risk = z.object({gram: z.number().int(), jettons: z.array(jettonQuantity), nfts: z.array(nftItem), ton: z.number().int().optional(), total_equivalent: z.number().optional(), transfer_all_remaining_balance: z.boolean()}); export const actionPhase = z.object({fwd_fees: z.number().int(), result_code: z.number().int(), result_code_description: z.string().optional(), skipped_actions: z.number().int(), success: z.boolean(), total_actions: z.number().int(), total_fees: z.number().int()}); export const bouncePhaseType = z.enum(["TrPhaseBounceNegfunds", "TrPhaseBounceNofunds", "TrPhaseBounceOk"]); export const computeSkipReason = z.enum(["cskip_no_state", "cskip_bad_state", "cskip_no_gas", "cskip_suspended"]); export const computePhase = z.object({exit_code: z.number().int().optional(), exit_code_description: z.string().optional(), gas_fees: z.number().int().optional(), gas_used: z.number().int().optional(), skip_reason: computeSkipReason.optional(), skipped: z.boolean(), success: z.boolean().optional(), vm_steps: z.number().int().optional()}); export const creditPhase = z.object({credit: z.number().int(), fees_collected: z.number().int()}); export const accountStatus = z.enum(["nonexist", "uninit", "active", "frozen"]); export const stateInit = z.object({boc: z.string(), interfaces: z.array(z.string())}); export const extraCurrency = z.object({amount: z.string(), preview: ecPreview}); export const message = z.object({bounce: z.boolean(), bounced: z.boolean(), created_at: z.number().int(), created_lt: z.number().int(), decoded_body: z.unknown(), decoded_op_name: z.string().optional(), destination: accountAddress.optional(), fwd_fee: z.number().int(), hash: z.string(), ihr_disabled: z.boolean(), ihr_fee: z.number().int(), import_fee: z.number().int(), init: stateInit.optional(), msg_type: z.enum(["int_msg", "ext_in_msg", "ext_out_msg"]), op_code: z.string().optional(), raw_body: z.string().optional(), source: accountAddress.optional(), value: z.number().int(), value_extra: z.array(extraCurrency).optional()}); export const accStatusChange = z.enum(["acst_unchanged", "acst_frozen", "acst_deleted"]); export const storagePhase = z.object({fees_collected: z.number().int(), fees_due: z.number().int().optional(), status_change: accStatusChange}); export const transactionType = z.enum(["TransOrd", "TransTickTock", "TransSplitPrepare", "TransSplitInstall", "TransMergePrepare", "TransMergeInstall", "TransStorage"]); export const transaction = z.object({aborted: z.boolean(), account: accountAddress, action_phase: actionPhase.optional(), block: z.string(), bounce_phase: bouncePhaseType.optional(), compute_phase: computePhase.optional(), credit_phase: creditPhase.optional(), destroyed: z.boolean(), end_balance: z.number().int(), end_status: accountStatus, hash: z.string(), in_msg: message.optional(), lt: z.number().int(), orig_status: accountStatus, out_msgs: z.array(message), prev_trans_hash: z.string().optional(), prev_trans_lt: z.number().int().optional(), raw: z.string(), state_update_new: z.string(), state_update_old: z.string(), storage_phase: storagePhase.optional(), success: z.boolean(), total_fees: z.number().int(), transaction_type: transactionType, utime: z.number().int()}); export const trace: z.ZodType = z.object({children: z.array(z.lazy(() => trace)).optional(), emulated: z.boolean().optional(), interfaces: z.array(z.string()), transaction: transaction}); export const messageConsequences = z.object({event: accountEvent, risk: risk, trace: trace}); export const migrationOutMessage = z.object({boc: z.string(), mode: z.number().int()}); export const migrationTransaction = z.object({boc: z.string(), commission: z.string().optional(), emulation: messageConsequences, gas_spent: z.number().int(), messages: z.array(migrationOutMessage), seqno: z.number().int(), sponsored: z.boolean().optional(), state_init: z.string().optional()}); export const migrationPrepareResponse = z.object({from: z.string(), to: z.string(), transactions: z.array(migrationTransaction), wallet_version: z.string()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Prepare ordered signable transactions that migrate every asset from `from` to `to`. * * POST /v2/migration/prepare */ export const createApiV2MigrationPrepare = async (args: CreateApiV2MigrationPrepareArgs, options?: CreateApiV2MigrationPrepareOptions) => { const {baseUrl = "https://tonapi.io", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/v2/migration/prepare${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(migrationPrepareResponse, await res.json()) };