diff --git a/modules/sdk-core/package.json b/modules/sdk-core/package.json index 4fd3b132a3..d11b5abe4c 100644 --- a/modules/sdk-core/package.json +++ b/modules/sdk-core/package.json @@ -69,7 +69,8 @@ "strip-hex-prefix": "^1.0.0", "superagent": "^9.0.1", "tweetnacl": "^1.0.3", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "zod": "^3.23.8" }, "devDependencies": { "@bitgo/sdk-opensslbytes": "^2.1.0", diff --git a/modules/sdk-core/src/bitgo/wallet/iWallet.ts b/modules/sdk-core/src/bitgo/wallet/iWallet.ts index 4c523e1a2f..73f0335af8 100644 --- a/modules/sdk-core/src/bitgo/wallet/iWallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/iWallet.ts @@ -34,6 +34,7 @@ import { SerializedNtilde } from '../../account-lib/mpc/tss/ecdsa/types'; import { IAddressBook } from '../address-book'; import { WalletUser, AddressQueryResult } from '@bitgo/public-types'; import { SubmitTransactionResponse } from '../inscriptionBuilder'; +import { z } from 'zod'; export interface MaximumSpendableOptions { minValue?: number | string; @@ -773,6 +774,66 @@ export interface SendManyOptions extends PrebuildAndSignTransactionOptions { custodianTransactionId?: string; } +const TokenTransferRecipientParamsSchema = z.object({ + tokenType: z.enum(['ERC721', 'ERC1155', 'ERC20', 'Digital Asset']), + tokenQuantity: z.string(), + tokenContractAddress: z.string().optional(), + tokenName: z.string().optional(), + tokenId: z.string().optional(), + decimalPlaces: z.number().optional(), +}); + +const SendManyRecipientSchema = z.object({ + address: z.string(), + amount: z.union([z.string(), z.number()]), + feeLimit: z.string().optional(), + data: z.union([z.string(), TokenTransferRecipientParamsSchema]).optional(), + tokenName: z.string().optional(), + tokenData: TokenTransferRecipientParamsSchema.optional(), +}); + +const EIP1559Schema = z.object({ + maxPriorityFeePerGas: z.string(), + maxFeePerGas: z.string(), +}); + +const MemoSchema = z.object({ + value: z.string(), + type: z.string(), +}); + +export const SendManyOptionsSchema = z + .object({ + recipients: z.array(SendManyRecipientSchema).optional(), + numBlocks: z.number().optional(), + feeRate: z.number().optional(), + feeMultiplier: z.number().optional(), + maxFeeRate: z.number().optional(), + minConfirms: z.number().optional(), + enforceMinConfirmsForChange: z.boolean().optional(), + targetWalletUnspents: z.number().optional(), + message: z.string().optional(), + minValue: z.union([z.number(), z.string()]).optional(), + maxValue: z.union([z.number(), z.string()]).optional(), + sequenceId: z.string().optional(), + lastLedgerSequence: z.number().optional(), + ledgerSequenceDelta: z.number().optional(), + gasPrice: z.number().optional(), + noSplitChange: z.boolean().optional(), + unspents: z.array(z.string()).optional(), + comment: z.string().optional(), + otp: z.string().optional(), + changeAddress: z.string().optional(), + allowExternalChangeAddress: z.boolean().optional(), + instant: z.boolean().optional(), + memo: MemoSchema.optional(), + transferId: z.number().optional(), + eip1559: EIP1559Schema.optional(), + gasLimit: z.number().optional(), + custodianTransactionId: z.string().optional(), + }) + .passthrough(); + export interface FetchCrossChainUTXOsOptions { sourceChain?: 'P' | 'C'; } diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index 3786c0c2e1..bb390588f9 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -99,6 +99,7 @@ import { RemovePolicyRuleOptions, RemoveUserOptions, SendManyOptions, + SendManyOptionsSchema, SendNFTOptions, SendNFTResult, SendOptions, @@ -2862,7 +2863,7 @@ export class Wallet implements IWallet { * @returns {*} */ async sendMany(params: SendManyOptions = {}): Promise { - common.validateParams(params, [], ['comment', 'otp']); + SendManyOptionsSchema.parse(params); debug('sendMany called'); const reqId = params.reqId || new RequestTracer(); params.reqId = reqId;