UNPKG

@coinbase/cdp-sdk

Version:

SDK for interacting with the Coinbase Developer Platform Wallet API

282 lines (255 loc) 9.34 kB
import { WaitForTransactionReceiptParameters } from "viem"; import { base, baseSepolia, mainnet, sepolia } from "viem/chains"; import { mapChainToNetwork } from "./chainToNetworkMapper.js"; import { isMethodSupportedOnNetwork } from "./networkCapabilities.js"; import { resolveViemClients } from "./resolveViemClients.js"; import { transferWithViem } from "../../actions/evm/transfer/transferWithViem.js"; import { Analytics } from "../../analytics.js"; import type { EvmServerAccount, NetworkScopedEvmServerAccount } from "./types.js"; import type { EvmFundOptions } from "../../actions/evm/fund/fund.js"; import type { EvmQuoteFundOptions } from "../../actions/evm/fund/quoteFund.js"; import type { ListTokenBalancesOptions } from "../../actions/evm/listTokenBalances.js"; import type { RequestFaucetOptions } from "../../actions/evm/requestFaucet.js"; import type { SendTransactionOptions, TransactionResult, } from "../../actions/evm/sendTransaction.js"; import type { UseSpendPermissionOptions } from "../../actions/evm/spend-permissions/types.js"; import type { AccountQuoteSwapOptions, AccountSwapOptions } from "../../actions/evm/swap/types.js"; import type { TransferOptions } from "../../actions/evm/transfer/types.js"; import type { WaitForFundOperationOptions } from "../../actions/waitForFundOperationReceipt.js"; import type { ListEvmTokenBalancesNetwork, SendEvmTransactionBodyNetwork, SpendPermissionNetwork, } from "../../openapi-client/index.js"; import type { Address, TransactionRequestEIP1559 } from "../../types/misc.js"; /** * Options for converting a pre-existing EvmAccount to a NetworkScopedEvmServerAccount. */ export type ToNetworkScopedEvmServerAccountOptions = { /** The EvmAccount that was previously created. */ account: EvmServerAccount; /** The network to scope the account to. */ network: string; }; /** * Creates a Network-scoped Server-managed EvmAccount instance from an existing EvmAccount. * Use this to interact with previously deployed EvmAccounts on a specific network. * * @param {ToNetworkScopedEvmServerAccountOptions} options - Configuration options. * @param {EvmServerAccount} options.account - The EvmServerAccount that was previously created. * @param {string} options.network - The network to scope the account to. * @returns {NetworkScopedEvmServerAccount} A configured NetworkScopedEvmServerAccount instance ready for signing. */ export async function toNetworkScopedEvmServerAccount<Network extends string>( options: ToNetworkScopedEvmServerAccountOptions & { network: Network }, ): Promise<NetworkScopedEvmServerAccount<Network>> { const { publicClient, walletClient, chain } = await resolveViemClients({ networkOrNodeUrl: options.network, account: options.account, }); /* * Determine the actual network name from the resolved chain * This handles cases where options.network is an RPC URL */ const resolvedNetworkName = mapChainToNetwork(chain) ?? options.network; const shouldUseApiForSends = chain.id === base.id || chain.id === baseSepolia.id || chain.id === mainnet.id || chain.id === sepolia.id; const account = { address: options.account.address as Address, network: options.network, signMessage: options.account.signMessage, sign: options.account.sign, signTransaction: options.account.signTransaction, signTypedData: options.account.signTypedData, name: options.account.name, type: "evm-server", policies: options.account.policies, sendTransaction: async (txOpts: Omit<SendTransactionOptions, "address" | "network">) => { if (shouldUseApiForSends) { return options.account.sendTransaction({ ...txOpts, network: mapChainToNetwork(chain) as SendEvmTransactionBodyNetwork, }); } else { Analytics.trackAction({ action: "send_transaction", accountType: "evm_server", properties: { network: options.network, managed: true, }, }); const hash = await walletClient.sendTransaction( txOpts.transaction as TransactionRequestEIP1559, ); return { transactionHash: hash }; } }, transfer: async (transferArgs: Omit<TransferOptions, "address" | "network">) => { if (shouldUseApiForSends) { return options.account.transfer({ ...transferArgs, network: mapChainToNetwork(chain) as SendEvmTransactionBodyNetwork, }); } else { Analytics.trackAction({ action: "transfer", accountType: "evm_server", properties: { network: options.network, managed: true, }, }); return transferWithViem(walletClient, account, transferArgs); } }, waitForTransactionReceipt: async ( waitOptions: WaitForTransactionReceiptParameters | TransactionResult, ) => { Analytics.trackAction({ action: "wait_for_transaction_receipt", accountType: "evm_server", properties: { managed: true, }, }); if ("transactionHash" in waitOptions) { return publicClient.waitForTransactionReceipt({ hash: waitOptions.transactionHash, }); } return publicClient.waitForTransactionReceipt(waitOptions); }, } as NetworkScopedEvmServerAccount<Network>; if (isMethodSupportedOnNetwork("listTokenBalances", resolvedNetworkName)) { Object.assign(account, { listTokenBalances: async ( listTokenBalancesOptions: Omit<ListTokenBalancesOptions, "address" | "network">, ) => { Analytics.trackAction({ action: "list_token_balances", accountType: "evm_server", properties: { managed: true, }, }); return options.account.listTokenBalances({ ...listTokenBalancesOptions, network: options.network as ListEvmTokenBalancesNetwork, }); }, }); } if (isMethodSupportedOnNetwork("requestFaucet", resolvedNetworkName)) { Object.assign(account, { requestFaucet: async (faucetOptions: Omit<RequestFaucetOptions, "address" | "network">) => { Analytics.trackAction({ action: "request_faucet", accountType: "evm_server", properties: { managed: true, }, }); return options.account.requestFaucet({ ...faucetOptions, network: chain.id === baseSepolia.id ? "base-sepolia" : "ethereum-sepolia", }); }, }); } if (isMethodSupportedOnNetwork("quoteFund", resolvedNetworkName)) { Object.assign(account, { quoteFund: async (quoteFundOptions: Omit<EvmQuoteFundOptions, "address">) => { Analytics.trackAction({ action: "quote_fund", accountType: "evm_server", properties: { managed: true, }, }); return options.account.quoteFund({ ...quoteFundOptions, }); }, }); } if (isMethodSupportedOnNetwork("fund", resolvedNetworkName)) { Object.assign(account, { fund: async (fundOptions: Omit<EvmFundOptions, "address">) => { Analytics.trackAction({ action: "fund", accountType: "evm_server", properties: { managed: true, }, }); return options.account.fund({ ...fundOptions, }); }, waitForFundOperationReceipt: async (waitOptions: WaitForFundOperationOptions) => { Analytics.trackAction({ action: "wait_for_fund_operation_receipt", accountType: "evm_server", properties: { managed: true, }, }); return options.account.waitForFundOperationReceipt(waitOptions); }, }); } if (isMethodSupportedOnNetwork("quoteSwap", resolvedNetworkName)) { Object.assign(account, { quoteSwap: async (quoteSwapOptions: AccountQuoteSwapOptions) => { Analytics.trackAction({ action: "quote_swap", accountType: "evm_server", properties: { managed: true, }, }); return options.account.quoteSwap(quoteSwapOptions); }, }); } if (isMethodSupportedOnNetwork("swap", resolvedNetworkName)) { Object.assign(account, { swap: async (swapOptions: AccountSwapOptions) => { Analytics.trackAction({ action: "swap", accountType: "evm_server", properties: { managed: true, }, }); return options.account.swap(swapOptions); }, }); } if (isMethodSupportedOnNetwork("useSpendPermission", resolvedNetworkName)) { Object.assign(account, { useSpendPermission: async ( spendPermissionOptions: Omit<UseSpendPermissionOptions, "network">, ) => { Analytics.trackAction({ action: "use_spend_permission", accountType: "evm_server", properties: { managed: true, }, }); return options.account.useSpendPermission({ ...spendPermissionOptions, network: options.network as SpendPermissionNetwork, }); }, }); } return account; }