@opendatalabs/vana-sdk 3.23.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +57 -0
  2. package/dist/auth/errors.cjs +1 -1
  3. package/dist/auth/web3-signed.cjs +1 -1
  4. package/dist/crypto/envelope/job-stream.test.d.ts +1 -0
  5. package/dist/crypto/envelope/job.cjs +593 -38
  6. package/dist/crypto/envelope/job.cjs.map +1 -1
  7. package/dist/crypto/envelope/job.d.ts +44 -27
  8. package/dist/crypto/envelope/job.js +589 -37
  9. package/dist/crypto/envelope/job.js.map +1 -1
  10. package/dist/direct/access-request-client.cjs +1 -1
  11. package/dist/direct/connect-flow.cjs +43 -1
  12. package/dist/direct/connect-flow.cjs.map +1 -1
  13. package/dist/direct/connect-flow.d.ts +12 -0
  14. package/dist/direct/connect-flow.js +43 -1
  15. package/dist/direct/connect-flow.js.map +1 -1
  16. package/dist/direct/controller.cjs +1 -1
  17. package/dist/direct/errors.cjs +1 -1
  18. package/dist/direct/escrow-payment.cjs.map +1 -1
  19. package/dist/direct/escrow-payment.d.ts +2 -2
  20. package/dist/direct/escrow-payment.js.map +1 -1
  21. package/dist/direct/personal-server-read.cjs +2 -2
  22. package/dist/direct/use-direct-vana-connect.cjs +2 -1
  23. package/dist/direct/use-direct-vana-connect.cjs.map +1 -1
  24. package/dist/direct/use-direct-vana-connect.d.ts +3 -1
  25. package/dist/direct/use-direct-vana-connect.js +2 -1
  26. package/dist/direct/use-direct-vana-connect.js.map +1 -1
  27. package/dist/error-entry-points.test.d.ts +1 -0
  28. package/dist/errors.cjs +7 -0
  29. package/dist/errors.cjs.map +1 -1
  30. package/dist/errors.d.ts +10 -0
  31. package/dist/errors.js +6 -0
  32. package/dist/errors.js.map +1 -1
  33. package/dist/index.browser.d.ts +4 -4
  34. package/dist/index.browser.js +882 -518
  35. package/dist/index.browser.js.map +4 -4
  36. package/dist/index.node.cjs +1035 -705
  37. package/dist/index.node.cjs.map +4 -4
  38. package/dist/index.node.d.ts +4 -4
  39. package/dist/index.node.js +989 -567
  40. package/dist/index.node.js.map +4 -4
  41. package/dist/protocol/data-point-deletion.cjs +1 -1
  42. package/dist/protocol/derivative-questions.cjs +1 -1
  43. package/dist/protocol/derivative-status.cjs +1 -1
  44. package/dist/protocol/eip712.cjs +28 -2
  45. package/dist/protocol/eip712.cjs.map +1 -1
  46. package/dist/protocol/eip712.d.ts +70 -0
  47. package/dist/protocol/eip712.js +24 -1
  48. package/dist/protocol/eip712.js.map +1 -1
  49. package/dist/protocol/escrow.cjs +154 -2
  50. package/dist/protocol/escrow.cjs.map +1 -1
  51. package/dist/protocol/escrow.d.ts +145 -2
  52. package/dist/protocol/escrow.js +152 -1
  53. package/dist/protocol/escrow.js.map +1 -1
  54. package/dist/protocol/fixtures/moksha-identity.json +20 -0
  55. package/dist/protocol/gateway.cjs +1 -1
  56. package/dist/protocol/gateway.cjs.map +1 -1
  57. package/dist/protocol/gateway.d.ts +8 -38
  58. package/dist/protocol/gateway.js.map +1 -1
  59. package/dist/protocol/identity.cjs +7 -2
  60. package/dist/protocol/identity.cjs.map +1 -1
  61. package/dist/protocol/identity.js +7 -2
  62. package/dist/protocol/identity.js.map +1 -1
  63. package/dist/protocol/jobs-client.cjs +73 -21
  64. package/dist/protocol/jobs-client.cjs.map +1 -1
  65. package/dist/protocol/jobs-client.d.ts +7 -5
  66. package/dist/protocol/jobs-client.js +74 -21
  67. package/dist/protocol/jobs-client.js.map +1 -1
  68. package/dist/protocol/jobs.cjs +0 -3
  69. package/dist/protocol/jobs.cjs.map +1 -1
  70. package/dist/protocol/jobs.d.ts +21 -13
  71. package/dist/protocol/jobs.js +0 -2
  72. package/dist/protocol/jobs.js.map +1 -1
  73. package/dist/protocol/lineage.cjs +1 -1
  74. package/dist/protocol/personal-server-data.cjs +1 -1
  75. package/dist/protocol/personal-server-write.cjs +1 -1
  76. package/dist/protocol/write-request.cjs +1 -1
  77. package/dist/protocol/write-signer.cjs +1 -1
  78. package/dist/react.cjs.map +1 -1
  79. package/dist/react.d.ts +1 -1
  80. package/dist/react.js.map +1 -1
  81. package/dist/session-relay/client.cjs +1 -1
  82. package/dist/session-relay/errors.cjs +1 -1
  83. package/dist/session-relay/index.cjs +1 -1
  84. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/protocol/escrow.ts"],"sourcesContent":["/**\n * DPv2 escrow payment helpers.\n *\n * Covers the three-phase flow used by builders to pay for data access:\n *\n * 1. **Deposit** — call `depositNative` or `depositToken` on the\n * DataPortabilityEscrow contract, then notify the gateway.\n * 2. **Balance** — read or force-sync the gateway's off-chain credit view.\n * 3. **Pay** — sign a `GenericPayment` EIP-712 message and POST it to the\n * gateway's `/v1/escrow/pay` endpoint.\n *\n * The gateway is the authority on balances; the on-chain contract is the\n * authority on what has been settled. Nothing in this module touches the\n * chain directly — signing is done by the caller's wallet.\n *\n * @category Protocol\n * @module escrow\n */\n\nimport type { TypedDataDomain } from \"viem\";\n\n// ---------------------------------------------------------------------------\n// EIP-712 — GenericPayment\n// ---------------------------------------------------------------------------\n\n/**\n * EIP-712 typed-data types for a generic op payment.\n *\n * The gateway verifies that the recovered signer == `payerAddress` and that\n * the (payer, paymentNonce) pair has not been seen before. Use a\n * monotonically-increasing nonce; the first payment for any payer should\n * start at 1.\n */\nexport const GENERIC_PAYMENT_TYPES = {\n GenericPayment: [\n { name: \"payerAddress\", type: \"address\" },\n { name: \"opType\", type: \"string\" },\n { name: \"opId\", type: \"bytes32\" },\n { name: \"asset\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"paymentNonce\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * EIP-712 message payload for a generic op payment.\n *\n * - `opType` is `\"grant\"` for legacy grant lifecycle payments or\n * `\"data_access\"` for a standalone receipt-bound read.\n * - `opId` is the bytes32 id of the operation being paid for: the grant id for\n * `\"grant\"`, or `accessRecord.recordId` for `\"data_access\"`.\n * - `asset` is the ERC-20 token address, or the zero address for native VANA.\n * - `amount` is the total amount in base units (wei for VANA). Must match the\n * sum the gateway expects for the current lifecycle of the op.\n * - `paymentNonce` must be a positive integer unique per `payerAddress`. Use 1\n * for the first payment; increment by at least 1 for each subsequent call.\n */\nexport interface GenericPaymentMessage {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n amount: bigint;\n paymentNonce: bigint;\n}\n\n/**\n * Returns the EIP-712 domain for signing a `GenericPayment` message.\n *\n * The verifying contract is the `DataPortabilityEscrow` contract; all gateway\n * deployments share the same domain name and version.\n *\n * @param chainId - Chain ID of the Vana network (e.g. 1480 mainnet, 14800 testnet).\n * @param escrowContract - Deployed address of DataPortabilityEscrow.\n */\nexport function genericPaymentDomain(\n chainId: number,\n escrowContract: `0x${string}`,\n): TypedDataDomain {\n return {\n name: \"Vana Data Portability\",\n version: \"1\",\n chainId,\n verifyingContract: escrowContract,\n };\n}\n\n// ---------------------------------------------------------------------------\n// On-chain deposit ABI fragments\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal ABI for the two deposit entry points on `DataPortabilityEscrow`.\n *\n * - `depositNative(address account)` payable — credits native VANA.\n * - `depositToken(address account, address token, uint256 amount)` — credits\n * an ERC-20 token (caller must have pre-approved the escrow contract).\n *\n * Pass this to viem's `writeContract` or encode it manually.\n */\nexport const ESCROW_DEPOSIT_ABI = [\n {\n type: \"function\",\n name: \"depositNative\",\n stateMutability: \"payable\",\n inputs: [{ name: \"account\", type: \"address\" }],\n outputs: [],\n },\n {\n type: \"function\",\n name: \"depositToken\",\n stateMutability: \"nonpayable\",\n inputs: [\n { name: \"account\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n outputs: [],\n },\n] as const;\n\n/**\n * The zero address used by the DataPortabilityEscrow contract to represent\n * native VANA in `asset` fields of events and balance responses.\n */\nexport const NATIVE_ASSET_ADDRESS =\n \"0x0000000000000000000000000000000000000000\" as const;\n\n// ---------------------------------------------------------------------------\n// Gateway API client\n// ---------------------------------------------------------------------------\n\n/**\n * Per-asset balance entry returned by the gateway's escrow balance endpoints.\n *\n * - `balance` — gross finalized credit (deposits credited so far).\n * - `pendingAmount` — sum of submitted deposits not yet confirmed.\n * - `authorizedAmount` — sum of all in-flight payments authorized by\n * `/v1/escrow/pay` (soft-lock). May include payments not yet settled\n * on-chain.\n * - `availableAmount` — `max(balance − authorizedAmount, 0)`. This is what\n * the payer can authorize before the gateway rejects with 402.\n */\nexport interface EscrowBalanceEntry {\n asset: string;\n balance: string;\n pendingAmount: string;\n authorizedAmount: string;\n availableAmount: string;\n updatedAt: string | null;\n}\n\nexport interface SubmittedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FinalizedDepositEntry {\n txHash: string;\n finalizedAt: string | null;\n blockNumber: string | null;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FailedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n lastError: string | null;\n}\n\n/** Full balance read response from `GET /v1/escrow/balance`. */\nexport interface EscrowBalanceResult {\n account: string;\n balances: EscrowBalanceEntry[];\n deposits: {\n submitted: SubmittedDepositEntry[];\n finalized: FinalizedDepositEntry[];\n failed: FailedDepositEntry[];\n };\n}\n\n/**\n * Response from `POST /v1/escrow/balance/sync`.\n *\n * Extends {@link EscrowBalanceResult} with a `sync` summary of what the\n * lazy-confirmation pass did.\n */\nexport interface EscrowBalanceSyncResult extends EscrowBalanceResult {\n sync:\n | {\n scanned: number;\n finalized: number;\n stillPending: number;\n failed: number;\n }\n | { skipped: true };\n}\n\n/** Response from `POST /v1/escrow/deposit`. */\nexport interface DepositSubmissionResult {\n success: true;\n txHash: string;\n account: string;\n status: \"submitted\" | \"finalized\" | \"failed\";\n blockNumber?: string | null;\n submittedAt: string;\n finalizedAt?: string | null;\n lastError?: string | null;\n}\n\n/** Breakdown returned by a successful `POST /v1/escrow/pay`. */\nexport interface PaymentBreakdown {\n registrationFee: string;\n dataAccessFee: string;\n /** True when this call settled the registration fee for the op. */\n registrationPaid: boolean;\n}\n\n/** Response from `POST /v1/escrow/pay`. */\nexport interface EscrowPayResult {\n success: true;\n opType: string;\n opId: string;\n payerAddress: string;\n asset: string;\n amount: string;\n breakdown: PaymentBreakdown;\n paymentNonce: string;\n paidAt: string;\n}\n\n/**\n * Parameters for submitting a deposit tx hash to the gateway.\n *\n * The gateway will decode the `account` from the tx's calldata and\n * credit the identified account once the tx reaches the configured\n * confirmation depth.\n */\nexport interface SubmitDepositParams {\n /** 0x-prefixed 32-byte transaction hash. */\n txHash: `0x${string}`;\n}\n\n/**\n * Parameters for the generic op payment endpoint (`POST /v1/escrow/pay`).\n *\n * The `signature` is an EIP-712 signature over a `GenericPayment` message\n * (see {@link GENERIC_PAYMENT_TYPES} and {@link genericPaymentDomain}).\n * Build and sign the typed data with your wallet before calling\n * {@link EscrowGatewayClient.payForOp}.\n */\nexport interface PayForOpParams {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n /** Decimal string representation of the uint256 amount. */\n amount: string;\n /** Decimal string representation of the uint256 nonce. */\n paymentNonce: string;\n /** 0x-prefixed 65-byte EIP-712 signature hex string. */\n signature: `0x${string}`;\n /**\n * Optional data-access receipt carried by x402 challenges.\n *\n * The gateway verifies its server signature; this type only describes the\n * wire shape.\n */\n accessRecord?: EscrowAccessRecord;\n}\n\n/** Wire shape of a receipt whose server signature the gateway verifies. */\nexport interface EscrowAccessRecord {\n dataPointId: `0x${string}`;\n version: string;\n accessor: `0x${string}`;\n recordId: `0x${string}`;\n signature: `0x${string}`;\n}\n\n/**\n * Minimal client for the gateway's escrow endpoints.\n *\n * Construct with {@link createEscrowGatewayClient}.\n */\nexport interface EscrowGatewayClient {\n /**\n * Notify the gateway of a submitted deposit transaction.\n *\n * The gateway decodes the credited account from the on-chain tx calldata\n * and starts tracking the deposit. Call this immediately after your\n * `depositNative` or `depositToken` tx is broadcast (it accepts pending\n * mempool txs). Returns `202` while the tx awaits confirmation.\n */\n submitDeposit(params: SubmitDepositParams): Promise<DepositSubmissionResult>;\n\n /**\n * Read the current escrow balance for an account.\n *\n * Pure read — no chain calls. To force a reconciliation pass first,\n * use {@link syncEscrowBalance}.\n */\n getEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceResult>;\n\n /**\n * Force a reconciliation pass then return the updated balance.\n *\n * Triggers the gateway's lazy-confirmation worker for the account — any\n * submitted deposits that have reached the configured confirmation level\n * are credited before the balance is returned. Prefer this over\n * {@link getEscrowBalance} when you need a fresh view after a deposit.\n */\n syncEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceSyncResult>;\n\n /**\n * Authorize a payment against the payer's escrow balance.\n *\n * The caller must:\n * 1. Assemble a {@link GenericPaymentMessage}.\n * 2. Sign it with `signTypedData` using {@link GENERIC_PAYMENT_TYPES} and\n * the domain from {@link genericPaymentDomain}.\n * 3. Pass the message fields + signature here.\n *\n * The gateway verifies the signature, checks the soft-lock balance, and\n * records the payment. Returns 402 if the payer has insufficient balance.\n */\n payForOp(params: PayForOpParams): Promise<EscrowPayResult>;\n}\n\n/**\n * Creates a client for the gateway escrow endpoints.\n *\n * @param baseUrl - Base URL of the DP RPC gateway\n * (e.g. `\"https://dp.vana.org\"`). Trailing slashes are trimmed.\n *\n * @example\n * ```typescript\n * import {\n * createEscrowGatewayClient,\n * genericPaymentDomain,\n * GENERIC_PAYMENT_TYPES,\n * } from \"@opendatalabs/vana-sdk/node\";\n *\n * const escrow = createEscrowGatewayClient(\"https://dp.vana.org\");\n *\n * // 1. Submit your deposit tx hash after broadcasting depositNative on-chain\n * const deposit = await escrow.submitDeposit({ txHash: \"0xabc…\" });\n *\n * // 2. Force-sync and read the updated balance\n * const { balances } = await escrow.syncEscrowBalance(\"0xpayerAddress\");\n *\n * // 3. Sign and authorize a grant payment\n * const sig = await walletClient.signTypedData({\n * domain: genericPaymentDomain(1480, \"0xEscrowContract\"),\n * types: GENERIC_PAYMENT_TYPES,\n * primaryType: \"GenericPayment\",\n * message: {\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: 1000000000000000000n,\n * paymentNonce: 1n,\n * },\n * });\n * const result = await escrow.payForOp({\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: \"1000000000000000000\",\n * paymentNonce: \"1\",\n * signature: sig,\n * });\n * ```\n */\nexport function createEscrowGatewayClient(\n baseUrl: string,\n): EscrowGatewayClient {\n const base = baseUrl.replace(/\\/+$/, \"\");\n\n async function throwOnError(res: Response, context: string): Promise<void> {\n if (!res.ok) {\n let detail = \"\";\n try {\n const body = (await res.json()) as { error?: string };\n if (body.error) detail = `: ${body.error}`;\n } catch {\n // Ignore JSON parse errors; use status text only.\n }\n throw new Error(\n `Escrow gateway error (${context}): ${res.status} ${res.statusText}${detail}`,\n );\n }\n }\n\n return {\n async submitDeposit({ txHash }) {\n const res = await fetch(`${base}/v1/escrow/deposit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ txHash }),\n });\n // 202 Accepted and 200 OK are both success states for deposit submission.\n if (res.status !== 200 && res.status !== 202) {\n await throwOnError(res, \"POST /v1/escrow/deposit\");\n }\n return res.json() as Promise<DepositSubmissionResult>;\n },\n\n async getEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance?account=${encodeURIComponent(account)}`,\n );\n await throwOnError(res, \"GET /v1/escrow/balance\");\n return res.json() as Promise<EscrowBalanceResult>;\n },\n\n async syncEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance/sync?account=${encodeURIComponent(account)}`,\n { method: \"POST\" },\n );\n await throwOnError(res, \"POST /v1/escrow/balance/sync\");\n return res.json() as Promise<EscrowBalanceSyncResult>;\n },\n\n async payForOp({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n signature,\n accessRecord,\n }) {\n const res = await fetch(`${base}/v1/escrow/pay`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${signature}`,\n },\n body: JSON.stringify({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n ...(accessRecord ? { accessRecord } : {}),\n }),\n });\n await throwOnError(res, \"POST /v1/escrow/pay\");\n return res.json() as Promise<EscrowPayResult>;\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/protocol/escrow.ts"],"sourcesContent":["/**\n * DPv2 escrow payment helpers.\n *\n * Covers the three-phase flow used by builders to pay for data access:\n *\n * 1. **Deposit** — call `depositNative` or `depositToken` on the\n * DataPortabilityEscrow contract, then notify the gateway.\n * 2. **Balance** — read or force-sync the gateway's off-chain credit view.\n * 3. **Pay** — sign a `GenericPayment` EIP-712 message and POST it to the\n * gateway's `/v1/escrow/pay` endpoint.\n *\n * The gateway is the authority on balances; the on-chain contract is the\n * authority on what has been settled. Nothing in this module touches the\n * chain directly — signing is done by the caller's wallet.\n *\n * @category Protocol\n * @module escrow\n */\n\nexport {\n buildWithdrawAuthorizationTypedData,\n withdrawAuthorizationDomain,\n WITHDRAW_AUTHORIZATION_TYPES,\n type WithdrawAuthorizationMessage,\n} from \"./eip712\";\nimport type { TypedDataDomain } from \"viem\";\nimport { isHex } from \"viem\";\n\n// ---------------------------------------------------------------------------\n// EIP-712 — GenericPayment\n// ---------------------------------------------------------------------------\n\n/**\n * EIP-712 typed-data types for a generic op payment.\n *\n * The gateway verifies that the recovered signer == `payerAddress` and that\n * the (payer, paymentNonce) pair has not been seen before. Use a\n * monotonically-increasing nonce; the first payment for any payer should\n * start at 1.\n */\nexport const GENERIC_PAYMENT_TYPES = {\n GenericPayment: [\n { name: \"payerAddress\", type: \"address\" },\n { name: \"opType\", type: \"string\" },\n { name: \"opId\", type: \"bytes32\" },\n { name: \"asset\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"paymentNonce\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * EIP-712 message payload for a generic op payment.\n *\n * - `opType` is `\"grant\"` for legacy grant lifecycle payments or\n * `\"data_access\"` for a standalone receipt-bound read.\n * - `opId` is the bytes32 id of the operation being paid for: the grant id for\n * `\"grant\"`, or `accessRecord.recordId` for `\"data_access\"`.\n * - `asset` is the ERC-20 token address, or the zero address for native VANA.\n * - `amount` is the total amount in base units (wei for VANA). Must match the\n * sum the gateway expects for the current lifecycle of the op.\n * - `paymentNonce` must be a positive integer unique per `payerAddress`. Use 1\n * for the first payment; increment by at least 1 for each subsequent call.\n */\nexport interface GenericPaymentMessage {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n amount: bigint;\n paymentNonce: bigint;\n}\n\n/**\n * Returns the EIP-712 domain for signing a `GenericPayment` message.\n *\n * The verifying contract is the `DataPortabilityEscrow` contract; all gateway\n * deployments share the same domain name and version.\n *\n * @param chainId - Chain ID of the Vana network (e.g. 1480 mainnet, 14800 testnet).\n * @param escrowContract - Deployed address of DataPortabilityEscrow.\n */\nexport function genericPaymentDomain(\n chainId: number,\n escrowContract: `0x${string}`,\n): TypedDataDomain {\n return {\n name: \"Vana Data Portability\",\n version: \"1\",\n chainId,\n verifyingContract: escrowContract,\n };\n}\n\n// ---------------------------------------------------------------------------\n// On-chain deposit ABI fragments\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal ABI for the two deposit entry points on `DataPortabilityEscrow`.\n *\n * - `depositNative(address account)` payable — credits native VANA.\n * - `depositToken(address account, address token, uint256 amount)` — credits\n * an ERC-20 token (caller must have pre-approved the escrow contract).\n *\n * Pass this to viem's `writeContract` or encode it manually.\n */\nexport const ESCROW_DEPOSIT_ABI = [\n {\n type: \"function\",\n name: \"depositNative\",\n stateMutability: \"payable\",\n inputs: [{ name: \"account\", type: \"address\" }],\n outputs: [],\n },\n {\n type: \"function\",\n name: \"depositToken\",\n stateMutability: \"nonpayable\",\n inputs: [\n { name: \"account\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n outputs: [],\n },\n] as const;\n\n/**\n * The zero address used by the DataPortabilityEscrow contract to represent\n * native VANA in `asset` fields of events and balance responses.\n */\nexport const NATIVE_ASSET_ADDRESS =\n \"0x0000000000000000000000000000000000000000\" as const;\n\n// ---------------------------------------------------------------------------\n// Gateway API client\n// ---------------------------------------------------------------------------\n\n/**\n * Per-asset balance entry returned by the gateway's escrow balance endpoints.\n *\n * - `balance` — gross finalized credit (deposits credited so far).\n * - `pendingAmount` — sum of submitted deposits not yet confirmed.\n * - `authorizedAmount` — sum of all in-flight payments authorized by\n * `/v1/escrow/pay` (soft-lock). May include payments not yet settled\n * on-chain.\n * - `withdrawingAmount` — sum of in-flight withdrawal reservations.\n * - `availableAmount` — `max(balance − authorizedAmount − withdrawingAmount, 0)`.\n * This is what the account can still authorize or withdraw.\n */\nexport interface EscrowBalanceEntry {\n asset: string;\n balance: string;\n pendingAmount: string;\n authorizedAmount: string;\n withdrawingAmount: string;\n availableAmount: string;\n /** Minimum withdrawal amount currently accepted for this asset, if configured. */\n withdrawalMinimumAmount: string | null;\n updatedAt: string | null;\n}\n\nexport interface SubmittedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FinalizedDepositEntry {\n txHash: string;\n finalizedAt: string | null;\n blockNumber: string | null;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FailedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n lastError: string | null;\n}\n\n/** Full balance read response from `GET /v1/escrow/balance`. */\nexport interface EscrowBalanceResult {\n account: string;\n balances: EscrowBalanceEntry[];\n deposits: {\n submitted: SubmittedDepositEntry[];\n finalized: FinalizedDepositEntry[];\n failed: FailedDepositEntry[];\n };\n}\n\n/**\n * Response from `POST /v1/escrow/balance/sync`.\n *\n * Extends {@link EscrowBalanceResult} with a `sync` summary of what the\n * lazy-confirmation pass did.\n */\nexport interface EscrowBalanceSyncResult extends EscrowBalanceResult {\n sync:\n | {\n scanned: number;\n finalized: number;\n stillPending: number;\n failed: number;\n }\n | { skipped: true };\n}\n\n/** Response from `POST /v1/escrow/deposit`. */\nexport interface DepositSubmissionResult {\n success: true;\n txHash: string;\n account: string;\n status: \"submitted\" | \"finalized\" | \"failed\";\n blockNumber?: string | null;\n submittedAt: string;\n finalizedAt?: string | null;\n lastError?: string | null;\n}\n\n/** Breakdown returned by a successful `POST /v1/escrow/pay`. */\nexport interface PaymentBreakdown {\n registrationFee: string;\n dataAccessFee: string;\n /** True when this call settled the registration fee for the op. */\n registrationPaid: boolean;\n}\n\n/** Response from `POST /v1/escrow/pay`. */\nexport interface EscrowPayResult {\n success: true;\n opType: string;\n opId: string;\n payerAddress: string;\n asset: string;\n amount: string;\n breakdown: PaymentBreakdown;\n paymentNonce: string;\n paidAt: string;\n}\n\ninterface EscrowWithdrawalResponseBase {\n account: `0x${string}`;\n asset: `0x${string}`;\n amount: string;\n withdrawNonce: string;\n deadline: string;\n}\n\n/** A persisted authorization whose transaction has not been broadcast yet. */\nexport interface EscrowWithdrawalSubmittedWithoutTransaction extends EscrowWithdrawalResponseBase {\n success: true;\n status: \"submitted\";\n txHash: null;\n message: string;\n}\n\n/** A withdrawal with a persisted transaction that is awaiting reconciliation. */\nexport interface EscrowWithdrawalSubmittedWithTransaction {\n success: true;\n status: \"submitted\";\n txHash: `0x${string}`;\n message: string;\n // A provisional-revert response contains only txHash, blockNumber, and\n // message. Ordinary submissions include the signed-intent fields.\n account?: `0x${string}`;\n asset?: `0x${string}`;\n amount?: string;\n withdrawNonce?: string;\n deadline?: string;\n blockNumber?: string;\n}\n\n/** A withdrawal that the gateway has accepted but not yet confirmed. */\nexport type EscrowWithdrawalSubmittedResult =\n | EscrowWithdrawalSubmittedWithoutTransaction\n | EscrowWithdrawalSubmittedWithTransaction;\n\n/** A withdrawal whose on-chain debit has reached the named lifecycle state. */\nexport interface EscrowWithdrawalSettledResult extends EscrowWithdrawalResponseBase {\n success: true;\n status: \"confirmed\" | \"finalized\";\n txHash: `0x${string}`;\n blockNumber: string | null;\n}\n\n/** Successful lifecycle responses from `POST /v1/escrow/withdraw`. */\nexport type EscrowWithdrawalResult =\n | EscrowWithdrawalSubmittedResult\n | EscrowWithdrawalSettledResult;\n\n/** Terminal or retryable withdrawal lifecycle state returned with a non-2xx status. */\nexport interface EscrowWithdrawalFailureResult extends EscrowWithdrawalResponseBase {\n success: false;\n status: \"retryable\" | \"reorged\" | \"failed\";\n error: string;\n txHash: `0x${string}` | null;\n blockNumber?: string | null;\n}\n\nexport type EscrowWithdrawalRejectionCode =\n | \"below_minimum\"\n | \"deadline_too_far\"\n | \"expired\"\n | \"insufficient_available\"\n | \"stale_nonce\";\n\n/** Definite pre-acceptance rejection. No durable withdrawal intent was created. */\nexport interface EscrowWithdrawalRejectedResult extends EscrowWithdrawalResponseBase {\n success: false;\n status: \"rejected\";\n code: EscrowWithdrawalRejectionCode;\n error: string;\n balance?: string;\n authorizedAmount?: string;\n withdrawingAmount?: string;\n availableAmount?: string;\n requestedAmount?: string;\n minimumAmount?: string;\n}\n\n/**\n * A typed non-2xx gateway lifecycle response.\n *\n * `retryable` means resend the exact signed intent. `reorged` and `failed`\n * require a newly signed authorization with a new nonce.\n */\nexport class EscrowWithdrawalLifecycleError extends Error {\n override readonly name = \"EscrowWithdrawalLifecycleError\";\n\n constructor(\n readonly httpStatus: number,\n readonly result: EscrowWithdrawalFailureResult,\n ) {\n super(result.error);\n }\n}\n\n/** A typed non-2xx gateway rejection before a withdrawal intent is accepted. */\nexport class EscrowWithdrawalRejectionError extends Error {\n override readonly name = \"EscrowWithdrawalRejectionError\";\n\n constructor(\n readonly httpStatus: number,\n readonly result: EscrowWithdrawalRejectedResult,\n ) {\n super(result.error);\n }\n}\n\n/**\n * Parameters for submitting a deposit tx hash to the gateway.\n *\n * The gateway will decode the `account` from the tx's calldata and\n * credit the identified account once the tx reaches the configured\n * confirmation depth.\n */\nexport interface SubmitDepositParams {\n /** 0x-prefixed 32-byte transaction hash. */\n txHash: `0x${string}`;\n}\n\n/**\n * Parameters for the generic op payment endpoint (`POST /v1/escrow/pay`).\n *\n * The `signature` is an EIP-712 signature over a `GenericPayment` message\n * (see {@link GENERIC_PAYMENT_TYPES} and {@link genericPaymentDomain}).\n * Build and sign the typed data with your wallet before calling\n * {@link EscrowGatewayClient.payForOp}.\n */\nexport interface PayForOpParams {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n /** Decimal string representation of the uint256 amount. */\n amount: string;\n /** Decimal string representation of the uint256 nonce. */\n paymentNonce: string;\n /** 0x-prefixed 65-byte EIP-712 signature hex string. */\n signature: `0x${string}`;\n /**\n * Optional data-access receipt carried by x402 challenges.\n *\n * The gateway verifies its server signature; this type only describes the\n * wire shape.\n */\n accessRecord?: EscrowAccessRecord;\n}\n\n/**\n * Parameters for `POST /v1/escrow/withdraw`.\n *\n * `withdrawNonce` and `deadline` are caller-supplied decimal uint256 strings.\n * The SDK intentionally does not generate a nonce: retrying safely requires a\n * durable caller-owned nonce source and the exact same signed payload.\n */\nexport interface WithdrawFromEscrowParams {\n account: `0x${string}`;\n asset: `0x${string}`;\n amount: string;\n withdrawNonce: string;\n deadline: string;\n signature: `0x${string}`;\n}\n\n/**\n * Response from `GET /v1/escrow/withdraw/nonce`.\n *\n * The gateway provides a read-only snapshot of the account's withdrawal nonce\n * state. This is **not** a reservation; multiple concurrent callers will see\n * the same `nextWithdrawNonce`. To reduce staleness risk, query immediately before\n * signing/submitting the withdrawal authorization. However, `stale_nonce` errors can\n * still occur under concurrent withdrawal attempts; if rejected, re-query and re-sign.\n *\n * Use `nextWithdrawNonce` in the signed withdrawal authorization; `lastWithdrawNonce`\n * is provided for reference and diagnostics.\n */\nexport interface WithdrawNonceResponse {\n success: true;\n account: `0x${string}`;\n chainId: string;\n lastWithdrawNonce: string | null;\n nextWithdrawNonce: string;\n}\n\n/** Wire shape of a receipt whose server signature the gateway verifies. */\nexport interface EscrowAccessRecord {\n dataPointId: `0x${string}`;\n version: string;\n accessor: `0x${string}`;\n recordId: `0x${string}`;\n signature: `0x${string}`;\n}\n\n/**\n * Minimal client for the gateway's escrow endpoints.\n *\n * Construct with {@link createEscrowGatewayClient}.\n */\nexport interface EscrowGatewayClient {\n /**\n * Notify the gateway of a submitted deposit transaction.\n *\n * The gateway decodes the credited account from the on-chain tx calldata\n * and starts tracking the deposit. Call this immediately after your\n * `depositNative` or `depositToken` tx is broadcast (it accepts pending\n * mempool txs). Returns `202` while the tx awaits confirmation.\n */\n submitDeposit(params: SubmitDepositParams): Promise<DepositSubmissionResult>;\n\n /**\n * Read the current escrow balance for an account.\n *\n * Pure read — no chain calls. To force a reconciliation pass first,\n * use {@link syncEscrowBalance}.\n */\n getEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceResult>;\n\n /**\n * Force a reconciliation pass then return the updated balance.\n *\n * Triggers the gateway's lazy-confirmation worker for the account — any\n * submitted deposits that have reached the configured confirmation level\n * are credited before the balance is returned. Prefer this over\n * {@link getEscrowBalance} when you need a fresh view after a deposit.\n */\n syncEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceSyncResult>;\n\n /**\n * Authorize a payment against the payer's escrow balance.\n *\n * The caller must:\n * 1. Assemble a {@link GenericPaymentMessage}.\n * 2. Sign it with `signTypedData` using {@link GENERIC_PAYMENT_TYPES} and\n * the domain from {@link genericPaymentDomain}.\n * 3. Pass the message fields + signature here.\n *\n * The gateway verifies the signature, checks the soft-lock balance, and\n * records the payment. Returns 402 if the payer has insufficient balance.\n */\n payForOp(params: PayForOpParams): Promise<EscrowPayResult>;\n\n /**\n * Submit or reconcile a signed withdrawal authorization.\n *\n * The gateway decides which signers may authorize an account. For example,\n * it may accept the account itself or the confirmed owner of a registered\n * app account.\n *\n * Retry a `submitted` result with the exact same parameters. Do not replace\n * `withdrawNonce`, `deadline`, or signature unless starting a new intent.\n */\n withdraw(params: WithdrawFromEscrowParams): Promise<EscrowWithdrawalResult>;\n\n /**\n * Read the authoritative next withdrawal nonce for an account.\n *\n * The gateway is the authority on what nonce to use; use the value from\n * `nextWithdrawNonce` when signing a withdrawal authorization.\n *\n * Do NOT generate or cache nonces client-side; concurrent callers cannot be\n * safely coordinated without durable shared state. Query this endpoint immediately\n * before signing/submitting to reduce staleness risk. However, `stale_nonce` errors\n * can still occur; if rejected, re-query and re-sign.\n */\n getWithdrawNonce(account: `0x${string}`): Promise<WithdrawNonceResponse>;\n}\n\n/** The only gateway capability required by direct data-access payment flows. */\nexport type EscrowPaymentClient = Pick<EscrowGatewayClient, \"payForOp\">;\n\n/**\n * Creates a client for the gateway escrow endpoints.\n *\n * @param baseUrl - Base URL of the DP RPC gateway\n * (e.g. `\"https://dp.vana.org\"`). Trailing slashes are trimmed.\n *\n * @example\n * ```typescript\n * import {\n * createEscrowGatewayClient,\n * genericPaymentDomain,\n * GENERIC_PAYMENT_TYPES,\n * } from \"@opendatalabs/vana-sdk/node\";\n *\n * const escrow = createEscrowGatewayClient(\"https://dp.vana.org\");\n *\n * // 1. Submit your deposit tx hash after broadcasting depositNative on-chain\n * const deposit = await escrow.submitDeposit({ txHash: \"0xabc…\" });\n *\n * // 2. Force-sync and read the updated balance\n * const { balances } = await escrow.syncEscrowBalance(\"0xpayerAddress\");\n *\n * // 3. Sign and authorize a grant payment\n * const sig = await walletClient.signTypedData({\n * domain: genericPaymentDomain(1480, \"0xEscrowContract\"),\n * types: GENERIC_PAYMENT_TYPES,\n * primaryType: \"GenericPayment\",\n * message: {\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: 1000000000000000000n,\n * paymentNonce: 1n,\n * },\n * });\n * const result = await escrow.payForOp({\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: \"1000000000000000000\",\n * paymentNonce: \"1\",\n * signature: sig,\n * });\n * ```\n */\nexport function createEscrowGatewayClient(\n baseUrl: string,\n): EscrowGatewayClient {\n const base = baseUrl.replace(/\\/+$/, \"\");\n\n async function throwOnError(res: Response, context: string): Promise<void> {\n if (!res.ok) {\n let detail = \"\";\n try {\n const body = (await res.json()) as { error?: string };\n if (body.error) detail = `: ${body.error}`;\n } catch {\n // Ignore JSON parse errors; use status text only.\n }\n throw new Error(\n `Escrow gateway error (${context}): ${res.status} ${res.statusText}${detail}`,\n );\n }\n }\n\n async function throwOnWithdrawError(res: Response): Promise<void> {\n if (res.ok) return;\n\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n throw new Error(\n `Escrow gateway error (POST /v1/escrow/withdraw): ${res.status} ${res.statusText}`,\n );\n }\n\n if (isEscrowWithdrawalFailureResult(body)) {\n throw new EscrowWithdrawalLifecycleError(res.status, body);\n }\n if (isEscrowWithdrawalRejectedResult(body)) {\n throw new EscrowWithdrawalRejectionError(res.status, body);\n }\n\n const error = getGatewayErrorMessage(body);\n throw new Error(\n `Escrow gateway error (POST /v1/escrow/withdraw): ${res.status} ${res.statusText}${error ? `: ${error}` : \"\"}`,\n );\n }\n\n return {\n async submitDeposit({ txHash }) {\n const res = await fetch(`${base}/v1/escrow/deposit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ txHash }),\n });\n // 202 Accepted and 200 OK are both success states for deposit submission.\n if (res.status !== 200 && res.status !== 202) {\n await throwOnError(res, \"POST /v1/escrow/deposit\");\n }\n return res.json() as Promise<DepositSubmissionResult>;\n },\n\n async getEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance?account=${encodeURIComponent(account)}`,\n );\n await throwOnError(res, \"GET /v1/escrow/balance\");\n return res.json() as Promise<EscrowBalanceResult>;\n },\n\n async syncEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance/sync?account=${encodeURIComponent(account)}`,\n { method: \"POST\" },\n );\n await throwOnError(res, \"POST /v1/escrow/balance/sync\");\n return res.json() as Promise<EscrowBalanceSyncResult>;\n },\n\n async payForOp({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n signature,\n accessRecord,\n }) {\n const res = await fetch(`${base}/v1/escrow/pay`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${signature}`,\n },\n body: JSON.stringify({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n ...(accessRecord ? { accessRecord } : {}),\n }),\n });\n await throwOnError(res, \"POST /v1/escrow/pay\");\n return res.json() as Promise<EscrowPayResult>;\n },\n\n async withdraw({\n account,\n asset,\n amount,\n withdrawNonce,\n deadline,\n signature,\n }) {\n const res = await fetch(`${base}/v1/escrow/withdraw`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${signature}`,\n },\n body: JSON.stringify({\n account,\n asset,\n amount,\n withdrawNonce,\n deadline,\n }),\n });\n await throwOnWithdrawError(res);\n return res.json() as Promise<EscrowWithdrawalResult>;\n },\n\n async getWithdrawNonce(account) {\n const res = await fetch(\n `${base}/v1/escrow/withdraw/nonce?account=${encodeURIComponent(account)}`,\n { cache: \"no-store\" },\n );\n await throwOnError(res, \"GET /v1/escrow/withdraw/nonce\");\n const body = (await res.json()) as unknown;\n if (!isWithdrawNonceResponse(body, account)) {\n throw new Error(\n \"GET /v1/escrow/withdraw/nonce: invalid response structure\",\n );\n }\n return body;\n },\n };\n}\n\nfunction getGatewayErrorMessage(body: unknown): string | undefined {\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"error\" in body &&\n typeof body.error === \"string\"\n ) {\n return body.error;\n }\n return undefined;\n}\n\nfunction isEscrowWithdrawalFailureResult(\n body: unknown,\n): body is EscrowWithdrawalFailureResult {\n if (typeof body !== \"object\" || body === null) return false;\n const value = body as Record<string, unknown>;\n return (\n value.success === false &&\n (value.status === \"retryable\" ||\n value.status === \"reorged\" ||\n value.status === \"failed\") &&\n typeof value.error === \"string\" &&\n isAddressHex(value.account) &&\n isAddressHex(value.asset) &&\n isUint256Decimal(value.amount) &&\n isUint256Decimal(value.withdrawNonce) &&\n isUint256Decimal(value.deadline) &&\n (isHash(value.txHash) || value.txHash === null) &&\n (!(\"blockNumber\" in value) || isBlockNumber(value.blockNumber))\n );\n}\n\nfunction isEscrowWithdrawalRejectedResult(\n body: unknown,\n): body is EscrowWithdrawalRejectedResult {\n if (typeof body !== \"object\" || body === null) return false;\n const value = body as Record<string, unknown>;\n return (\n value.success === false &&\n value.status === \"rejected\" &&\n isWithdrawalRejectionCode(value.code) &&\n typeof value.error === \"string\" &&\n isAddressHex(value.account) &&\n isAddressHex(value.asset) &&\n isUint256Decimal(value.amount) &&\n isUint256Decimal(value.withdrawNonce) &&\n isUint256Decimal(value.deadline) &&\n optionalUint256Decimal(value.balance) &&\n optionalUint256Decimal(value.authorizedAmount) &&\n optionalUint256Decimal(value.withdrawingAmount) &&\n optionalUint256Decimal(value.availableAmount) &&\n optionalUint256Decimal(value.requestedAmount) &&\n optionalUint256Decimal(value.minimumAmount)\n );\n}\n\nfunction isWithdrawalRejectionCode(\n value: unknown,\n): value is EscrowWithdrawalRejectionCode {\n return (\n value === \"below_minimum\" ||\n value === \"deadline_too_far\" ||\n value === \"expired\" ||\n value === \"insufficient_available\" ||\n value === \"stale_nonce\"\n );\n}\n\nfunction optionalUint256Decimal(value: unknown): boolean {\n return value === undefined || isUint256Decimal(value);\n}\n\nfunction isAddressHex(value: unknown): value is `0x${string}` {\n return (\n typeof value === \"string\" &&\n isHex(value, { strict: true }) &&\n value.length === 42\n );\n}\n\nfunction isHash(value: unknown): value is `0x${string}` {\n return (\n typeof value === \"string\" &&\n isHex(value, { strict: true }) &&\n value.length === 66\n );\n}\n\nfunction isUint256Decimal(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 78) {\n return false;\n }\n if (!/^(0|[1-9]\\d*)$/.test(value)) return false;\n return BigInt(value) <= 2n ** 256n - 1n;\n}\n\nfunction isBlockNumber(value: unknown): value is string | null {\n return value === null || isUint256Decimal(value);\n}\n\nfunction isWithdrawNonceResponse(\n body: unknown,\n requestedAccount: `0x${string}`,\n): body is WithdrawNonceResponse {\n if (typeof body !== \"object\" || body === null) return false;\n const value = body as Record<string, unknown>;\n\n if (value.success !== true) return false;\n\n if (!isAddressHex(value.account)) return false;\n if (value.account.toLowerCase() !== requestedAccount.toLowerCase())\n return false;\n if (typeof value.chainId !== \"string\") return false;\n if (!/^(0|[1-9]\\d*)$/.test(value.chainId)) return false;\n\n const isLastNull = value.lastWithdrawNonce === null;\n const lastNonceValid =\n isLastNull || isUint256Decimal(value.lastWithdrawNonce);\n if (!lastNonceValid) return false;\n\n if (!isUint256Decimal(value.nextWithdrawNonce)) return false;\n\n // Validate nonce pair consistency: nextWithdrawNonce must be exactly lastWithdrawNonce + 1\n // or exactly 1 if lastWithdrawNonce is null\n if (isLastNull) {\n return value.nextWithdrawNonce === \"1\";\n }\n\n const lastNonce = BigInt(value.lastWithdrawNonce as string);\n const nextNonce = BigInt(value.nextWithdrawNonce as string);\n const expectedNextNonce = lastNonce + 1n;\n\n // Ensure no overflow (nextNonce must still be within uint256)\n if (expectedNextNonce > 2n ** 256n - 1n) return false;\n\n return nextNonce === expectedNextNonce;\n}\n"],"mappings":"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","names":[]}
@@ -16,6 +16,7 @@
16
16
  * @category Protocol
17
17
  * @module escrow
18
18
  */
19
+ export { buildWithdrawAuthorizationTypedData, withdrawAuthorizationDomain, WITHDRAW_AUTHORIZATION_TYPES, type WithdrawAuthorizationMessage, } from "./eip712.js";
19
20
  import type { TypedDataDomain } from "viem";
20
21
  /**
21
22
  * EIP-712 typed-data types for a generic op payment.
@@ -124,15 +125,19 @@ export declare const NATIVE_ASSET_ADDRESS: "0x0000000000000000000000000000000000
124
125
  * - `authorizedAmount` — sum of all in-flight payments authorized by
125
126
  * `/v1/escrow/pay` (soft-lock). May include payments not yet settled
126
127
  * on-chain.
127
- * - `availableAmount` — `max(balance authorizedAmount, 0)`. This is what
128
- * the payer can authorize before the gateway rejects with 402.
128
+ * - `withdrawingAmount` — sum of in-flight withdrawal reservations.
129
+ * - `availableAmount` `max(balance authorizedAmount withdrawingAmount, 0)`.
130
+ * This is what the account can still authorize or withdraw.
129
131
  */
130
132
  export interface EscrowBalanceEntry {
131
133
  asset: string;
132
134
  balance: string;
133
135
  pendingAmount: string;
134
136
  authorizedAmount: string;
137
+ withdrawingAmount: string;
135
138
  availableAmount: string;
139
+ /** Minimum withdrawal amount currently accepted for this asset, if configured. */
140
+ withdrawalMinimumAmount: string | null;
136
141
  updatedAt: string | null;
137
142
  }
138
143
  export interface SubmittedDepositEntry {
@@ -211,6 +216,85 @@ export interface EscrowPayResult {
211
216
  paymentNonce: string;
212
217
  paidAt: string;
213
218
  }
219
+ interface EscrowWithdrawalResponseBase {
220
+ account: `0x${string}`;
221
+ asset: `0x${string}`;
222
+ amount: string;
223
+ withdrawNonce: string;
224
+ deadline: string;
225
+ }
226
+ /** A persisted authorization whose transaction has not been broadcast yet. */
227
+ export interface EscrowWithdrawalSubmittedWithoutTransaction extends EscrowWithdrawalResponseBase {
228
+ success: true;
229
+ status: "submitted";
230
+ txHash: null;
231
+ message: string;
232
+ }
233
+ /** A withdrawal with a persisted transaction that is awaiting reconciliation. */
234
+ export interface EscrowWithdrawalSubmittedWithTransaction {
235
+ success: true;
236
+ status: "submitted";
237
+ txHash: `0x${string}`;
238
+ message: string;
239
+ account?: `0x${string}`;
240
+ asset?: `0x${string}`;
241
+ amount?: string;
242
+ withdrawNonce?: string;
243
+ deadline?: string;
244
+ blockNumber?: string;
245
+ }
246
+ /** A withdrawal that the gateway has accepted but not yet confirmed. */
247
+ export type EscrowWithdrawalSubmittedResult = EscrowWithdrawalSubmittedWithoutTransaction | EscrowWithdrawalSubmittedWithTransaction;
248
+ /** A withdrawal whose on-chain debit has reached the named lifecycle state. */
249
+ export interface EscrowWithdrawalSettledResult extends EscrowWithdrawalResponseBase {
250
+ success: true;
251
+ status: "confirmed" | "finalized";
252
+ txHash: `0x${string}`;
253
+ blockNumber: string | null;
254
+ }
255
+ /** Successful lifecycle responses from `POST /v1/escrow/withdraw`. */
256
+ export type EscrowWithdrawalResult = EscrowWithdrawalSubmittedResult | EscrowWithdrawalSettledResult;
257
+ /** Terminal or retryable withdrawal lifecycle state returned with a non-2xx status. */
258
+ export interface EscrowWithdrawalFailureResult extends EscrowWithdrawalResponseBase {
259
+ success: false;
260
+ status: "retryable" | "reorged" | "failed";
261
+ error: string;
262
+ txHash: `0x${string}` | null;
263
+ blockNumber?: string | null;
264
+ }
265
+ export type EscrowWithdrawalRejectionCode = "below_minimum" | "deadline_too_far" | "expired" | "insufficient_available" | "stale_nonce";
266
+ /** Definite pre-acceptance rejection. No durable withdrawal intent was created. */
267
+ export interface EscrowWithdrawalRejectedResult extends EscrowWithdrawalResponseBase {
268
+ success: false;
269
+ status: "rejected";
270
+ code: EscrowWithdrawalRejectionCode;
271
+ error: string;
272
+ balance?: string;
273
+ authorizedAmount?: string;
274
+ withdrawingAmount?: string;
275
+ availableAmount?: string;
276
+ requestedAmount?: string;
277
+ minimumAmount?: string;
278
+ }
279
+ /**
280
+ * A typed non-2xx gateway lifecycle response.
281
+ *
282
+ * `retryable` means resend the exact signed intent. `reorged` and `failed`
283
+ * require a newly signed authorization with a new nonce.
284
+ */
285
+ export declare class EscrowWithdrawalLifecycleError extends Error {
286
+ readonly httpStatus: number;
287
+ readonly result: EscrowWithdrawalFailureResult;
288
+ readonly name = "EscrowWithdrawalLifecycleError";
289
+ constructor(httpStatus: number, result: EscrowWithdrawalFailureResult);
290
+ }
291
+ /** A typed non-2xx gateway rejection before a withdrawal intent is accepted. */
292
+ export declare class EscrowWithdrawalRejectionError extends Error {
293
+ readonly httpStatus: number;
294
+ readonly result: EscrowWithdrawalRejectedResult;
295
+ readonly name = "EscrowWithdrawalRejectionError";
296
+ constructor(httpStatus: number, result: EscrowWithdrawalRejectedResult);
297
+ }
214
298
  /**
215
299
  * Parameters for submitting a deposit tx hash to the gateway.
216
300
  *
@@ -249,6 +333,40 @@ export interface PayForOpParams {
249
333
  */
250
334
  accessRecord?: EscrowAccessRecord;
251
335
  }
336
+ /**
337
+ * Parameters for `POST /v1/escrow/withdraw`.
338
+ *
339
+ * `withdrawNonce` and `deadline` are caller-supplied decimal uint256 strings.
340
+ * The SDK intentionally does not generate a nonce: retrying safely requires a
341
+ * durable caller-owned nonce source and the exact same signed payload.
342
+ */
343
+ export interface WithdrawFromEscrowParams {
344
+ account: `0x${string}`;
345
+ asset: `0x${string}`;
346
+ amount: string;
347
+ withdrawNonce: string;
348
+ deadline: string;
349
+ signature: `0x${string}`;
350
+ }
351
+ /**
352
+ * Response from `GET /v1/escrow/withdraw/nonce`.
353
+ *
354
+ * The gateway provides a read-only snapshot of the account's withdrawal nonce
355
+ * state. This is **not** a reservation; multiple concurrent callers will see
356
+ * the same `nextWithdrawNonce`. To reduce staleness risk, query immediately before
357
+ * signing/submitting the withdrawal authorization. However, `stale_nonce` errors can
358
+ * still occur under concurrent withdrawal attempts; if rejected, re-query and re-sign.
359
+ *
360
+ * Use `nextWithdrawNonce` in the signed withdrawal authorization; `lastWithdrawNonce`
361
+ * is provided for reference and diagnostics.
362
+ */
363
+ export interface WithdrawNonceResponse {
364
+ success: true;
365
+ account: `0x${string}`;
366
+ chainId: string;
367
+ lastWithdrawNonce: string | null;
368
+ nextWithdrawNonce: string;
369
+ }
252
370
  /** Wire shape of a receipt whose server signature the gateway verifies. */
253
371
  export interface EscrowAccessRecord {
254
372
  dataPointId: `0x${string}`;
@@ -301,7 +419,32 @@ export interface EscrowGatewayClient {
301
419
  * records the payment. Returns 402 if the payer has insufficient balance.
302
420
  */
303
421
  payForOp(params: PayForOpParams): Promise<EscrowPayResult>;
422
+ /**
423
+ * Submit or reconcile a signed withdrawal authorization.
424
+ *
425
+ * The gateway decides which signers may authorize an account. For example,
426
+ * it may accept the account itself or the confirmed owner of a registered
427
+ * app account.
428
+ *
429
+ * Retry a `submitted` result with the exact same parameters. Do not replace
430
+ * `withdrawNonce`, `deadline`, or signature unless starting a new intent.
431
+ */
432
+ withdraw(params: WithdrawFromEscrowParams): Promise<EscrowWithdrawalResult>;
433
+ /**
434
+ * Read the authoritative next withdrawal nonce for an account.
435
+ *
436
+ * The gateway is the authority on what nonce to use; use the value from
437
+ * `nextWithdrawNonce` when signing a withdrawal authorization.
438
+ *
439
+ * Do NOT generate or cache nonces client-side; concurrent callers cannot be
440
+ * safely coordinated without durable shared state. Query this endpoint immediately
441
+ * before signing/submitting to reduce staleness risk. However, `stale_nonce` errors
442
+ * can still occur; if rejected, re-query and re-sign.
443
+ */
444
+ getWithdrawNonce(account: `0x${string}`): Promise<WithdrawNonceResponse>;
304
445
  }
446
+ /** The only gateway capability required by direct data-access payment flows. */
447
+ export type EscrowPaymentClient = Pick<EscrowGatewayClient, "payForOp">;
305
448
  /**
306
449
  * Creates a client for the gateway escrow endpoints.
307
450
  *
@@ -1,3 +1,9 @@
1
+ import {
2
+ buildWithdrawAuthorizationTypedData,
3
+ withdrawAuthorizationDomain,
4
+ WITHDRAW_AUTHORIZATION_TYPES
5
+ } from "./eip712.js";
6
+ import { isHex } from "viem";
1
7
  const GENERIC_PAYMENT_TYPES = {
2
8
  GenericPayment: [
3
9
  { name: "payerAddress", type: "address" },
@@ -37,6 +43,26 @@ const ESCROW_DEPOSIT_ABI = [
37
43
  }
38
44
  ];
39
45
  const NATIVE_ASSET_ADDRESS = "0x0000000000000000000000000000000000000000";
46
+ class EscrowWithdrawalLifecycleError extends Error {
47
+ constructor(httpStatus, result) {
48
+ super(result.error);
49
+ this.httpStatus = httpStatus;
50
+ this.result = result;
51
+ }
52
+ httpStatus;
53
+ result;
54
+ name = "EscrowWithdrawalLifecycleError";
55
+ }
56
+ class EscrowWithdrawalRejectionError extends Error {
57
+ constructor(httpStatus, result) {
58
+ super(result.error);
59
+ this.httpStatus = httpStatus;
60
+ this.result = result;
61
+ }
62
+ httpStatus;
63
+ result;
64
+ name = "EscrowWithdrawalRejectionError";
65
+ }
40
66
  function createEscrowGatewayClient(baseUrl) {
41
67
  const base = baseUrl.replace(/\/+$/, "");
42
68
  async function throwOnError(res, context) {
@@ -52,6 +78,27 @@ function createEscrowGatewayClient(baseUrl) {
52
78
  );
53
79
  }
54
80
  }
81
+ async function throwOnWithdrawError(res) {
82
+ if (res.ok) return;
83
+ let body;
84
+ try {
85
+ body = await res.json();
86
+ } catch {
87
+ throw new Error(
88
+ `Escrow gateway error (POST /v1/escrow/withdraw): ${res.status} ${res.statusText}`
89
+ );
90
+ }
91
+ if (isEscrowWithdrawalFailureResult(body)) {
92
+ throw new EscrowWithdrawalLifecycleError(res.status, body);
93
+ }
94
+ if (isEscrowWithdrawalRejectedResult(body)) {
95
+ throw new EscrowWithdrawalRejectionError(res.status, body);
96
+ }
97
+ const error = getGatewayErrorMessage(body);
98
+ throw new Error(
99
+ `Escrow gateway error (POST /v1/escrow/withdraw): ${res.status} ${res.statusText}${error ? `: ${error}` : ""}`
100
+ );
101
+ }
55
102
  return {
56
103
  async submitDeposit({ txHash }) {
57
104
  const res = await fetch(`${base}/v1/escrow/deposit`, {
@@ -107,14 +154,118 @@ function createEscrowGatewayClient(baseUrl) {
107
154
  });
108
155
  await throwOnError(res, "POST /v1/escrow/pay");
109
156
  return res.json();
157
+ },
158
+ async withdraw({
159
+ account,
160
+ asset,
161
+ amount,
162
+ withdrawNonce,
163
+ deadline,
164
+ signature
165
+ }) {
166
+ const res = await fetch(`${base}/v1/escrow/withdraw`, {
167
+ method: "POST",
168
+ headers: {
169
+ "Content-Type": "application/json",
170
+ Authorization: `Web3Signed ${signature}`
171
+ },
172
+ body: JSON.stringify({
173
+ account,
174
+ asset,
175
+ amount,
176
+ withdrawNonce,
177
+ deadline
178
+ })
179
+ });
180
+ await throwOnWithdrawError(res);
181
+ return res.json();
182
+ },
183
+ async getWithdrawNonce(account) {
184
+ const res = await fetch(
185
+ `${base}/v1/escrow/withdraw/nonce?account=${encodeURIComponent(account)}`,
186
+ { cache: "no-store" }
187
+ );
188
+ await throwOnError(res, "GET /v1/escrow/withdraw/nonce");
189
+ const body = await res.json();
190
+ if (!isWithdrawNonceResponse(body, account)) {
191
+ throw new Error(
192
+ "GET /v1/escrow/withdraw/nonce: invalid response structure"
193
+ );
194
+ }
195
+ return body;
110
196
  }
111
197
  };
112
198
  }
199
+ function getGatewayErrorMessage(body) {
200
+ if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
201
+ return body.error;
202
+ }
203
+ return void 0;
204
+ }
205
+ function isEscrowWithdrawalFailureResult(body) {
206
+ if (typeof body !== "object" || body === null) return false;
207
+ const value = body;
208
+ return value.success === false && (value.status === "retryable" || value.status === "reorged" || value.status === "failed") && typeof value.error === "string" && isAddressHex(value.account) && isAddressHex(value.asset) && isUint256Decimal(value.amount) && isUint256Decimal(value.withdrawNonce) && isUint256Decimal(value.deadline) && (isHash(value.txHash) || value.txHash === null) && (!("blockNumber" in value) || isBlockNumber(value.blockNumber));
209
+ }
210
+ function isEscrowWithdrawalRejectedResult(body) {
211
+ if (typeof body !== "object" || body === null) return false;
212
+ const value = body;
213
+ return value.success === false && value.status === "rejected" && isWithdrawalRejectionCode(value.code) && typeof value.error === "string" && isAddressHex(value.account) && isAddressHex(value.asset) && isUint256Decimal(value.amount) && isUint256Decimal(value.withdrawNonce) && isUint256Decimal(value.deadline) && optionalUint256Decimal(value.balance) && optionalUint256Decimal(value.authorizedAmount) && optionalUint256Decimal(value.withdrawingAmount) && optionalUint256Decimal(value.availableAmount) && optionalUint256Decimal(value.requestedAmount) && optionalUint256Decimal(value.minimumAmount);
214
+ }
215
+ function isWithdrawalRejectionCode(value) {
216
+ return value === "below_minimum" || value === "deadline_too_far" || value === "expired" || value === "insufficient_available" || value === "stale_nonce";
217
+ }
218
+ function optionalUint256Decimal(value) {
219
+ return value === void 0 || isUint256Decimal(value);
220
+ }
221
+ function isAddressHex(value) {
222
+ return typeof value === "string" && isHex(value, { strict: true }) && value.length === 42;
223
+ }
224
+ function isHash(value) {
225
+ return typeof value === "string" && isHex(value, { strict: true }) && value.length === 66;
226
+ }
227
+ function isUint256Decimal(value) {
228
+ if (typeof value !== "string" || value.length === 0 || value.length > 78) {
229
+ return false;
230
+ }
231
+ if (!/^(0|[1-9]\d*)$/.test(value)) return false;
232
+ return BigInt(value) <= 2n ** 256n - 1n;
233
+ }
234
+ function isBlockNumber(value) {
235
+ return value === null || isUint256Decimal(value);
236
+ }
237
+ function isWithdrawNonceResponse(body, requestedAccount) {
238
+ if (typeof body !== "object" || body === null) return false;
239
+ const value = body;
240
+ if (value.success !== true) return false;
241
+ if (!isAddressHex(value.account)) return false;
242
+ if (value.account.toLowerCase() !== requestedAccount.toLowerCase())
243
+ return false;
244
+ if (typeof value.chainId !== "string") return false;
245
+ if (!/^(0|[1-9]\d*)$/.test(value.chainId)) return false;
246
+ const isLastNull = value.lastWithdrawNonce === null;
247
+ const lastNonceValid = isLastNull || isUint256Decimal(value.lastWithdrawNonce);
248
+ if (!lastNonceValid) return false;
249
+ if (!isUint256Decimal(value.nextWithdrawNonce)) return false;
250
+ if (isLastNull) {
251
+ return value.nextWithdrawNonce === "1";
252
+ }
253
+ const lastNonce = BigInt(value.lastWithdrawNonce);
254
+ const nextNonce = BigInt(value.nextWithdrawNonce);
255
+ const expectedNextNonce = lastNonce + 1n;
256
+ if (expectedNextNonce > 2n ** 256n - 1n) return false;
257
+ return nextNonce === expectedNextNonce;
258
+ }
113
259
  export {
114
260
  ESCROW_DEPOSIT_ABI,
261
+ EscrowWithdrawalLifecycleError,
262
+ EscrowWithdrawalRejectionError,
115
263
  GENERIC_PAYMENT_TYPES,
116
264
  NATIVE_ASSET_ADDRESS,
265
+ WITHDRAW_AUTHORIZATION_TYPES,
266
+ buildWithdrawAuthorizationTypedData,
117
267
  createEscrowGatewayClient,
118
- genericPaymentDomain
268
+ genericPaymentDomain,
269
+ withdrawAuthorizationDomain
119
270
  };
120
271
  //# sourceMappingURL=escrow.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/protocol/escrow.ts"],"sourcesContent":["/**\n * DPv2 escrow payment helpers.\n *\n * Covers the three-phase flow used by builders to pay for data access:\n *\n * 1. **Deposit** — call `depositNative` or `depositToken` on the\n * DataPortabilityEscrow contract, then notify the gateway.\n * 2. **Balance** — read or force-sync the gateway's off-chain credit view.\n * 3. **Pay** — sign a `GenericPayment` EIP-712 message and POST it to the\n * gateway's `/v1/escrow/pay` endpoint.\n *\n * The gateway is the authority on balances; the on-chain contract is the\n * authority on what has been settled. Nothing in this module touches the\n * chain directly — signing is done by the caller's wallet.\n *\n * @category Protocol\n * @module escrow\n */\n\nimport type { TypedDataDomain } from \"viem\";\n\n// ---------------------------------------------------------------------------\n// EIP-712 — GenericPayment\n// ---------------------------------------------------------------------------\n\n/**\n * EIP-712 typed-data types for a generic op payment.\n *\n * The gateway verifies that the recovered signer == `payerAddress` and that\n * the (payer, paymentNonce) pair has not been seen before. Use a\n * monotonically-increasing nonce; the first payment for any payer should\n * start at 1.\n */\nexport const GENERIC_PAYMENT_TYPES = {\n GenericPayment: [\n { name: \"payerAddress\", type: \"address\" },\n { name: \"opType\", type: \"string\" },\n { name: \"opId\", type: \"bytes32\" },\n { name: \"asset\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"paymentNonce\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * EIP-712 message payload for a generic op payment.\n *\n * - `opType` is `\"grant\"` for legacy grant lifecycle payments or\n * `\"data_access\"` for a standalone receipt-bound read.\n * - `opId` is the bytes32 id of the operation being paid for: the grant id for\n * `\"grant\"`, or `accessRecord.recordId` for `\"data_access\"`.\n * - `asset` is the ERC-20 token address, or the zero address for native VANA.\n * - `amount` is the total amount in base units (wei for VANA). Must match the\n * sum the gateway expects for the current lifecycle of the op.\n * - `paymentNonce` must be a positive integer unique per `payerAddress`. Use 1\n * for the first payment; increment by at least 1 for each subsequent call.\n */\nexport interface GenericPaymentMessage {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n amount: bigint;\n paymentNonce: bigint;\n}\n\n/**\n * Returns the EIP-712 domain for signing a `GenericPayment` message.\n *\n * The verifying contract is the `DataPortabilityEscrow` contract; all gateway\n * deployments share the same domain name and version.\n *\n * @param chainId - Chain ID of the Vana network (e.g. 1480 mainnet, 14800 testnet).\n * @param escrowContract - Deployed address of DataPortabilityEscrow.\n */\nexport function genericPaymentDomain(\n chainId: number,\n escrowContract: `0x${string}`,\n): TypedDataDomain {\n return {\n name: \"Vana Data Portability\",\n version: \"1\",\n chainId,\n verifyingContract: escrowContract,\n };\n}\n\n// ---------------------------------------------------------------------------\n// On-chain deposit ABI fragments\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal ABI for the two deposit entry points on `DataPortabilityEscrow`.\n *\n * - `depositNative(address account)` payable — credits native VANA.\n * - `depositToken(address account, address token, uint256 amount)` — credits\n * an ERC-20 token (caller must have pre-approved the escrow contract).\n *\n * Pass this to viem's `writeContract` or encode it manually.\n */\nexport const ESCROW_DEPOSIT_ABI = [\n {\n type: \"function\",\n name: \"depositNative\",\n stateMutability: \"payable\",\n inputs: [{ name: \"account\", type: \"address\" }],\n outputs: [],\n },\n {\n type: \"function\",\n name: \"depositToken\",\n stateMutability: \"nonpayable\",\n inputs: [\n { name: \"account\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n outputs: [],\n },\n] as const;\n\n/**\n * The zero address used by the DataPortabilityEscrow contract to represent\n * native VANA in `asset` fields of events and balance responses.\n */\nexport const NATIVE_ASSET_ADDRESS =\n \"0x0000000000000000000000000000000000000000\" as const;\n\n// ---------------------------------------------------------------------------\n// Gateway API client\n// ---------------------------------------------------------------------------\n\n/**\n * Per-asset balance entry returned by the gateway's escrow balance endpoints.\n *\n * - `balance` — gross finalized credit (deposits credited so far).\n * - `pendingAmount` — sum of submitted deposits not yet confirmed.\n * - `authorizedAmount` — sum of all in-flight payments authorized by\n * `/v1/escrow/pay` (soft-lock). May include payments not yet settled\n * on-chain.\n * - `availableAmount` — `max(balance − authorizedAmount, 0)`. This is what\n * the payer can authorize before the gateway rejects with 402.\n */\nexport interface EscrowBalanceEntry {\n asset: string;\n balance: string;\n pendingAmount: string;\n authorizedAmount: string;\n availableAmount: string;\n updatedAt: string | null;\n}\n\nexport interface SubmittedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FinalizedDepositEntry {\n txHash: string;\n finalizedAt: string | null;\n blockNumber: string | null;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FailedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n lastError: string | null;\n}\n\n/** Full balance read response from `GET /v1/escrow/balance`. */\nexport interface EscrowBalanceResult {\n account: string;\n balances: EscrowBalanceEntry[];\n deposits: {\n submitted: SubmittedDepositEntry[];\n finalized: FinalizedDepositEntry[];\n failed: FailedDepositEntry[];\n };\n}\n\n/**\n * Response from `POST /v1/escrow/balance/sync`.\n *\n * Extends {@link EscrowBalanceResult} with a `sync` summary of what the\n * lazy-confirmation pass did.\n */\nexport interface EscrowBalanceSyncResult extends EscrowBalanceResult {\n sync:\n | {\n scanned: number;\n finalized: number;\n stillPending: number;\n failed: number;\n }\n | { skipped: true };\n}\n\n/** Response from `POST /v1/escrow/deposit`. */\nexport interface DepositSubmissionResult {\n success: true;\n txHash: string;\n account: string;\n status: \"submitted\" | \"finalized\" | \"failed\";\n blockNumber?: string | null;\n submittedAt: string;\n finalizedAt?: string | null;\n lastError?: string | null;\n}\n\n/** Breakdown returned by a successful `POST /v1/escrow/pay`. */\nexport interface PaymentBreakdown {\n registrationFee: string;\n dataAccessFee: string;\n /** True when this call settled the registration fee for the op. */\n registrationPaid: boolean;\n}\n\n/** Response from `POST /v1/escrow/pay`. */\nexport interface EscrowPayResult {\n success: true;\n opType: string;\n opId: string;\n payerAddress: string;\n asset: string;\n amount: string;\n breakdown: PaymentBreakdown;\n paymentNonce: string;\n paidAt: string;\n}\n\n/**\n * Parameters for submitting a deposit tx hash to the gateway.\n *\n * The gateway will decode the `account` from the tx's calldata and\n * credit the identified account once the tx reaches the configured\n * confirmation depth.\n */\nexport interface SubmitDepositParams {\n /** 0x-prefixed 32-byte transaction hash. */\n txHash: `0x${string}`;\n}\n\n/**\n * Parameters for the generic op payment endpoint (`POST /v1/escrow/pay`).\n *\n * The `signature` is an EIP-712 signature over a `GenericPayment` message\n * (see {@link GENERIC_PAYMENT_TYPES} and {@link genericPaymentDomain}).\n * Build and sign the typed data with your wallet before calling\n * {@link EscrowGatewayClient.payForOp}.\n */\nexport interface PayForOpParams {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n /** Decimal string representation of the uint256 amount. */\n amount: string;\n /** Decimal string representation of the uint256 nonce. */\n paymentNonce: string;\n /** 0x-prefixed 65-byte EIP-712 signature hex string. */\n signature: `0x${string}`;\n /**\n * Optional data-access receipt carried by x402 challenges.\n *\n * The gateway verifies its server signature; this type only describes the\n * wire shape.\n */\n accessRecord?: EscrowAccessRecord;\n}\n\n/** Wire shape of a receipt whose server signature the gateway verifies. */\nexport interface EscrowAccessRecord {\n dataPointId: `0x${string}`;\n version: string;\n accessor: `0x${string}`;\n recordId: `0x${string}`;\n signature: `0x${string}`;\n}\n\n/**\n * Minimal client for the gateway's escrow endpoints.\n *\n * Construct with {@link createEscrowGatewayClient}.\n */\nexport interface EscrowGatewayClient {\n /**\n * Notify the gateway of a submitted deposit transaction.\n *\n * The gateway decodes the credited account from the on-chain tx calldata\n * and starts tracking the deposit. Call this immediately after your\n * `depositNative` or `depositToken` tx is broadcast (it accepts pending\n * mempool txs). Returns `202` while the tx awaits confirmation.\n */\n submitDeposit(params: SubmitDepositParams): Promise<DepositSubmissionResult>;\n\n /**\n * Read the current escrow balance for an account.\n *\n * Pure read — no chain calls. To force a reconciliation pass first,\n * use {@link syncEscrowBalance}.\n */\n getEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceResult>;\n\n /**\n * Force a reconciliation pass then return the updated balance.\n *\n * Triggers the gateway's lazy-confirmation worker for the account — any\n * submitted deposits that have reached the configured confirmation level\n * are credited before the balance is returned. Prefer this over\n * {@link getEscrowBalance} when you need a fresh view after a deposit.\n */\n syncEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceSyncResult>;\n\n /**\n * Authorize a payment against the payer's escrow balance.\n *\n * The caller must:\n * 1. Assemble a {@link GenericPaymentMessage}.\n * 2. Sign it with `signTypedData` using {@link GENERIC_PAYMENT_TYPES} and\n * the domain from {@link genericPaymentDomain}.\n * 3. Pass the message fields + signature here.\n *\n * The gateway verifies the signature, checks the soft-lock balance, and\n * records the payment. Returns 402 if the payer has insufficient balance.\n */\n payForOp(params: PayForOpParams): Promise<EscrowPayResult>;\n}\n\n/**\n * Creates a client for the gateway escrow endpoints.\n *\n * @param baseUrl - Base URL of the DP RPC gateway\n * (e.g. `\"https://dp.vana.org\"`). Trailing slashes are trimmed.\n *\n * @example\n * ```typescript\n * import {\n * createEscrowGatewayClient,\n * genericPaymentDomain,\n * GENERIC_PAYMENT_TYPES,\n * } from \"@opendatalabs/vana-sdk/node\";\n *\n * const escrow = createEscrowGatewayClient(\"https://dp.vana.org\");\n *\n * // 1. Submit your deposit tx hash after broadcasting depositNative on-chain\n * const deposit = await escrow.submitDeposit({ txHash: \"0xabc…\" });\n *\n * // 2. Force-sync and read the updated balance\n * const { balances } = await escrow.syncEscrowBalance(\"0xpayerAddress\");\n *\n * // 3. Sign and authorize a grant payment\n * const sig = await walletClient.signTypedData({\n * domain: genericPaymentDomain(1480, \"0xEscrowContract\"),\n * types: GENERIC_PAYMENT_TYPES,\n * primaryType: \"GenericPayment\",\n * message: {\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: 1000000000000000000n,\n * paymentNonce: 1n,\n * },\n * });\n * const result = await escrow.payForOp({\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: \"1000000000000000000\",\n * paymentNonce: \"1\",\n * signature: sig,\n * });\n * ```\n */\nexport function createEscrowGatewayClient(\n baseUrl: string,\n): EscrowGatewayClient {\n const base = baseUrl.replace(/\\/+$/, \"\");\n\n async function throwOnError(res: Response, context: string): Promise<void> {\n if (!res.ok) {\n let detail = \"\";\n try {\n const body = (await res.json()) as { error?: string };\n if (body.error) detail = `: ${body.error}`;\n } catch {\n // Ignore JSON parse errors; use status text only.\n }\n throw new Error(\n `Escrow gateway error (${context}): ${res.status} ${res.statusText}${detail}`,\n );\n }\n }\n\n return {\n async submitDeposit({ txHash }) {\n const res = await fetch(`${base}/v1/escrow/deposit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ txHash }),\n });\n // 202 Accepted and 200 OK are both success states for deposit submission.\n if (res.status !== 200 && res.status !== 202) {\n await throwOnError(res, \"POST /v1/escrow/deposit\");\n }\n return res.json() as Promise<DepositSubmissionResult>;\n },\n\n async getEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance?account=${encodeURIComponent(account)}`,\n );\n await throwOnError(res, \"GET /v1/escrow/balance\");\n return res.json() as Promise<EscrowBalanceResult>;\n },\n\n async syncEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance/sync?account=${encodeURIComponent(account)}`,\n { method: \"POST\" },\n );\n await throwOnError(res, \"POST /v1/escrow/balance/sync\");\n return res.json() as Promise<EscrowBalanceSyncResult>;\n },\n\n async payForOp({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n signature,\n accessRecord,\n }) {\n const res = await fetch(`${base}/v1/escrow/pay`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${signature}`,\n },\n body: JSON.stringify({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n ...(accessRecord ? { accessRecord } : {}),\n }),\n });\n await throwOnError(res, \"POST /v1/escrow/pay\");\n return res.json() as Promise<EscrowPayResult>;\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/protocol/escrow.ts"],"sourcesContent":["/**\n * DPv2 escrow payment helpers.\n *\n * Covers the three-phase flow used by builders to pay for data access:\n *\n * 1. **Deposit** — call `depositNative` or `depositToken` on the\n * DataPortabilityEscrow contract, then notify the gateway.\n * 2. **Balance** — read or force-sync the gateway's off-chain credit view.\n * 3. **Pay** — sign a `GenericPayment` EIP-712 message and POST it to the\n * gateway's `/v1/escrow/pay` endpoint.\n *\n * The gateway is the authority on balances; the on-chain contract is the\n * authority on what has been settled. Nothing in this module touches the\n * chain directly — signing is done by the caller's wallet.\n *\n * @category Protocol\n * @module escrow\n */\n\nexport {\n buildWithdrawAuthorizationTypedData,\n withdrawAuthorizationDomain,\n WITHDRAW_AUTHORIZATION_TYPES,\n type WithdrawAuthorizationMessage,\n} from \"./eip712\";\nimport type { TypedDataDomain } from \"viem\";\nimport { isHex } from \"viem\";\n\n// ---------------------------------------------------------------------------\n// EIP-712 — GenericPayment\n// ---------------------------------------------------------------------------\n\n/**\n * EIP-712 typed-data types for a generic op payment.\n *\n * The gateway verifies that the recovered signer == `payerAddress` and that\n * the (payer, paymentNonce) pair has not been seen before. Use a\n * monotonically-increasing nonce; the first payment for any payer should\n * start at 1.\n */\nexport const GENERIC_PAYMENT_TYPES = {\n GenericPayment: [\n { name: \"payerAddress\", type: \"address\" },\n { name: \"opType\", type: \"string\" },\n { name: \"opId\", type: \"bytes32\" },\n { name: \"asset\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"paymentNonce\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * EIP-712 message payload for a generic op payment.\n *\n * - `opType` is `\"grant\"` for legacy grant lifecycle payments or\n * `\"data_access\"` for a standalone receipt-bound read.\n * - `opId` is the bytes32 id of the operation being paid for: the grant id for\n * `\"grant\"`, or `accessRecord.recordId` for `\"data_access\"`.\n * - `asset` is the ERC-20 token address, or the zero address for native VANA.\n * - `amount` is the total amount in base units (wei for VANA). Must match the\n * sum the gateway expects for the current lifecycle of the op.\n * - `paymentNonce` must be a positive integer unique per `payerAddress`. Use 1\n * for the first payment; increment by at least 1 for each subsequent call.\n */\nexport interface GenericPaymentMessage {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n amount: bigint;\n paymentNonce: bigint;\n}\n\n/**\n * Returns the EIP-712 domain for signing a `GenericPayment` message.\n *\n * The verifying contract is the `DataPortabilityEscrow` contract; all gateway\n * deployments share the same domain name and version.\n *\n * @param chainId - Chain ID of the Vana network (e.g. 1480 mainnet, 14800 testnet).\n * @param escrowContract - Deployed address of DataPortabilityEscrow.\n */\nexport function genericPaymentDomain(\n chainId: number,\n escrowContract: `0x${string}`,\n): TypedDataDomain {\n return {\n name: \"Vana Data Portability\",\n version: \"1\",\n chainId,\n verifyingContract: escrowContract,\n };\n}\n\n// ---------------------------------------------------------------------------\n// On-chain deposit ABI fragments\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal ABI for the two deposit entry points on `DataPortabilityEscrow`.\n *\n * - `depositNative(address account)` payable — credits native VANA.\n * - `depositToken(address account, address token, uint256 amount)` — credits\n * an ERC-20 token (caller must have pre-approved the escrow contract).\n *\n * Pass this to viem's `writeContract` or encode it manually.\n */\nexport const ESCROW_DEPOSIT_ABI = [\n {\n type: \"function\",\n name: \"depositNative\",\n stateMutability: \"payable\",\n inputs: [{ name: \"account\", type: \"address\" }],\n outputs: [],\n },\n {\n type: \"function\",\n name: \"depositToken\",\n stateMutability: \"nonpayable\",\n inputs: [\n { name: \"account\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n outputs: [],\n },\n] as const;\n\n/**\n * The zero address used by the DataPortabilityEscrow contract to represent\n * native VANA in `asset` fields of events and balance responses.\n */\nexport const NATIVE_ASSET_ADDRESS =\n \"0x0000000000000000000000000000000000000000\" as const;\n\n// ---------------------------------------------------------------------------\n// Gateway API client\n// ---------------------------------------------------------------------------\n\n/**\n * Per-asset balance entry returned by the gateway's escrow balance endpoints.\n *\n * - `balance` — gross finalized credit (deposits credited so far).\n * - `pendingAmount` — sum of submitted deposits not yet confirmed.\n * - `authorizedAmount` — sum of all in-flight payments authorized by\n * `/v1/escrow/pay` (soft-lock). May include payments not yet settled\n * on-chain.\n * - `withdrawingAmount` — sum of in-flight withdrawal reservations.\n * - `availableAmount` — `max(balance − authorizedAmount − withdrawingAmount, 0)`.\n * This is what the account can still authorize or withdraw.\n */\nexport interface EscrowBalanceEntry {\n asset: string;\n balance: string;\n pendingAmount: string;\n authorizedAmount: string;\n withdrawingAmount: string;\n availableAmount: string;\n /** Minimum withdrawal amount currently accepted for this asset, if configured. */\n withdrawalMinimumAmount: string | null;\n updatedAt: string | null;\n}\n\nexport interface SubmittedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FinalizedDepositEntry {\n txHash: string;\n finalizedAt: string | null;\n blockNumber: string | null;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface FailedDepositEntry {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n lastError: string | null;\n}\n\n/** Full balance read response from `GET /v1/escrow/balance`. */\nexport interface EscrowBalanceResult {\n account: string;\n balances: EscrowBalanceEntry[];\n deposits: {\n submitted: SubmittedDepositEntry[];\n finalized: FinalizedDepositEntry[];\n failed: FailedDepositEntry[];\n };\n}\n\n/**\n * Response from `POST /v1/escrow/balance/sync`.\n *\n * Extends {@link EscrowBalanceResult} with a `sync` summary of what the\n * lazy-confirmation pass did.\n */\nexport interface EscrowBalanceSyncResult extends EscrowBalanceResult {\n sync:\n | {\n scanned: number;\n finalized: number;\n stillPending: number;\n failed: number;\n }\n | { skipped: true };\n}\n\n/** Response from `POST /v1/escrow/deposit`. */\nexport interface DepositSubmissionResult {\n success: true;\n txHash: string;\n account: string;\n status: \"submitted\" | \"finalized\" | \"failed\";\n blockNumber?: string | null;\n submittedAt: string;\n finalizedAt?: string | null;\n lastError?: string | null;\n}\n\n/** Breakdown returned by a successful `POST /v1/escrow/pay`. */\nexport interface PaymentBreakdown {\n registrationFee: string;\n dataAccessFee: string;\n /** True when this call settled the registration fee for the op. */\n registrationPaid: boolean;\n}\n\n/** Response from `POST /v1/escrow/pay`. */\nexport interface EscrowPayResult {\n success: true;\n opType: string;\n opId: string;\n payerAddress: string;\n asset: string;\n amount: string;\n breakdown: PaymentBreakdown;\n paymentNonce: string;\n paidAt: string;\n}\n\ninterface EscrowWithdrawalResponseBase {\n account: `0x${string}`;\n asset: `0x${string}`;\n amount: string;\n withdrawNonce: string;\n deadline: string;\n}\n\n/** A persisted authorization whose transaction has not been broadcast yet. */\nexport interface EscrowWithdrawalSubmittedWithoutTransaction extends EscrowWithdrawalResponseBase {\n success: true;\n status: \"submitted\";\n txHash: null;\n message: string;\n}\n\n/** A withdrawal with a persisted transaction that is awaiting reconciliation. */\nexport interface EscrowWithdrawalSubmittedWithTransaction {\n success: true;\n status: \"submitted\";\n txHash: `0x${string}`;\n message: string;\n // A provisional-revert response contains only txHash, blockNumber, and\n // message. Ordinary submissions include the signed-intent fields.\n account?: `0x${string}`;\n asset?: `0x${string}`;\n amount?: string;\n withdrawNonce?: string;\n deadline?: string;\n blockNumber?: string;\n}\n\n/** A withdrawal that the gateway has accepted but not yet confirmed. */\nexport type EscrowWithdrawalSubmittedResult =\n | EscrowWithdrawalSubmittedWithoutTransaction\n | EscrowWithdrawalSubmittedWithTransaction;\n\n/** A withdrawal whose on-chain debit has reached the named lifecycle state. */\nexport interface EscrowWithdrawalSettledResult extends EscrowWithdrawalResponseBase {\n success: true;\n status: \"confirmed\" | \"finalized\";\n txHash: `0x${string}`;\n blockNumber: string | null;\n}\n\n/** Successful lifecycle responses from `POST /v1/escrow/withdraw`. */\nexport type EscrowWithdrawalResult =\n | EscrowWithdrawalSubmittedResult\n | EscrowWithdrawalSettledResult;\n\n/** Terminal or retryable withdrawal lifecycle state returned with a non-2xx status. */\nexport interface EscrowWithdrawalFailureResult extends EscrowWithdrawalResponseBase {\n success: false;\n status: \"retryable\" | \"reorged\" | \"failed\";\n error: string;\n txHash: `0x${string}` | null;\n blockNumber?: string | null;\n}\n\nexport type EscrowWithdrawalRejectionCode =\n | \"below_minimum\"\n | \"deadline_too_far\"\n | \"expired\"\n | \"insufficient_available\"\n | \"stale_nonce\";\n\n/** Definite pre-acceptance rejection. No durable withdrawal intent was created. */\nexport interface EscrowWithdrawalRejectedResult extends EscrowWithdrawalResponseBase {\n success: false;\n status: \"rejected\";\n code: EscrowWithdrawalRejectionCode;\n error: string;\n balance?: string;\n authorizedAmount?: string;\n withdrawingAmount?: string;\n availableAmount?: string;\n requestedAmount?: string;\n minimumAmount?: string;\n}\n\n/**\n * A typed non-2xx gateway lifecycle response.\n *\n * `retryable` means resend the exact signed intent. `reorged` and `failed`\n * require a newly signed authorization with a new nonce.\n */\nexport class EscrowWithdrawalLifecycleError extends Error {\n override readonly name = \"EscrowWithdrawalLifecycleError\";\n\n constructor(\n readonly httpStatus: number,\n readonly result: EscrowWithdrawalFailureResult,\n ) {\n super(result.error);\n }\n}\n\n/** A typed non-2xx gateway rejection before a withdrawal intent is accepted. */\nexport class EscrowWithdrawalRejectionError extends Error {\n override readonly name = \"EscrowWithdrawalRejectionError\";\n\n constructor(\n readonly httpStatus: number,\n readonly result: EscrowWithdrawalRejectedResult,\n ) {\n super(result.error);\n }\n}\n\n/**\n * Parameters for submitting a deposit tx hash to the gateway.\n *\n * The gateway will decode the `account` from the tx's calldata and\n * credit the identified account once the tx reaches the configured\n * confirmation depth.\n */\nexport interface SubmitDepositParams {\n /** 0x-prefixed 32-byte transaction hash. */\n txHash: `0x${string}`;\n}\n\n/**\n * Parameters for the generic op payment endpoint (`POST /v1/escrow/pay`).\n *\n * The `signature` is an EIP-712 signature over a `GenericPayment` message\n * (see {@link GENERIC_PAYMENT_TYPES} and {@link genericPaymentDomain}).\n * Build and sign the typed data with your wallet before calling\n * {@link EscrowGatewayClient.payForOp}.\n */\nexport interface PayForOpParams {\n payerAddress: `0x${string}`;\n opType: string;\n opId: `0x${string}`;\n asset: `0x${string}`;\n /** Decimal string representation of the uint256 amount. */\n amount: string;\n /** Decimal string representation of the uint256 nonce. */\n paymentNonce: string;\n /** 0x-prefixed 65-byte EIP-712 signature hex string. */\n signature: `0x${string}`;\n /**\n * Optional data-access receipt carried by x402 challenges.\n *\n * The gateway verifies its server signature; this type only describes the\n * wire shape.\n */\n accessRecord?: EscrowAccessRecord;\n}\n\n/**\n * Parameters for `POST /v1/escrow/withdraw`.\n *\n * `withdrawNonce` and `deadline` are caller-supplied decimal uint256 strings.\n * The SDK intentionally does not generate a nonce: retrying safely requires a\n * durable caller-owned nonce source and the exact same signed payload.\n */\nexport interface WithdrawFromEscrowParams {\n account: `0x${string}`;\n asset: `0x${string}`;\n amount: string;\n withdrawNonce: string;\n deadline: string;\n signature: `0x${string}`;\n}\n\n/**\n * Response from `GET /v1/escrow/withdraw/nonce`.\n *\n * The gateway provides a read-only snapshot of the account's withdrawal nonce\n * state. This is **not** a reservation; multiple concurrent callers will see\n * the same `nextWithdrawNonce`. To reduce staleness risk, query immediately before\n * signing/submitting the withdrawal authorization. However, `stale_nonce` errors can\n * still occur under concurrent withdrawal attempts; if rejected, re-query and re-sign.\n *\n * Use `nextWithdrawNonce` in the signed withdrawal authorization; `lastWithdrawNonce`\n * is provided for reference and diagnostics.\n */\nexport interface WithdrawNonceResponse {\n success: true;\n account: `0x${string}`;\n chainId: string;\n lastWithdrawNonce: string | null;\n nextWithdrawNonce: string;\n}\n\n/** Wire shape of a receipt whose server signature the gateway verifies. */\nexport interface EscrowAccessRecord {\n dataPointId: `0x${string}`;\n version: string;\n accessor: `0x${string}`;\n recordId: `0x${string}`;\n signature: `0x${string}`;\n}\n\n/**\n * Minimal client for the gateway's escrow endpoints.\n *\n * Construct with {@link createEscrowGatewayClient}.\n */\nexport interface EscrowGatewayClient {\n /**\n * Notify the gateway of a submitted deposit transaction.\n *\n * The gateway decodes the credited account from the on-chain tx calldata\n * and starts tracking the deposit. Call this immediately after your\n * `depositNative` or `depositToken` tx is broadcast (it accepts pending\n * mempool txs). Returns `202` while the tx awaits confirmation.\n */\n submitDeposit(params: SubmitDepositParams): Promise<DepositSubmissionResult>;\n\n /**\n * Read the current escrow balance for an account.\n *\n * Pure read — no chain calls. To force a reconciliation pass first,\n * use {@link syncEscrowBalance}.\n */\n getEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceResult>;\n\n /**\n * Force a reconciliation pass then return the updated balance.\n *\n * Triggers the gateway's lazy-confirmation worker for the account — any\n * submitted deposits that have reached the configured confirmation level\n * are credited before the balance is returned. Prefer this over\n * {@link getEscrowBalance} when you need a fresh view after a deposit.\n */\n syncEscrowBalance(account: `0x${string}`): Promise<EscrowBalanceSyncResult>;\n\n /**\n * Authorize a payment against the payer's escrow balance.\n *\n * The caller must:\n * 1. Assemble a {@link GenericPaymentMessage}.\n * 2. Sign it with `signTypedData` using {@link GENERIC_PAYMENT_TYPES} and\n * the domain from {@link genericPaymentDomain}.\n * 3. Pass the message fields + signature here.\n *\n * The gateway verifies the signature, checks the soft-lock balance, and\n * records the payment. Returns 402 if the payer has insufficient balance.\n */\n payForOp(params: PayForOpParams): Promise<EscrowPayResult>;\n\n /**\n * Submit or reconcile a signed withdrawal authorization.\n *\n * The gateway decides which signers may authorize an account. For example,\n * it may accept the account itself or the confirmed owner of a registered\n * app account.\n *\n * Retry a `submitted` result with the exact same parameters. Do not replace\n * `withdrawNonce`, `deadline`, or signature unless starting a new intent.\n */\n withdraw(params: WithdrawFromEscrowParams): Promise<EscrowWithdrawalResult>;\n\n /**\n * Read the authoritative next withdrawal nonce for an account.\n *\n * The gateway is the authority on what nonce to use; use the value from\n * `nextWithdrawNonce` when signing a withdrawal authorization.\n *\n * Do NOT generate or cache nonces client-side; concurrent callers cannot be\n * safely coordinated without durable shared state. Query this endpoint immediately\n * before signing/submitting to reduce staleness risk. However, `stale_nonce` errors\n * can still occur; if rejected, re-query and re-sign.\n */\n getWithdrawNonce(account: `0x${string}`): Promise<WithdrawNonceResponse>;\n}\n\n/** The only gateway capability required by direct data-access payment flows. */\nexport type EscrowPaymentClient = Pick<EscrowGatewayClient, \"payForOp\">;\n\n/**\n * Creates a client for the gateway escrow endpoints.\n *\n * @param baseUrl - Base URL of the DP RPC gateway\n * (e.g. `\"https://dp.vana.org\"`). Trailing slashes are trimmed.\n *\n * @example\n * ```typescript\n * import {\n * createEscrowGatewayClient,\n * genericPaymentDomain,\n * GENERIC_PAYMENT_TYPES,\n * } from \"@opendatalabs/vana-sdk/node\";\n *\n * const escrow = createEscrowGatewayClient(\"https://dp.vana.org\");\n *\n * // 1. Submit your deposit tx hash after broadcasting depositNative on-chain\n * const deposit = await escrow.submitDeposit({ txHash: \"0xabc…\" });\n *\n * // 2. Force-sync and read the updated balance\n * const { balances } = await escrow.syncEscrowBalance(\"0xpayerAddress\");\n *\n * // 3. Sign and authorize a grant payment\n * const sig = await walletClient.signTypedData({\n * domain: genericPaymentDomain(1480, \"0xEscrowContract\"),\n * types: GENERIC_PAYMENT_TYPES,\n * primaryType: \"GenericPayment\",\n * message: {\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: 1000000000000000000n,\n * paymentNonce: 1n,\n * },\n * });\n * const result = await escrow.payForOp({\n * payerAddress: \"0xpayerAddress\",\n * opType: \"grant\",\n * opId: \"0xgrantId\",\n * asset: \"0x0000000000000000000000000000000000000000\",\n * amount: \"1000000000000000000\",\n * paymentNonce: \"1\",\n * signature: sig,\n * });\n * ```\n */\nexport function createEscrowGatewayClient(\n baseUrl: string,\n): EscrowGatewayClient {\n const base = baseUrl.replace(/\\/+$/, \"\");\n\n async function throwOnError(res: Response, context: string): Promise<void> {\n if (!res.ok) {\n let detail = \"\";\n try {\n const body = (await res.json()) as { error?: string };\n if (body.error) detail = `: ${body.error}`;\n } catch {\n // Ignore JSON parse errors; use status text only.\n }\n throw new Error(\n `Escrow gateway error (${context}): ${res.status} ${res.statusText}${detail}`,\n );\n }\n }\n\n async function throwOnWithdrawError(res: Response): Promise<void> {\n if (res.ok) return;\n\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n throw new Error(\n `Escrow gateway error (POST /v1/escrow/withdraw): ${res.status} ${res.statusText}`,\n );\n }\n\n if (isEscrowWithdrawalFailureResult(body)) {\n throw new EscrowWithdrawalLifecycleError(res.status, body);\n }\n if (isEscrowWithdrawalRejectedResult(body)) {\n throw new EscrowWithdrawalRejectionError(res.status, body);\n }\n\n const error = getGatewayErrorMessage(body);\n throw new Error(\n `Escrow gateway error (POST /v1/escrow/withdraw): ${res.status} ${res.statusText}${error ? `: ${error}` : \"\"}`,\n );\n }\n\n return {\n async submitDeposit({ txHash }) {\n const res = await fetch(`${base}/v1/escrow/deposit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ txHash }),\n });\n // 202 Accepted and 200 OK are both success states for deposit submission.\n if (res.status !== 200 && res.status !== 202) {\n await throwOnError(res, \"POST /v1/escrow/deposit\");\n }\n return res.json() as Promise<DepositSubmissionResult>;\n },\n\n async getEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance?account=${encodeURIComponent(account)}`,\n );\n await throwOnError(res, \"GET /v1/escrow/balance\");\n return res.json() as Promise<EscrowBalanceResult>;\n },\n\n async syncEscrowBalance(account) {\n const res = await fetch(\n `${base}/v1/escrow/balance/sync?account=${encodeURIComponent(account)}`,\n { method: \"POST\" },\n );\n await throwOnError(res, \"POST /v1/escrow/balance/sync\");\n return res.json() as Promise<EscrowBalanceSyncResult>;\n },\n\n async payForOp({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n signature,\n accessRecord,\n }) {\n const res = await fetch(`${base}/v1/escrow/pay`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${signature}`,\n },\n body: JSON.stringify({\n payerAddress,\n opType,\n opId,\n asset,\n amount,\n paymentNonce,\n ...(accessRecord ? { accessRecord } : {}),\n }),\n });\n await throwOnError(res, \"POST /v1/escrow/pay\");\n return res.json() as Promise<EscrowPayResult>;\n },\n\n async withdraw({\n account,\n asset,\n amount,\n withdrawNonce,\n deadline,\n signature,\n }) {\n const res = await fetch(`${base}/v1/escrow/withdraw`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${signature}`,\n },\n body: JSON.stringify({\n account,\n asset,\n amount,\n withdrawNonce,\n deadline,\n }),\n });\n await throwOnWithdrawError(res);\n return res.json() as Promise<EscrowWithdrawalResult>;\n },\n\n async getWithdrawNonce(account) {\n const res = await fetch(\n `${base}/v1/escrow/withdraw/nonce?account=${encodeURIComponent(account)}`,\n { cache: \"no-store\" },\n );\n await throwOnError(res, \"GET /v1/escrow/withdraw/nonce\");\n const body = (await res.json()) as unknown;\n if (!isWithdrawNonceResponse(body, account)) {\n throw new Error(\n \"GET /v1/escrow/withdraw/nonce: invalid response structure\",\n );\n }\n return body;\n },\n };\n}\n\nfunction getGatewayErrorMessage(body: unknown): string | undefined {\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"error\" in body &&\n typeof body.error === \"string\"\n ) {\n return body.error;\n }\n return undefined;\n}\n\nfunction isEscrowWithdrawalFailureResult(\n body: unknown,\n): body is EscrowWithdrawalFailureResult {\n if (typeof body !== \"object\" || body === null) return false;\n const value = body as Record<string, unknown>;\n return (\n value.success === false &&\n (value.status === \"retryable\" ||\n value.status === \"reorged\" ||\n value.status === \"failed\") &&\n typeof value.error === \"string\" &&\n isAddressHex(value.account) &&\n isAddressHex(value.asset) &&\n isUint256Decimal(value.amount) &&\n isUint256Decimal(value.withdrawNonce) &&\n isUint256Decimal(value.deadline) &&\n (isHash(value.txHash) || value.txHash === null) &&\n (!(\"blockNumber\" in value) || isBlockNumber(value.blockNumber))\n );\n}\n\nfunction isEscrowWithdrawalRejectedResult(\n body: unknown,\n): body is EscrowWithdrawalRejectedResult {\n if (typeof body !== \"object\" || body === null) return false;\n const value = body as Record<string, unknown>;\n return (\n value.success === false &&\n value.status === \"rejected\" &&\n isWithdrawalRejectionCode(value.code) &&\n typeof value.error === \"string\" &&\n isAddressHex(value.account) &&\n isAddressHex(value.asset) &&\n isUint256Decimal(value.amount) &&\n isUint256Decimal(value.withdrawNonce) &&\n isUint256Decimal(value.deadline) &&\n optionalUint256Decimal(value.balance) &&\n optionalUint256Decimal(value.authorizedAmount) &&\n optionalUint256Decimal(value.withdrawingAmount) &&\n optionalUint256Decimal(value.availableAmount) &&\n optionalUint256Decimal(value.requestedAmount) &&\n optionalUint256Decimal(value.minimumAmount)\n );\n}\n\nfunction isWithdrawalRejectionCode(\n value: unknown,\n): value is EscrowWithdrawalRejectionCode {\n return (\n value === \"below_minimum\" ||\n value === \"deadline_too_far\" ||\n value === \"expired\" ||\n value === \"insufficient_available\" ||\n value === \"stale_nonce\"\n );\n}\n\nfunction optionalUint256Decimal(value: unknown): boolean {\n return value === undefined || isUint256Decimal(value);\n}\n\nfunction isAddressHex(value: unknown): value is `0x${string}` {\n return (\n typeof value === \"string\" &&\n isHex(value, { strict: true }) &&\n value.length === 42\n );\n}\n\nfunction isHash(value: unknown): value is `0x${string}` {\n return (\n typeof value === \"string\" &&\n isHex(value, { strict: true }) &&\n value.length === 66\n );\n}\n\nfunction isUint256Decimal(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 78) {\n return false;\n }\n if (!/^(0|[1-9]\\d*)$/.test(value)) return false;\n return BigInt(value) <= 2n ** 256n - 1n;\n}\n\nfunction isBlockNumber(value: unknown): value is string | null {\n return value === null || isUint256Decimal(value);\n}\n\nfunction isWithdrawNonceResponse(\n body: unknown,\n requestedAccount: `0x${string}`,\n): body is WithdrawNonceResponse {\n if (typeof body !== \"object\" || body === null) return false;\n const value = body as Record<string, unknown>;\n\n if (value.success !== true) return false;\n\n if (!isAddressHex(value.account)) return false;\n if (value.account.toLowerCase() !== requestedAccount.toLowerCase())\n return false;\n if (typeof value.chainId !== \"string\") return false;\n if (!/^(0|[1-9]\\d*)$/.test(value.chainId)) return false;\n\n const isLastNull = value.lastWithdrawNonce === null;\n const lastNonceValid =\n isLastNull || isUint256Decimal(value.lastWithdrawNonce);\n if (!lastNonceValid) return false;\n\n if (!isUint256Decimal(value.nextWithdrawNonce)) return false;\n\n // Validate nonce pair consistency: nextWithdrawNonce must be exactly lastWithdrawNonce + 1\n // or exactly 1 if lastWithdrawNonce is null\n if (isLastNull) {\n return value.nextWithdrawNonce === \"1\";\n }\n\n const lastNonce = BigInt(value.lastWithdrawNonce as string);\n const nextNonce = BigInt(value.nextWithdrawNonce as string);\n const expectedNextNonce = lastNonce + 1n;\n\n // Ensure no overflow (nextNonce must still be within uint256)\n if (expectedNextNonce > 2n ** 256n - 1n) return false;\n\n return nextNonce === expectedNextNonce;\n}\n"],"mappings":"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","names":[]}