import {z} from 'zod' export type GetApiTransactionsTxIdArgs = {txId: string}; export type GetApiTransactionsTxIdOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const getTransactionOperation = z.enum(["TRANSFER", "BURN", "CONTRACT_CALL", "MINT", "RAW", "TYPED_MESSAGE", "ENABLE_ASSET", "STAKE", "REDEEM_FROM_COMPOUND", "SUPPLY_TO_COMPOUND", "PROGRAM_CALL", "APPROVE", "SOROBAN_CALL"]); export const transferPeerPathType = z.enum(["VAULT_ACCOUNT", "EXCHANGE_ACCOUNT", "CONNECTED_ACCOUNT", "INTERNAL_WALLET", "EXTERNAL_WALLET", "CONTRACT", "NETWORK_CONNECTION", "FIAT_ACCOUNT", "COMPOUND", "GAS_STATION", "ONE_TIME_ADDRESS", "UNKNOWN", "END_USER_WALLET", "PROGRAM_CALL", "MULTI_DESTINATION", "OEC_PARTNER", "WALLET_POOL", "SOROBAN_CONTRACT"]); export const transactionTag = z.object({id: z.string(), label: z.string()}); export const sourceTransferPeerPathResponse = z.object({type: transferPeerPathType, subType: z.string().optional(), id: z.string().nullable().optional(), name: z.string().optional(), walletId: z.string().optional(), tradingAccount: z.string().nullable().optional(), tags: z.array(transactionTag).optional()}); export const destinationTransferPeerPathResponse = z.object({type: transferPeerPathType, subType: z.string().optional(), id: z.string().nullable().optional(), name: z.string().optional(), walletId: z.string().optional(), tradingAccount: z.string().nullable().optional(), tags: z.array(transactionTag).optional()}); export const screeningVerdictEnum = z.enum(["PASS", "FAIL", "ALERT", "REJECT", "WARN", "TRAVEL_RULE_REQUIRED", "ACCEPT", "REVIEW", "WAIT"]); export const screeningStatusEnum = z.enum(["COMPLETED", "PENDING", "BYPASSED", "FAILED", "FROZEN"]); export const amlBypassReasonEnum = z.enum(["MANUAL", "UNSUPPORTED_ASSET", "BYPASSED_FAILURE", "UNSUPPORTED_ROUTE", "PASSED_BY_POLICY", "TIMED_OUT", "BAD_CREDENTIALS", "CONFIGURATION_ERROR", "DROPPED_BY_BLOCKCHAIN", "PROCESS_DISMISSED"]); export const policyVerdictActionEnum = z.enum(["ALLOW", "BLOCK", "2-TIER", "SCREEN", "ACCEPT", "REJECT", "ALERT", "WAIT", "FREEZE", "CANCEL"]); export const amlMatchedRule = z.object({ruleId: z.string().optional(), ruleName: z.string().optional(), action: policyVerdictActionEnum.optional()}); export const alertLevelEnum = z.enum(["SEVERE", "HIGH", "MEDIUM", "LOW"]); export const alertExposureTypeEnum = z.enum(["DIRECT", "INDIRECT"]); export const amlAlert = z.object({alertLevel: alertLevelEnum, alertName: z.string().optional(), category: z.string().optional(), service: z.string().optional(), externalId: z.string(), alertAmount: z.number(), exposureType: alertExposureTypeEnum, policyAction: z.string().optional(), categoryId: z.number().optional()}); export const amlScreeningResult = z.object({provider: z.string().optional(), payload: z.record(z.string(), z.unknown()).optional(), verdict: screeningVerdictEnum.optional(), screeningStatus: screeningStatusEnum.optional(), bypassReason: amlBypassReasonEnum.optional(), timestamp: z.number().optional(), customerRefId: z.string().optional(), externalId: z.string().optional(), category: z.string().optional(), categoryId: z.number().optional(), risk: z.string().optional(), destAddress: z.string().optional(), matchedRule: amlMatchedRule.optional(), matchedPrescreeningRule: amlMatchedRule.optional(), matchedAlert: amlAlert.optional()}); export const authorizationGroups = z.object({th: z.number().optional(), users: z.record(z.string(), z.enum(["PENDING_AUTHORIZATION", "APPROVED", "REJECTED", "NA"])).optional()}); export const authorizationInfo = z.object({allowOperatorAsAuthorizer: z.boolean().optional(), logic: z.enum(["AND", "OR"]).optional(), groups: z.array(authorizationGroups).optional()}); export const transactionResponseDestination = z.object({amount: z.string().optional(), amountUSD: z.string().optional(), travelRuleMessageId: z.string().optional(), amlScreeningResult: amlScreeningResult.optional(), destination: destinationTransferPeerPathResponse.optional(), authorizationInfo: authorizationInfo.optional()}); export const programCallInstructionData = z.record(z.string(), z.unknown()); export const programCallDecodedDataItem = z.object({programId: z.string(), programName: z.string(), instructionData: programCallInstructionData}); export const amountInfo = z.object({amount: z.string().optional(), requestedAmount: z.string().optional(), netAmount: z.string().optional(), amountUSD: z.string().optional()}); export const roleFeeInfo = z.object({feesDeducted: z.string().optional(), valueCredited: z.string().optional()}); export const feeInfo = z.object({networkFee: z.string().optional(), serviceFee: z.string().optional(), gasPrice: z.string().optional(), L1networkFee: z.string().optional(), L2networkFee: z.string().optional(), paidByRelay: z.boolean().optional(), relayType: z.enum(["LOCAL", "THIRD_PARTY"]).optional(), relayId: z.string().optional(), relayName: z.string().optional(), feeUSD: z.string().optional(), sender: roleFeeInfo.optional(), recipient: roleFeeInfo.optional()}); export const cantonDomainEnum = z.enum(["ONBOARDING", "ALLOCATION", "TRANSFER"]); export const cantonVendorEnum = z.enum(["DTCC", "TRADEWEB"]); export const cantonOfferResponseDetails = z.object({version: z.number().int().optional(), domain: cantonDomainEnum.optional(), vendor: cantonVendorEnum.optional(), availableResponses: z.array(z.string()).optional(), expiresAt: z.string().optional(), approvalTransactionId: z.string().optional(), originalTransactionId: z.string().optional(), responseType: z.string().optional(), allocationTransactionId: z.string().optional(), verdict: z.enum(["ACCEPTED", "REJECTED"]).optional()}); export const cantonCallDetails = z.object({version: z.number().int().optional(), domain: cantonDomainEnum.optional(), type: z.string().optional(), vendor: cantonVendorEnum.optional(), originalTransactionId: z.string().optional()}); export const cantonSettlementDetails = z.object({version: z.number().int().optional(), domain: cantonDomainEnum.optional(), type: z.string().optional(), originalTransactionId: z.string().optional()}); export const cantonDetails = z.object({offerResponse: cantonOfferResponseDetails.optional(), call: cantonCallDetails.optional(), settlement: cantonSettlementDetails.optional()}); export const rewardInfo = z.object({srcRewards: z.string().optional(), destRewards: z.string().optional()}); export const networkRecord = z.object({source: sourceTransferPeerPathResponse.optional(), destination: destinationTransferPeerPathResponse.optional(), txHash: z.string().optional(), networkFee: z.string().optional(), assetId: z.string().optional(), netAmount: z.string().optional(), isDropped: z.boolean().optional(), type: z.string().optional(), destinationAddress: z.string().optional(), amountUSD: z.string().optional(), rewardInfo: rewardInfo.optional()}); export const amlStatusEnum = z.enum(["COMPLETED", "PENDING", "BYPASSED", "FAILED", "FROZEN"]); export const amlResult = z.object({alerts: z.array(amlAlert).optional(), providerResponse: z.record(z.string(), z.unknown()).optional(), matchedRule: amlMatchedRule.optional(), matchedAlert: amlAlert.optional()}); export const travelRuleDirectionEnum = z.enum(["INBOUND", "OUTBOUND"]); export const travelRuleVerdictEnum = z.enum(["ACCEPT", "REJECT", "ALERT", "REVIEW", "WAIT", "FREEZE", "CANCEL"]); export const travelRuleStatusEnum = z.enum(["COMPLETED", "PENDING", "BYPASSED", "FAILED", "FROZEN", "REJECTED", "CANCELED", "BLOCKING_TIME_EXPIRED"]); export const travelRuleMatchedRule = z.object({direction: travelRuleDirectionEnum.optional(), status: travelRuleStatusEnum.optional(), amountUSD: z.number().optional(), amount: z.number().optional(), asset: z.string().optional(), action: travelRuleVerdictEnum.optional()}); export const travelRuleResult = z.object({direction: travelRuleDirectionEnum.optional(), isVerified: z.boolean().optional(), action: travelRuleVerdictEnum.optional(), providerResponse: z.record(z.string(), z.unknown()).optional(), matchedRule: travelRuleMatchedRule.optional()}); export const transferPeerTypeEnum = z.enum(["VAULT_ACCOUNT", "EXCHANGE_ACCOUNT", "INTERNAL_WALLET", "EXTERNAL_WALLET", "CONTRACT", "NETWORK_CONNECTION", "FIAT_ACCOUNT", "COMPOUND", "GAS_STATION", "ONE_TIME_ADDRESS", "UNKNOWN", "END_USER_WALLET", "PROGRAM_CALL", "MULTI_DESTINATION", "WALLET_POOL"]); export const transferPeerSubTypeEnum = z.enum(["EXTERNAL", "INTERNAL", "CONTRACT", "EXCHANGETEST"]); export const transactionOperationEnum = z.enum(["TRANSFER", "BURN", "CONTRACT_CALL", "MINT", "RAW", "TYPED_MESSAGE", "ENABLE_ASSET", "STAKE", "UNSTAKE", "WITHDRAW", "REDEEM_FROM_COMPOUND", "SUPPLY_TO_COMPOUND", "PROGRAM_CALL", "APPROVE"]); export const travelRuleActionEnum = z.enum(["SCREEN", "BYPASS", "BLOCK", "PASS", "FREEZE"]); export const travelRulePrescreeningRule = z.object({bypassReason: z.string().optional(), sourceType: transferPeerTypeEnum.optional(), sourceSubType: transferPeerSubTypeEnum.optional(), destType: transferPeerTypeEnum.optional(), destSubType: transferPeerSubTypeEnum.optional(), transferPeerType: transferPeerTypeEnum.optional(), transferPeerSubType: transferPeerSubTypeEnum.optional(), destAddress: z.string().optional(), sourceId: z.string().optional(), destId: z.string().optional(), asset: z.string().optional(), baseAsset: z.string().optional(), amount: z.number().optional(), amountUSD: z.number().optional(), networkProtocol: z.string().optional(), operation: transactionOperationEnum.optional(), action: travelRuleActionEnum.optional()}); export const screeningProviderResponse = z.record(z.string(), z.unknown()); export const complianceScreeningResult = z.object({provider: z.string().optional(), payload: z.object({}).optional(), timestamp: z.number().optional(), screeningStatus: z.enum(["COMPLETED", "PENDING", "BYPASSED", "FAILED", "FROZEN"]).optional(), bypassReason: z.string().optional(), status: amlStatusEnum.optional(), prevStatus: amlStatusEnum.optional(), prevBypassReason: z.string().optional(), verdict: screeningVerdictEnum.optional(), risk: z.string().optional(), extendedRisk: z.string().optional(), externalId: z.string().optional(), customerRefId: z.string().optional(), refId: z.string().optional(), category: z.string().optional(), categoryId: z.number().optional(), destAddress: z.string().optional(), destTag: z.string().optional(), destRecordId: z.string().optional(), addressResolutionSignature: z.string().optional(), amlResult: amlResult.optional(), result: travelRuleResult.optional(), detailsMessage: z.string().optional(), matchedAlert: z.object({}).optional(), matchedRule: z.object({}).optional(), matchedPrescreeningRule: travelRulePrescreeningRule.optional(), matchedNoTrmScreeningRule: z.object({}).optional(), customerIntegrationId: z.string().optional(), customerShortName: z.string().optional(), travelRuleMessageId: z.string().optional(), providerResponse: screeningProviderResponse.optional()}); export const complianceResultStatusesEnum = z.enum(["Unknown", "Stalled", "Started", "NetworkConnectionAddressResolve", "ScreeningPrepare", "AMLStarted", "AMLCompleted", "AMLFailed", "AMLInBackground", "TRPreconditionChecks", "TRStarted", "TRLinkStarted", "TRLinkDestinationStarted", "TRLinkDestinationPrescreenPolicy", "TRLinkDestinationNoTRMPolicy", "TRLinkDestinationScreen", "TRLinkDestinationPostscreenPolicy", "TRLinkDestinationCompleted", "TRLinkCompleted", "TRCompleted", "TRFailed", "Completed", "RegistrationStarted", "RegistrationWaitForFirstConfirmation", "AMLRegistrationStarted", "AMLRegistrationCompleted", "TRUpdateStarted", "TRUpdateCompleted", "UpdateCompleted", "IncomingStarted", "IncomingByorkSL", "IncomingScreeningPrepare", "IncomingWaitForFirstConfirmation", "AMLIncomingStarted", "AMLIncomingCompleted", "AMLIncomingFailed", "AMLIncomingInBackground", "TRIncomingStarted", "TRIncomingCompleted", "TRIncomingFailed", "IncomingCompleted", "AddressAmlInitiated", "AddressAmlAddressResolve", "AddressAmlPrepare", "AddressAmlProcessing", "AddressAmlSucceeded", "AddressAmlFailed", "AddressAmlCompleted"]); export const amlRegistrationResult = z.object({provider: z.string().optional(), success: z.boolean().optional(), timestamp: z.number().optional()}); export const tRLinkRegistrationStatus = z.enum(["STARTING", "PRESCREENED", "COMPLETED", "FAILED", "VOID"]); export const tRLinkProviderResult = z.object({providerResponse: z.object({}).optional()}); export const transactionDirection = z.enum(["INBOUND", "OUTBOUND"]); export const amountRangeMinMax = z.object({min: z.string().optional(), max: z.string().optional()}); export const tRLinkAmount = z.object({range: amountRangeMinMax.optional(), currency: z.enum(["USD", "NATIVE"]).optional()}); export const tRLinkPreScreeningAction = z.enum(["SCREEN", "PASS"]); export const tRLinkPreScreeningRule = z.object({customerId: z.string().optional(), direction: transactionDirection.optional(), sourceType: transferPeerTypeEnum.optional(), sourceSubType: transferPeerSubTypeEnum.optional(), sourceAddress: z.string().optional(), destType: transferPeerTypeEnum.optional(), destSubType: transferPeerSubTypeEnum.optional(), destAddress: z.string().optional(), sourceId: z.string().optional(), destId: z.string().optional(), asset: z.string().optional(), baseAsset: z.string().optional(), amount: tRLinkAmount.optional(), networkProtocol: z.string().optional(), operation: transactionOperationEnum.optional(), description: z.string().optional(), isDefault: z.boolean().optional(), action: tRLinkPreScreeningAction}); export const tRLinkRegistrationResult = z.object({status: tRLinkRegistrationStatus, provider: z.string().optional(), success: z.boolean().optional(), timestamp: z.number(), destRecordId: z.string().optional(), travelRuleMessageId: z.string().optional(), customerIntegrationId: z.string().optional(), customerShortName: z.string().optional(), result: tRLinkProviderResult.optional(), matchedPrescreeningRule: tRLinkPreScreeningRule.optional()}); export const tRLinkVerdict = z.enum(["ACCEPT", "ALERT", "REJECT", "WAIT"]); export const tRLinkTrmScreeningStatus = z.enum(["PENDING", "ACCEPTED", "REJECTED", "FAILED"]); export const tRLinkPostScreeningRule = z.object({customerId: z.string().optional(), direction: transactionDirection.optional(), sourceType: transferPeerTypeEnum.optional(), sourceSubType: transferPeerSubTypeEnum.optional(), sourceAddress: z.string().optional(), destType: transferPeerTypeEnum.optional(), destSubType: transferPeerSubTypeEnum.optional(), destAddress: z.string().optional(), sourceId: z.string().optional(), destId: z.string().optional(), asset: z.string().optional(), baseAsset: z.string().optional(), amount: tRLinkAmount.optional(), networkProtocol: z.string().optional(), operation: transactionOperationEnum.optional(), description: z.string().optional(), isDefault: z.boolean().optional(), providerIdent: z.string().optional(), trmStatus: tRLinkTrmScreeningStatus.optional(), validBefore: z.number().optional(), validAfter: z.number().optional(), action: tRLinkVerdict}); export const tRLinkProviderResultWithRule = z.object({providerResponse: z.object({}).optional(), matchedRule: tRLinkPostScreeningRule.optional()}); export const tRLinkMissingTrmAction = z.enum(["WAIT", "REJECT", "ACCEPT", "INITIATE_TRM"]); export const tRLinkMissingTrmDecision = z.object({customerId: z.string().optional(), direction: transactionDirection.optional(), sourceType: transferPeerTypeEnum.optional(), sourceSubType: transferPeerSubTypeEnum.optional(), sourceAddress: z.string().optional(), destType: transferPeerTypeEnum.optional(), destSubType: transferPeerSubTypeEnum.optional(), destAddress: z.string().optional(), sourceId: z.string().optional(), destId: z.string().optional(), asset: z.string().optional(), baseAsset: z.string().optional(), amount: tRLinkAmount.optional(), networkProtocol: z.string().optional(), operation: transactionOperationEnum.optional(), description: z.string().optional(), isDefault: z.boolean().optional(), validBefore: z.number().optional(), validAfter: z.number().optional(), action: tRLinkMissingTrmAction, source: z.string(), timestamp: z.string().optional(), reason: z.string().optional()}); export const tRLinkResult = z.object({provider: z.string(), timestamp: z.number(), status: z.enum(["COMPLETED", "PENDING", "BYPASSED", "FAILED", "FROZEN"]), verdict: tRLinkVerdict.optional(), destAddress: z.string().optional(), destTag: z.string().optional(), bypassReason: z.string().optional(), detailsMessage: z.string().optional(), customerIntegrationId: z.string().optional(), customerShortName: z.string().optional(), travelRuleMessageId: z.string().optional(), result: tRLinkProviderResultWithRule.optional(), matchedPrescreeningRule: tRLinkPreScreeningRule.optional(), matchedNoTrmScreeningRule: tRLinkMissingTrmDecision.optional()}); export const complianceResults = z.object({aml: complianceScreeningResult.optional(), tr: complianceScreeningResult.optional(), amlList: z.array(complianceScreeningResult).optional(), status: complianceResultStatusesEnum.optional(), amlRegistration: amlRegistrationResult.optional(), trlinkRegistration: tRLinkRegistrationResult.optional(), trlinkDestinations: z.array(tRLinkResult).optional()}); export const sorobanCallData = z.object({envelopeXdr: z.string().min(1).max(176128)}); export const extraParameters = z.object({nodeControls: z.union([z.record(z.string(), z.unknown()), z.unknown()]).optional(), rawMessageData: z.union([z.record(z.string(), z.unknown()), z.unknown()]).optional(), contractCallData: z.union([z.string(), z.unknown()]).optional(), programCallData: z.union([z.string(), z.unknown()]).optional(), sorobanCallData: z.union([sorobanCallData, z.unknown()]).optional(), inputsSelection: z.union([z.record(z.string(), z.unknown()), z.unknown()]).optional(), allowBaseAssetAddress: z.union([z.boolean(), z.unknown()]).optional(), piiData: z.union([z.record(z.string(), z.unknown()), z.unknown()]).optional()}).and(z.record(z.string(), z.unknown())); export const signedMessage = z.object({content: z.string().optional(), algorithm: z.enum(["MPC_ECDSA_SECP256K1", "MPC_EDDSA_ED25519"]).optional(), derivationPath: z.array(z.number()).optional(), signature: z.object({fullSig: z.string().optional(), r: z.string().optional(), s: z.string().optional(), v: z.number().optional()}).optional(), publicKey: z.string().optional()}); export const signedMessages = z.array(signedMessage); export const blockInfo = z.object({blockHeight: z.string().optional(), blockHash: z.string().nullable().optional()}); export const feePayerInfo = z.object({feePayerAccountId: z.string().optional()}); export const gaslessInfo = z.object({isMetaTx: z.boolean().optional(), relayTenantId: z.string().optional(), relayTenantName: z.string().optional(), relayVaultAccountId: z.string().optional()}); export const systemMessageInfo = z.object({type: z.enum(["WARN", "BLOCK"]).optional(), message: z.string().optional()}); export const transactionResponse = z.object({id: z.string().optional(), externalTxId: z.string().optional(), status: z.string().optional(), subStatus: z.string().optional(), txHash: z.string().optional(), operation: getTransactionOperation.optional(), note: z.string().optional(), assetId: z.string().optional(), assetType: z.string().optional(), source: sourceTransferPeerPathResponse.optional(), sourceAddress: z.string().optional(), tag: z.string().optional(), destination: destinationTransferPeerPathResponse.optional(), destinations: z.array(transactionResponseDestination).optional(), destinationAddress: z.string().optional(), destinationAddressDescription: z.string().optional(), destinationTag: z.string().optional(), contractCallDecodedData: z.object({contractName: z.string().optional(), functionCalls: z.array(z.object({})).optional()}).optional(), programCallDecodedData: z.array(programCallDecodedDataItem).optional(), amountInfo: amountInfo.optional(), treatAsGrossAmount: z.boolean().optional(), feeInfo: feeInfo.optional(), feeCurrency: z.string().optional(), requestedFeeCurrency: z.string().optional(), cantonDetails: cantonDetails.optional(), networkRecords: z.array(networkRecord).optional(), createdAt: z.number().optional(), lastUpdated: z.number().optional(), expiresAt: z.number().optional(), createdBy: z.string().optional(), signedBy: z.array(z.string()).optional(), rejectedBy: z.string().optional(), authorizationInfo: authorizationInfo.optional(), exchangeTxId: z.string().optional(), customerRefId: z.string().optional(), travelRuleMessageId: z.string().optional(), amlScreeningResult: amlScreeningResult.optional(), complianceResults: complianceResults.optional(), notBroadcastByFireblocks: z.boolean().optional(), dappUrl: z.string().optional(), gasLimit: z.string().optional(), blockchainIndex: z.string().optional(), paidRent: z.string().optional(), extraParameters: extraParameters.optional(), signedMessages: signedMessages.optional(), numOfConfirmations: z.number().optional(), blockInfo: blockInfo.optional(), index: z.number().optional(), rewardInfo: rewardInfo.optional(), feePayerInfo: feePayerInfo.optional(), gaslessInfo: gaslessInfo.optional(), systemMessages: z.array(systemMessageInfo).optional(), addressType: z.enum(["", "WHITELISTED", "ONE_TIME"]).optional(), requestedAmount: z.number().optional(), amount: z.number().optional(), netAmount: z.number().optional(), amountUSD: z.number().nullable().optional(), serviceFee: z.number().optional(), fee: z.number().optional(), networkFee: z.number().optional(), errorDescription: z.string().optional(), replacedTxHash: z.string().optional(), nonce: z.string().optional(), blockchainInfo: z.object({}).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 }; /** * Get a specific transaction by Fireblocks transaction ID * * GET /transactions/{txId} */ export const getApiTransactionsTxId = async (args: GetApiTransactionsTxIdArgs, options?: GetApiTransactionsTxIdOptions) => { const {baseUrl = "https://api.fireblocks.io/v1", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/transactions/${encodeURIComponent(String(args.txId))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(transactionResponse, await res.json()) };