import {z} from 'zod' export type TypeId = string; export type ExternalAccountOwner = {kind: 'customer', customerId: TypeId} | {kind: 'counterparty', counterpartyId: TypeId}; export type ExternalAccountType = 'bankUs' | 'bankIban' | 'bankCanada' | 'card' | 'walletExternal'; export type BankAccountType = 'checking' | 'savings'; export type ExternalAccountBankUsRequest = {accountNumber: string, routingNumber: string, accountType?: BankAccountType | undefined, bankName?: string | undefined}; export type Address = {line1?: string | undefined, line2?: string | undefined, city?: string | undefined, state?: string | undefined, country?: string | undefined, zipCode?: string | undefined}; export type ExternalAccountBankIbanRequest = {iban: string, BIC: string, bankAddress?: Address | undefined, countryCode?: string | undefined}; export type ExternalAccountBankCanadaRequest = {institutionNumber: string, transitNumber: string, accountNumber: string, bankName?: string | undefined}; export type NetworkFamily = 'evm' | 'solana'; export type DestinationCustodian = 'ANCHORAGE_SG' | 'ANCHORAGE_US' | 'AQUANOW_CA' | 'B2C2_UK' | 'B2C2_US' | 'BITGO_SG' | 'BITGO_US' | 'BITSTAMP_US' | 'BVNK_US' | 'CIRCLE_FR' | 'CIRCLE_US' | 'CITIBANK_US' | 'COINBASE_US' | 'COINSMART_CA' | 'COPPER_CH' | 'COPPER_UK' | 'CUMBERLAND_DRW_LLC_US' | 'CUMBERLAND_SG' | 'EREBOR_BANK_US' | 'FALCONX_US' | 'FIDELITY_UK' | 'FIDELITY_US' | 'FIREBLOCKS_APAC' | 'FIREBLOCKS_US' | 'GALAXY_KY' | 'GEMINI_US' | 'KRAKEN_BVI' | 'KRAKEN_EU_IE' | 'KRAKEN_UK' | 'KRAKEN_US' | 'NUBANK_BR' | 'PAXOS_US' | 'RAMP_NETWORK_US' | 'ROBINHOOD_US' | 'WINTERMUTE_GB' | 'SELF_HOSTED' | 'OTHER'; export type ExternalAccountWalletExternalRequest = {blockchainAddress: string, networkFamily: NetworkFamily, custodian: DestinationCustodian, otherCustodian?: string | undefined}; export type ExternalAccountCreateRequest = {owner: ExternalAccountOwner, type: ExternalAccountType, label?: string | undefined, metadata?: {[key: string]: string} | undefined, bankUs?: ExternalAccountBankUsRequest | undefined, bankIban?: ExternalAccountBankIbanRequest | undefined, bankCanada?: ExternalAccountBankCanadaRequest | undefined, walletExternal?: ExternalAccountWalletExternalRequest | undefined}; export type CreateApiExternalAccountsArgs = {'Idempotency-Key': string, body: ExternalAccountCreateRequest}; export type CreateApiExternalAccountsOptions = {baseUrl?: string | undefined, BearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const typeId = z.string(); export const externalAccountOwner = z.discriminatedUnion("kind", [z.object({kind: z.literal("customer"), customerId: typeId}), z.object({kind: z.literal("counterparty"), counterpartyId: typeId})]); export const externalAccountType = z.enum(["bankUs", "bankIban", "bankCanada", "card", "walletExternal"]); export const externalAccountCategory = z.enum(["fiatAccount", "crypto"]); export const externalAccountStatus = z.enum(["active", "pending", "rejected", "invalid", "deleted", "failed"]); export const externalAccountFailureReason = z.enum(["ereborRejected", "cardProviderRejected", "providerAccountMissing", "cardLimitReached", "cardInUse", "provisioningTimeout", "systemError"]); export const providerRejectionDetail = z.object({provider: z.string().optional(), code: z.string().optional(), message: z.string().optional(), details: z.array(z.string()).optional(), providerRequestId: z.string().optional()}); export const bankAccountType = z.enum(["checking", "savings"]); export const externalAccountBankUsResponse = z.object({accountNumberLast4: z.string(), routingNumber: z.string(), accountType: bankAccountType.optional(), bankName: z.string().optional()}); export const address = z.object({line1: z.string().optional(), line2: z.string().optional(), city: z.string().optional(), state: z.string().optional(), country: z.string().optional(), zipCode: z.string().optional()}); export const externalAccountBankIbanResponse = z.object({ibanLast4: z.string(), BIC: z.string(), bankAddress: address.optional(), countryCode: z.string()}); export const externalAccountBankCanadaResponse = z.object({institutionNumber: z.string(), transitNumber: z.string(), accountNumberLast4: z.string(), bankName: z.string().optional()}); export const networkFamily = z.enum(["evm", "solana"]); export const destinationCustodian = z.enum(["ANCHORAGE_SG", "ANCHORAGE_US", "AQUANOW_CA", "B2C2_UK", "B2C2_US", "BITGO_SG", "BITGO_US", "BITSTAMP_US", "BVNK_US", "CIRCLE_FR", "CIRCLE_US", "CITIBANK_US", "COINBASE_US", "COINSMART_CA", "COPPER_CH", "COPPER_UK", "CUMBERLAND_DRW_LLC_US", "CUMBERLAND_SG", "EREBOR_BANK_US", "FALCONX_US", "FIDELITY_UK", "FIDELITY_US", "FIREBLOCKS_APAC", "FIREBLOCKS_US", "GALAXY_KY", "GEMINI_US", "KRAKEN_BVI", "KRAKEN_EU_IE", "KRAKEN_UK", "KRAKEN_US", "NUBANK_BR", "PAXOS_US", "RAMP_NETWORK_US", "ROBINHOOD_US", "WINTERMUTE_GB", "SELF_HOSTED", "OTHER"]); export const supportedDestination = z.object({asset: z.string(), network: z.string()}); export const externalAccountWalletExternalResponse = z.object({blockchainAddress: z.string(), networkFamily: networkFamily, custodian: destinationCustodian, otherCustodian: z.string().optional(), supportedDestinations: z.array(supportedDestination).optional()}); export const cardBrand = z.enum(["visa", "mastercard", "amex", "discover"]); export const cardType = z.enum(["debit", "credit", "prepaid"]); export const billingAddressSource = z.enum(["provided", "customerDefault"]); export const externalAccountCardResponse = z.object({cardNumberLast4: z.string().optional(), cardProvider: cardBrand.optional(), cardType: cardType.optional(), expiryMonth: z.number().int().optional(), expiryYear: z.number().int().optional(), billingAddressSource: billingAddressSource.optional()}); export const externalAccount = z.object({id: typeId.optional(), object: z.literal("externalAccount").optional(), owner: externalAccountOwner.optional(), type: externalAccountType.optional(), category: externalAccountCategory.optional(), status: externalAccountStatus.optional(), failureReason: externalAccountFailureReason.optional(), failureDetail: providerRejectionDetail.optional(), rejectionReason: z.string().optional(), invalidReason: z.string().optional(), label: z.string().optional(), metadata: z.record(z.string(), z.string()).optional(), bankUs: externalAccountBankUsResponse.optional(), bankIban: externalAccountBankIbanResponse.optional(), bankCanada: externalAccountBankCanadaResponse.optional(), walletExternal: externalAccountWalletExternalResponse.optional(), card: externalAccountCardResponse.optional(), resolvedTransactions: z.array(typeId).optional(), createdAt: z.string().optional(), updatedAt: z.string().optional()}); 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 }; /** * Create an External Account * * POST /external-accounts */ export const createApiExternalAccounts = async (args: CreateApiExternalAccountsArgs, options?: CreateApiExternalAccountsOptions) => { const {baseUrl = "https://sandbox-api.polygon.technology/v0.10", fetch: fetchFn = fetch, BearerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args['Idempotency-Key'] != null) headers.set("Idempotency-Key", String(args['Idempotency-Key'])) if (BearerAuth !== undefined) headers.set('authorization', `Bearer ${BearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/external-accounts${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(externalAccount, await res.json()) };