@x402r/evm 0.0.2 → 0.0.3
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.
- package/README.md +20 -16
- package/dist/cjs/escrow/client/index.cjs +227 -0
- package/dist/cjs/escrow/client/index.cjs.map +1 -0
- package/dist/cjs/escrow/client/index.d.cts +23 -0
- package/dist/cjs/escrow/client/index.d.ts +23 -0
- package/dist/cjs/escrow/client/index.js +223 -0
- package/dist/cjs/escrow/client/index.js.map +1 -0
- package/dist/cjs/escrow/facilitator/index.cjs +359 -0
- package/dist/cjs/escrow/facilitator/index.cjs.map +1 -0
- package/dist/cjs/escrow/facilitator/index.d.cts +53 -0
- package/dist/{escrow → cjs/escrow}/facilitator/index.d.ts +17 -13
- package/dist/cjs/escrow/facilitator/index.js +358 -0
- package/dist/cjs/escrow/facilitator/index.js.map +1 -0
- package/dist/cjs/escrow/server/index.cjs +222 -0
- package/dist/cjs/escrow/server/index.cjs.map +1 -0
- package/dist/cjs/escrow/server/index.d.cts +78 -0
- package/dist/{escrow → cjs/escrow}/server/index.d.ts +15 -9
- package/dist/cjs/escrow/server/index.js +217 -0
- package/dist/cjs/escrow/server/index.js.map +1 -0
- package/dist/{shared/types.d.ts → cjs/escrow/types/index.d.ts} +7 -6
- package/dist/cjs/escrow/types/index.js +40 -0
- package/dist/cjs/escrow/types/index.js.map +1 -0
- package/dist/cjs/index.cjs +215 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +22 -0
- package/dist/cjs/index.d.ts +54 -0
- package/dist/cjs/index.js +223 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/scheme-CNrmuyp3.d.ts +22 -0
- package/dist/esm/chunk-DLIBGHEY.mjs +85 -0
- package/dist/esm/chunk-DLIBGHEY.mjs.map +1 -0
- package/dist/esm/chunk-IYUU7AJZ.mjs +187 -0
- package/dist/esm/chunk-IYUU7AJZ.mjs.map +1 -0
- package/dist/esm/chunk-JBHVAJN3.mjs +13 -0
- package/dist/esm/chunk-JBHVAJN3.mjs.map +1 -0
- package/dist/esm/chunk-NSSMTXJJ.mjs +8 -0
- package/dist/esm/chunk-NSSMTXJJ.mjs.map +1 -0
- package/dist/esm/escrow/client/index.d.mts +23 -0
- package/dist/esm/escrow/client/index.mjs +20 -0
- package/dist/esm/escrow/client/index.mjs.map +1 -0
- package/dist/esm/escrow/facilitator/index.d.mts +53 -0
- package/dist/esm/escrow/facilitator/index.mjs +230 -0
- package/dist/esm/escrow/facilitator/index.mjs.map +1 -0
- package/dist/esm/escrow/server/index.d.mts +78 -0
- package/dist/esm/escrow/server/index.mjs +191 -0
- package/dist/esm/escrow/server/index.mjs.map +1 -0
- package/dist/esm/index.d.mts +54 -0
- package/dist/esm/index.mjs +15 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/scheme-CNrmuyp3.d.mts +22 -0
- package/package.json +42 -16
- package/src/escrow/client/index.ts +3 -161
- package/src/escrow/client/register.ts +33 -0
- package/src/escrow/client/scheme.ts +107 -0
- package/src/escrow/facilitator/index.ts +3 -388
- package/src/escrow/facilitator/register.ts +33 -0
- package/src/escrow/facilitator/scheme.ts +289 -0
- package/src/escrow/index.ts +3 -0
- package/src/escrow/server/index.ts +3 -261
- package/src/escrow/server/register.ts +34 -0
- package/src/escrow/server/scheme.ts +226 -0
- package/src/escrow/shared/constants.ts +65 -0
- package/src/escrow/shared/nonce.ts +175 -0
- package/src/escrow/shared/types.ts +69 -0
- package/src/escrow/shared/utils.ts +16 -0
- package/dist/escrow/client/index.d.ts +0 -40
- package/dist/escrow/client/index.d.ts.map +0 -1
- package/dist/escrow/client/index.js +0 -104
- package/dist/escrow/client/index.js.map +0 -1
- package/dist/escrow/facilitator/index.d.ts.map +0 -1
- package/dist/escrow/facilitator/index.js +0 -300
- package/dist/escrow/facilitator/index.js.map +0 -1
- package/dist/escrow/server/index.d.ts.map +0 -1
- package/dist/escrow/server/index.js +0 -214
- package/dist/escrow/server/index.js.map +0 -1
- package/dist/shared/constants.d.ts +0 -112
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/constants.js +0 -51
- package/dist/shared/constants.js.map +0 -1
- package/dist/shared/nonce.d.ts +0 -41
- package/dist/shared/nonce.d.ts.map +0 -1
- package/dist/shared/nonce.js +0 -154
- package/dist/shared/nonce.js.map +0 -1
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js +0 -21
- package/dist/shared/types.js.map +0 -1
- package/src/shared/constants.ts +0 -58
- package/src/shared/nonce.ts +0 -203
- package/src/shared/types.ts +0 -69
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/escrow/index.ts","../../src/escrow/shared/nonce.ts","../../src/escrow/shared/constants.ts","../../src/escrow/shared/utils.ts","../../src/escrow/client/scheme.ts"],"sourcesContent":["export { EscrowEvmScheme } from \"./client/scheme\";\n","/**\n * Nonce computation and ERC-3009 signing utilities\n * Adapted from @agentokratia/x402-escrow (MIT)\n */\n\nimport { encodeAbiParameters, getAddress, keccak256, toHex } from \"viem\";\nimport type { ClientEvmSigner } from \"@x402/evm\";\nimport { ZERO_ADDRESS, PAYMENT_INFO_COMPONENTS, RECEIVE_AUTHORIZATION_TYPES } from \"./constants\";\nimport type { EscrowExtra, EscrowPayload } from \"./types\";\n\n/**\n * PaymentInfo typehash - must match AuthCaptureEscrow.PAYMENT_INFO_TYPEHASH\n */\nconst PAYMENT_INFO_TYPEHASH = keccak256(\n new TextEncoder().encode(\n \"PaymentInfo(address operator,address payer,address receiver,address token,uint120 maxAmount,uint48 preApprovalExpiry,uint48 authorizationExpiry,uint48 refundExpiry,uint16 minFeeBps,uint16 maxFeeBps,address feeReceiver,uint256 salt)\",\n ),\n);\n\n/**\n * Compute escrow nonce for ERC-3009 authorization\n * Must match AuthCaptureEscrow.getHash() with payer=address(0)\n */\nexport function computeEscrowNonce(\n chainId: number,\n escrowAddress: `0x${string}`,\n paymentInfo: EscrowPayload[\"paymentInfo\"],\n): `0x${string}` {\n // Step 1: Encode paymentInfo with payer=0 (payer-agnostic)\n const paymentInfoEncoded = encodeAbiParameters(\n [\n { name: \"typehash\", type: \"bytes32\" },\n { name: \"operator\", type: \"address\" },\n { name: \"payer\", type: \"address\" },\n { name: \"receiver\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"maxAmount\", type: \"uint120\" },\n { name: \"preApprovalExpiry\", type: \"uint48\" },\n { name: \"authorizationExpiry\", type: \"uint48\" },\n { name: \"refundExpiry\", type: \"uint48\" },\n { name: \"minFeeBps\", type: \"uint16\" },\n { name: \"maxFeeBps\", type: \"uint16\" },\n { name: \"feeReceiver\", type: \"address\" },\n { name: \"salt\", type: \"uint256\" },\n ],\n [\n PAYMENT_INFO_TYPEHASH,\n paymentInfo.operator,\n ZERO_ADDRESS, // payer-agnostic\n paymentInfo.receiver,\n paymentInfo.token,\n BigInt(paymentInfo.maxAmount),\n paymentInfo.preApprovalExpiry,\n paymentInfo.authorizationExpiry,\n paymentInfo.refundExpiry,\n paymentInfo.minFeeBps,\n paymentInfo.maxFeeBps,\n paymentInfo.feeReceiver,\n BigInt(paymentInfo.salt),\n ],\n );\n const paymentInfoHash = keccak256(paymentInfoEncoded);\n\n // Step 2: Encode (chainId, escrow, paymentInfoHash) and hash\n const outerEncoded = encodeAbiParameters(\n [\n { name: \"chainId\", type: \"uint256\" },\n { name: \"escrow\", type: \"address\" },\n { name: \"paymentInfoHash\", type: \"bytes32\" },\n ],\n [BigInt(chainId), escrowAddress, paymentInfoHash],\n );\n\n return keccak256(outerEncoded);\n}\n\n/**\n * Sign ERC-3009 ReceiveWithAuthorization\n * Note: receiveWithAuthorization uses a different primary type than transferWithAuthorization\n */\nexport async function signERC3009(\n signer: ClientEvmSigner,\n authorization: EscrowPayload[\"authorization\"],\n extra: EscrowExtra,\n tokenAddress: `0x${string}`,\n chainId: number,\n): Promise<`0x${string}`> {\n // EIP-712 domain - name must match the token's EIP-712 domain\n // (e.g., \"USDC\" for Base USDC, not \"USD Coin\")\n const domain = {\n name: extra.name,\n version: extra.version,\n chainId,\n verifyingContract: getAddress(tokenAddress),\n };\n\n const message = {\n from: getAddress(authorization.from),\n to: getAddress(authorization.to),\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce,\n };\n\n return signer.signTypedData({\n domain,\n types: RECEIVE_AUTHORIZATION_TYPES,\n primaryType: \"ReceiveWithAuthorization\",\n message,\n });\n}\n\n/**\n * Verify ERC-3009 signature (facilitator-side)\n * @param signer - The signer with verifyTypedData method\n * @param authorization - ERC-3009 authorization data\n * @param signature - The signature to verify\n * @param extra - Extra configuration including chainId\n * @param tokenAddress - The token contract address (verifyingContract for EIP-712)\n */\nexport async function verifyERC3009Signature(\n signer: {\n verifyTypedData: (args: {\n address: `0x${string}`;\n domain: Record<string, unknown>;\n types: Record<string, unknown>;\n primaryType: string;\n message: Record<string, unknown>;\n signature: `0x${string}`;\n }) => Promise<boolean>;\n },\n authorization: EscrowPayload[\"authorization\"],\n signature: `0x${string}`,\n extra: EscrowExtra & { chainId: number },\n tokenAddress: `0x${string}`,\n): Promise<boolean> {\n const domain = {\n name: extra.name,\n version: extra.version,\n chainId: extra.chainId,\n verifyingContract: getAddress(tokenAddress),\n };\n\n const message = {\n from: getAddress(authorization.from),\n to: getAddress(authorization.to),\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce,\n };\n\n try {\n return await signer.verifyTypedData({\n address: getAddress(authorization.from),\n domain,\n types: RECEIVE_AUTHORIZATION_TYPES,\n primaryType: \"ReceiveWithAuthorization\",\n message,\n signature,\n });\n } catch {\n return false;\n }\n}\n\n/**\n * Generate random salt for paymentInfo\n */\nexport function generateSalt(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return toHex(bytes);\n}\n","export const ZERO_ADDRESS =\n \"0x0000000000000000000000000000000000000000\" as const;\nexport const MAX_UINT48 = 281474976710655;\nexport const MAX_UINT32 = 4294967295;\n\n// PaymentInfo struct for AuthCaptureEscrow (matches commerce-payments contract)\nexport const PAYMENT_INFO_COMPONENTS = [\n { name: \"operator\", type: \"address\" },\n { name: \"payer\", type: \"address\" },\n { name: \"receiver\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"maxAmount\", type: \"uint120\" },\n { name: \"preApprovalExpiry\", type: \"uint48\" },\n { name: \"authorizationExpiry\", type: \"uint48\" },\n { name: \"refundExpiry\", type: \"uint48\" },\n { name: \"minFeeBps\", type: \"uint16\" },\n { name: \"maxFeeBps\", type: \"uint16\" },\n { name: \"feeReceiver\", type: \"address\" },\n { name: \"salt\", type: \"uint256\" },\n] as const;\n\nexport const OPERATOR_ABI = [\n {\n name: \"authorize\",\n type: \"function\",\n stateMutability: \"nonpayable\",\n inputs: [\n {\n name: \"paymentInfo\",\n type: \"tuple\",\n components: PAYMENT_INFO_COMPONENTS,\n },\n { name: \"amount\", type: \"uint256\" },\n { name: \"tokenCollector\", type: \"address\" },\n { name: \"collectorData\", type: \"bytes\" },\n ],\n outputs: [],\n },\n] as const;\n\n// ERC-3009 TransferWithAuthorization type hash\nexport const TRANSFER_WITH_AUTHORIZATION_TYPEHASH =\n \"0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267\" as const;\n\n// ERC-3009 ReceiveWithAuthorization EIP-712 types\nexport const RECEIVE_AUTHORIZATION_TYPES = {\n ReceiveWithAuthorization: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n ],\n} as const;\n\n// ERC-20 balanceOf ABI for balance checks\nexport const ERC20_BALANCE_OF_ABI = [\n {\n name: \"balanceOf\",\n type: \"function\",\n stateMutability: \"view\",\n inputs: [{ name: \"account\", type: \"address\" }],\n outputs: [{ name: \"balance\", type: \"uint256\" }],\n },\n] as const;\n","/**\n * Parse chainId from CAIP-2 network identifier\n * @param network - CAIP-2 network identifier (e.g., 'eip155:84532')\n * @returns The chain ID as a number\n */\nexport function parseChainId(network: string): number {\n const parts = network.split(\":\");\n if (parts.length !== 2 || parts[0] !== \"eip155\") {\n throw new Error(\n `Invalid network format: ${network}. Expected 'eip155:<chainId>'`,\n );\n }\n const chainId = parseInt(parts[1], 10);\n if (isNaN(chainId)) {\n throw new Error(`Invalid chainId in network: ${network}`);\n }\n return chainId;\n}\n","/**\n * Escrow Scheme - Client\n * Creates payment payloads for escrow payments.\n *\n * Implements x402's SchemeNetworkClient interface so it can be registered\n * on an x402Client via client.register('eip155:84532', new EscrowEvmScheme(signer)).\n */\n\nimport type {\n PaymentPayloadContext,\n PaymentPayloadResult,\n PaymentRequirements,\n SchemeNetworkClient,\n} from \"@x402/core/types\";\nimport type { ClientEvmSigner } from \"@x402/evm\";\nimport {\n computeEscrowNonce,\n signERC3009,\n generateSalt,\n} from \"../shared/nonce\";\nimport { MAX_UINT48 } from \"../shared/constants\";\nimport type { EscrowExtra } from \"../shared/types\";\nimport { parseChainId } from \"../shared/utils\";\n\n/**\n * Escrow Client Scheme - implements x402's SchemeNetworkClient\n */\nexport class EscrowEvmScheme implements SchemeNetworkClient {\n readonly scheme = \"escrow\";\n\n constructor(private readonly signer: ClientEvmSigner) {}\n\n async createPaymentPayload(\n x402Version: number,\n requirements: PaymentRequirements,\n _context?: PaymentPayloadContext,\n ): Promise<PaymentPayloadResult> {\n if (x402Version !== 2) {\n throw new Error(\n `Unsupported x402Version: ${x402Version}. Only version 2 is supported.`,\n );\n }\n\n const extra = requirements.extra as unknown as EscrowExtra;\n\n // Validate required EIP-712 domain parameters (M3, M10)\n if (!extra.name) {\n throw new Error(\n `EIP-712 domain parameter 'name' is required in payment requirements for asset ${requirements.asset}`,\n );\n }\n if (!extra.version) {\n throw new Error(\n `EIP-712 domain parameter 'version' is required in payment requirements for asset ${requirements.asset}`,\n );\n }\n\n const {\n escrowAddress,\n operatorAddress,\n tokenCollector,\n minFeeBps = 0,\n maxFeeBps = 0,\n feeReceiver,\n preApprovalExpirySeconds,\n refundExpirySeconds,\n authorizationExpirySeconds,\n } = extra;\n\n const chainId = parseChainId(requirements.network);\n const maxAmount = requirements.amount;\n\n const paymentInfo = {\n operator: operatorAddress,\n receiver: requirements.payTo as `0x${string}`,\n token: requirements.asset as `0x${string}`,\n maxAmount,\n preApprovalExpiry: preApprovalExpirySeconds ?? MAX_UINT48,\n authorizationExpiry: authorizationExpirySeconds ?? MAX_UINT48,\n refundExpiry: refundExpirySeconds ?? MAX_UINT48,\n minFeeBps,\n maxFeeBps,\n feeReceiver: feeReceiver ?? operatorAddress,\n salt: generateSalt(),\n };\n\n const nonce = computeEscrowNonce(chainId, escrowAddress, paymentInfo);\n\n // ERC-3009 authorization - validBefore MUST match what contract passes to receiveWithAuthorization\n // The contract uses paymentInfo.preApprovalExpiry as validBefore\n const authorization = {\n from: this.signer.address,\n to: tokenCollector,\n value: maxAmount,\n validAfter: \"0\",\n validBefore: String(paymentInfo.preApprovalExpiry),\n nonce,\n };\n\n const signature = await signERC3009(\n this.signer,\n authorization,\n extra,\n requirements.asset as `0x${string}`,\n chainId,\n );\n\n return {\n x402Version,\n payload: { authorization, signature, paymentInfo },\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SchemeNetworkClient, PaymentRequirements, PaymentPayloadContext, PaymentPayloadResult } from '@x402/core/types';
|
|
2
|
+
import { ClientEvmSigner } from '@x402/evm';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Escrow Scheme - Client
|
|
6
|
+
* Creates payment payloads for escrow payments.
|
|
7
|
+
*
|
|
8
|
+
* Implements x402's SchemeNetworkClient interface so it can be registered
|
|
9
|
+
* on an x402Client via client.register('eip155:84532', new EscrowEvmScheme(signer)).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Escrow Client Scheme - implements x402's SchemeNetworkClient
|
|
14
|
+
*/
|
|
15
|
+
declare class EscrowEvmScheme implements SchemeNetworkClient {
|
|
16
|
+
private readonly signer;
|
|
17
|
+
readonly scheme = "escrow";
|
|
18
|
+
constructor(signer: ClientEvmSigner);
|
|
19
|
+
createPaymentPayload(x402Version: number, requirements: PaymentRequirements, _context?: PaymentPayloadContext): Promise<PaymentPayloadResult>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { EscrowEvmScheme };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export { E as EscrowEvmScheme } from './scheme-CNrmuyp3.js';
|
|
2
|
+
import '@x402/core/types';
|
|
3
|
+
import '@x402/evm';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Type guard for EscrowPayload
|
|
7
|
+
*/
|
|
8
|
+
declare function isEscrowPayload(value: unknown): value is EscrowPayload;
|
|
9
|
+
/**
|
|
10
|
+
* Type guard for EscrowExtra
|
|
11
|
+
*/
|
|
12
|
+
declare function isEscrowExtra(value: unknown): value is EscrowExtra;
|
|
13
|
+
interface EscrowExtra {
|
|
14
|
+
escrowAddress: `0x${string}`;
|
|
15
|
+
operatorAddress: `0x${string}`;
|
|
16
|
+
tokenCollector: `0x${string}`;
|
|
17
|
+
authorizeAddress?: `0x${string}`;
|
|
18
|
+
minDeposit?: string;
|
|
19
|
+
maxDeposit?: string;
|
|
20
|
+
preApprovalExpirySeconds?: number;
|
|
21
|
+
authorizationExpirySeconds?: number;
|
|
22
|
+
refundExpirySeconds?: number;
|
|
23
|
+
minFeeBps?: number;
|
|
24
|
+
maxFeeBps?: number;
|
|
25
|
+
feeReceiver?: `0x${string}`;
|
|
26
|
+
name: string;
|
|
27
|
+
version: string;
|
|
28
|
+
}
|
|
29
|
+
interface EscrowPayload {
|
|
30
|
+
authorization: {
|
|
31
|
+
from: `0x${string}`;
|
|
32
|
+
to: `0x${string}`;
|
|
33
|
+
value: string;
|
|
34
|
+
validAfter: string;
|
|
35
|
+
validBefore: string;
|
|
36
|
+
nonce: `0x${string}`;
|
|
37
|
+
};
|
|
38
|
+
signature: `0x${string}`;
|
|
39
|
+
paymentInfo: {
|
|
40
|
+
operator: `0x${string}`;
|
|
41
|
+
receiver: `0x${string}`;
|
|
42
|
+
token: `0x${string}`;
|
|
43
|
+
maxAmount: string;
|
|
44
|
+
preApprovalExpiry: number;
|
|
45
|
+
authorizationExpiry: number;
|
|
46
|
+
refundExpiry: number;
|
|
47
|
+
minFeeBps: number;
|
|
48
|
+
maxFeeBps: number;
|
|
49
|
+
feeReceiver: `0x${string}`;
|
|
50
|
+
salt: `0x${string}`;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { type EscrowExtra, type EscrowPayload, isEscrowExtra, isEscrowPayload };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
|
|
22
|
+
// src/escrow/index.ts
|
|
23
|
+
var escrow_exports = {};
|
|
24
|
+
__export(escrow_exports, {
|
|
25
|
+
EscrowEvmScheme: () => EscrowEvmScheme,
|
|
26
|
+
isEscrowExtra: () => isEscrowExtra,
|
|
27
|
+
isEscrowPayload: () => isEscrowPayload
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(escrow_exports);
|
|
30
|
+
|
|
31
|
+
// src/escrow/shared/nonce.ts
|
|
32
|
+
var import_viem = require("viem");
|
|
33
|
+
|
|
34
|
+
// src/escrow/shared/constants.ts
|
|
35
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
36
|
+
var MAX_UINT48 = 281474976710655;
|
|
37
|
+
var RECEIVE_AUTHORIZATION_TYPES = {
|
|
38
|
+
ReceiveWithAuthorization: [
|
|
39
|
+
{ name: "from", type: "address" },
|
|
40
|
+
{ name: "to", type: "address" },
|
|
41
|
+
{ name: "value", type: "uint256" },
|
|
42
|
+
{ name: "validAfter", type: "uint256" },
|
|
43
|
+
{ name: "validBefore", type: "uint256" },
|
|
44
|
+
{ name: "nonce", type: "bytes32" }
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/escrow/shared/nonce.ts
|
|
49
|
+
var PAYMENT_INFO_TYPEHASH = (0, import_viem.keccak256)(
|
|
50
|
+
new TextEncoder().encode(
|
|
51
|
+
"PaymentInfo(address operator,address payer,address receiver,address token,uint120 maxAmount,uint48 preApprovalExpiry,uint48 authorizationExpiry,uint48 refundExpiry,uint16 minFeeBps,uint16 maxFeeBps,address feeReceiver,uint256 salt)"
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
function computeEscrowNonce(chainId, escrowAddress, paymentInfo) {
|
|
55
|
+
const paymentInfoEncoded = (0, import_viem.encodeAbiParameters)(
|
|
56
|
+
[
|
|
57
|
+
{ name: "typehash", type: "bytes32" },
|
|
58
|
+
{ name: "operator", type: "address" },
|
|
59
|
+
{ name: "payer", type: "address" },
|
|
60
|
+
{ name: "receiver", type: "address" },
|
|
61
|
+
{ name: "token", type: "address" },
|
|
62
|
+
{ name: "maxAmount", type: "uint120" },
|
|
63
|
+
{ name: "preApprovalExpiry", type: "uint48" },
|
|
64
|
+
{ name: "authorizationExpiry", type: "uint48" },
|
|
65
|
+
{ name: "refundExpiry", type: "uint48" },
|
|
66
|
+
{ name: "minFeeBps", type: "uint16" },
|
|
67
|
+
{ name: "maxFeeBps", type: "uint16" },
|
|
68
|
+
{ name: "feeReceiver", type: "address" },
|
|
69
|
+
{ name: "salt", type: "uint256" }
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
PAYMENT_INFO_TYPEHASH,
|
|
73
|
+
paymentInfo.operator,
|
|
74
|
+
ZERO_ADDRESS,
|
|
75
|
+
// payer-agnostic
|
|
76
|
+
paymentInfo.receiver,
|
|
77
|
+
paymentInfo.token,
|
|
78
|
+
BigInt(paymentInfo.maxAmount),
|
|
79
|
+
paymentInfo.preApprovalExpiry,
|
|
80
|
+
paymentInfo.authorizationExpiry,
|
|
81
|
+
paymentInfo.refundExpiry,
|
|
82
|
+
paymentInfo.minFeeBps,
|
|
83
|
+
paymentInfo.maxFeeBps,
|
|
84
|
+
paymentInfo.feeReceiver,
|
|
85
|
+
BigInt(paymentInfo.salt)
|
|
86
|
+
]
|
|
87
|
+
);
|
|
88
|
+
const paymentInfoHash = (0, import_viem.keccak256)(paymentInfoEncoded);
|
|
89
|
+
const outerEncoded = (0, import_viem.encodeAbiParameters)(
|
|
90
|
+
[
|
|
91
|
+
{ name: "chainId", type: "uint256" },
|
|
92
|
+
{ name: "escrow", type: "address" },
|
|
93
|
+
{ name: "paymentInfoHash", type: "bytes32" }
|
|
94
|
+
],
|
|
95
|
+
[BigInt(chainId), escrowAddress, paymentInfoHash]
|
|
96
|
+
);
|
|
97
|
+
return (0, import_viem.keccak256)(outerEncoded);
|
|
98
|
+
}
|
|
99
|
+
async function signERC3009(signer, authorization, extra, tokenAddress, chainId) {
|
|
100
|
+
const domain = {
|
|
101
|
+
name: extra.name,
|
|
102
|
+
version: extra.version,
|
|
103
|
+
chainId,
|
|
104
|
+
verifyingContract: (0, import_viem.getAddress)(tokenAddress)
|
|
105
|
+
};
|
|
106
|
+
const message = {
|
|
107
|
+
from: (0, import_viem.getAddress)(authorization.from),
|
|
108
|
+
to: (0, import_viem.getAddress)(authorization.to),
|
|
109
|
+
value: BigInt(authorization.value),
|
|
110
|
+
validAfter: BigInt(authorization.validAfter),
|
|
111
|
+
validBefore: BigInt(authorization.validBefore),
|
|
112
|
+
nonce: authorization.nonce
|
|
113
|
+
};
|
|
114
|
+
return signer.signTypedData({
|
|
115
|
+
domain,
|
|
116
|
+
types: RECEIVE_AUTHORIZATION_TYPES,
|
|
117
|
+
primaryType: "ReceiveWithAuthorization",
|
|
118
|
+
message
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function generateSalt() {
|
|
122
|
+
const bytes = new Uint8Array(32);
|
|
123
|
+
crypto.getRandomValues(bytes);
|
|
124
|
+
return (0, import_viem.toHex)(bytes);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/escrow/shared/utils.ts
|
|
128
|
+
function parseChainId(network) {
|
|
129
|
+
const parts = network.split(":");
|
|
130
|
+
if (parts.length !== 2 || parts[0] !== "eip155") {
|
|
131
|
+
throw new Error(`Invalid network format: ${network}. Expected 'eip155:<chainId>'`);
|
|
132
|
+
}
|
|
133
|
+
const chainId = parseInt(parts[1], 10);
|
|
134
|
+
if (isNaN(chainId)) {
|
|
135
|
+
throw new Error(`Invalid chainId in network: ${network}`);
|
|
136
|
+
}
|
|
137
|
+
return chainId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/escrow/client/scheme.ts
|
|
141
|
+
var EscrowEvmScheme = class {
|
|
142
|
+
constructor(signer) {
|
|
143
|
+
this.signer = signer;
|
|
144
|
+
__publicField(this, "scheme", "escrow");
|
|
145
|
+
}
|
|
146
|
+
async createPaymentPayload(x402Version, requirements, _context) {
|
|
147
|
+
if (x402Version !== 2) {
|
|
148
|
+
throw new Error(`Unsupported x402Version: ${x402Version}. Only version 2 is supported.`);
|
|
149
|
+
}
|
|
150
|
+
const extra = requirements.extra;
|
|
151
|
+
if (!extra.name) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`EIP-712 domain parameter 'name' is required in payment requirements for asset ${requirements.asset}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (!extra.version) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`EIP-712 domain parameter 'version' is required in payment requirements for asset ${requirements.asset}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const {
|
|
162
|
+
escrowAddress,
|
|
163
|
+
operatorAddress,
|
|
164
|
+
tokenCollector,
|
|
165
|
+
minFeeBps = 0,
|
|
166
|
+
maxFeeBps = 0,
|
|
167
|
+
feeReceiver,
|
|
168
|
+
preApprovalExpirySeconds,
|
|
169
|
+
refundExpirySeconds,
|
|
170
|
+
authorizationExpirySeconds
|
|
171
|
+
} = extra;
|
|
172
|
+
const chainId = parseChainId(requirements.network);
|
|
173
|
+
const maxAmount = requirements.amount;
|
|
174
|
+
const paymentInfo = {
|
|
175
|
+
operator: operatorAddress,
|
|
176
|
+
receiver: requirements.payTo,
|
|
177
|
+
token: requirements.asset,
|
|
178
|
+
maxAmount,
|
|
179
|
+
preApprovalExpiry: preApprovalExpirySeconds ?? MAX_UINT48,
|
|
180
|
+
authorizationExpiry: authorizationExpirySeconds ?? MAX_UINT48,
|
|
181
|
+
refundExpiry: refundExpirySeconds ?? MAX_UINT48,
|
|
182
|
+
minFeeBps,
|
|
183
|
+
maxFeeBps,
|
|
184
|
+
feeReceiver: feeReceiver ?? operatorAddress,
|
|
185
|
+
salt: generateSalt()
|
|
186
|
+
};
|
|
187
|
+
const nonce = computeEscrowNonce(chainId, escrowAddress, paymentInfo);
|
|
188
|
+
const authorization = {
|
|
189
|
+
from: this.signer.address,
|
|
190
|
+
to: tokenCollector,
|
|
191
|
+
value: maxAmount,
|
|
192
|
+
validAfter: "0",
|
|
193
|
+
validBefore: String(paymentInfo.preApprovalExpiry),
|
|
194
|
+
nonce
|
|
195
|
+
};
|
|
196
|
+
const signature = await signERC3009(
|
|
197
|
+
this.signer,
|
|
198
|
+
authorization,
|
|
199
|
+
extra,
|
|
200
|
+
requirements.asset,
|
|
201
|
+
chainId
|
|
202
|
+
);
|
|
203
|
+
return {
|
|
204
|
+
x402Version,
|
|
205
|
+
payload: { authorization, signature, paymentInfo }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/escrow/shared/types.ts
|
|
211
|
+
function isEscrowPayload(value) {
|
|
212
|
+
return typeof value === "object" && value !== null && "authorization" in value && "signature" in value && "paymentInfo" in value;
|
|
213
|
+
}
|
|
214
|
+
function isEscrowExtra(value) {
|
|
215
|
+
return typeof value === "object" && value !== null && "escrowAddress" in value && "operatorAddress" in value && "tokenCollector" in value;
|
|
216
|
+
}
|
|
217
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
218
|
+
0 && (module.exports = {
|
|
219
|
+
EscrowEvmScheme,
|
|
220
|
+
isEscrowExtra,
|
|
221
|
+
isEscrowPayload
|
|
222
|
+
});
|
|
223
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/escrow/index.ts","../../src/escrow/shared/nonce.ts","../../src/escrow/shared/constants.ts","../../src/escrow/shared/utils.ts","../../src/escrow/client/scheme.ts","../../src/escrow/shared/types.ts"],"sourcesContent":["export { EscrowEvmScheme } from './client/scheme'\nexport type { EscrowPayload, EscrowExtra } from './shared/types'\nexport { isEscrowPayload, isEscrowExtra } from './shared/types'\n","/**\n * Nonce computation and ERC-3009 signing utilities\n * Adapted from @agentokratia/x402-escrow (MIT)\n */\n\nimport { encodeAbiParameters, getAddress, keccak256, toHex } from 'viem'\nimport type { ClientEvmSigner } from '@x402/evm'\nimport { ZERO_ADDRESS, RECEIVE_AUTHORIZATION_TYPES } from './constants'\nimport type { EscrowExtra, EscrowPayload } from './types'\n\n/**\n * PaymentInfo typehash - must match AuthCaptureEscrow.PAYMENT_INFO_TYPEHASH\n */\nconst PAYMENT_INFO_TYPEHASH = keccak256(\n new TextEncoder().encode(\n 'PaymentInfo(address operator,address payer,address receiver,address token,uint120 maxAmount,uint48 preApprovalExpiry,uint48 authorizationExpiry,uint48 refundExpiry,uint16 minFeeBps,uint16 maxFeeBps,address feeReceiver,uint256 salt)',\n ),\n)\n\n/**\n * Compute escrow nonce for ERC-3009 authorization\n * Must match AuthCaptureEscrow.getHash() with payer=address(0)\n */\nexport function computeEscrowNonce(\n chainId: number,\n escrowAddress: `0x${string}`,\n paymentInfo: EscrowPayload['paymentInfo'],\n): `0x${string}` {\n // Step 1: Encode paymentInfo with payer=0 (payer-agnostic)\n const paymentInfoEncoded = encodeAbiParameters(\n [\n { name: 'typehash', type: 'bytes32' },\n { name: 'operator', type: 'address' },\n { name: 'payer', type: 'address' },\n { name: 'receiver', type: 'address' },\n { name: 'token', type: 'address' },\n { name: 'maxAmount', type: 'uint120' },\n { name: 'preApprovalExpiry', type: 'uint48' },\n { name: 'authorizationExpiry', type: 'uint48' },\n { name: 'refundExpiry', type: 'uint48' },\n { name: 'minFeeBps', type: 'uint16' },\n { name: 'maxFeeBps', type: 'uint16' },\n { name: 'feeReceiver', type: 'address' },\n { name: 'salt', type: 'uint256' },\n ],\n [\n PAYMENT_INFO_TYPEHASH,\n paymentInfo.operator,\n ZERO_ADDRESS, // payer-agnostic\n paymentInfo.receiver,\n paymentInfo.token,\n BigInt(paymentInfo.maxAmount),\n paymentInfo.preApprovalExpiry,\n paymentInfo.authorizationExpiry,\n paymentInfo.refundExpiry,\n paymentInfo.minFeeBps,\n paymentInfo.maxFeeBps,\n paymentInfo.feeReceiver,\n BigInt(paymentInfo.salt),\n ],\n )\n const paymentInfoHash = keccak256(paymentInfoEncoded)\n\n // Step 2: Encode (chainId, escrow, paymentInfoHash) and hash\n const outerEncoded = encodeAbiParameters(\n [\n { name: 'chainId', type: 'uint256' },\n { name: 'escrow', type: 'address' },\n { name: 'paymentInfoHash', type: 'bytes32' },\n ],\n [BigInt(chainId), escrowAddress, paymentInfoHash],\n )\n\n return keccak256(outerEncoded)\n}\n\n/**\n * Sign ERC-3009 ReceiveWithAuthorization\n * Note: receiveWithAuthorization uses a different primary type than transferWithAuthorization\n */\nexport async function signERC3009(\n signer: ClientEvmSigner,\n authorization: EscrowPayload['authorization'],\n extra: EscrowExtra,\n tokenAddress: `0x${string}`,\n chainId: number,\n): Promise<`0x${string}`> {\n // EIP-712 domain - name must match the token's EIP-712 domain\n // (e.g., \"USDC\" for Base USDC, not \"USD Coin\")\n const domain = {\n name: extra.name,\n version: extra.version,\n chainId,\n verifyingContract: getAddress(tokenAddress),\n }\n\n const message = {\n from: getAddress(authorization.from),\n to: getAddress(authorization.to),\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce,\n }\n\n return signer.signTypedData({\n domain,\n types: RECEIVE_AUTHORIZATION_TYPES,\n primaryType: 'ReceiveWithAuthorization',\n message,\n })\n}\n\n/**\n * Verify ERC-3009 signature (facilitator-side)\n * @param signer - The signer with verifyTypedData method\n * @param authorization - ERC-3009 authorization data\n * @param signature - The signature to verify\n * @param extra - Extra configuration including chainId\n * @param tokenAddress - The token contract address (verifyingContract for EIP-712)\n */\nexport async function verifyERC3009Signature(\n signer: {\n verifyTypedData: (_args: {\n address: `0x${string}`\n domain: Record<string, unknown>\n types: Record<string, unknown>\n primaryType: string\n message: Record<string, unknown>\n signature: `0x${string}`\n }) => Promise<boolean>\n },\n authorization: EscrowPayload['authorization'],\n signature: `0x${string}`,\n extra: EscrowExtra & { chainId: number },\n tokenAddress: `0x${string}`,\n): Promise<boolean> {\n const domain = {\n name: extra.name,\n version: extra.version,\n chainId: extra.chainId,\n verifyingContract: getAddress(tokenAddress),\n }\n\n const message = {\n from: getAddress(authorization.from),\n to: getAddress(authorization.to),\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce,\n }\n\n try {\n return await signer.verifyTypedData({\n address: getAddress(authorization.from),\n domain,\n types: RECEIVE_AUTHORIZATION_TYPES,\n primaryType: 'ReceiveWithAuthorization',\n message,\n signature,\n })\n } catch {\n return false\n }\n}\n\n/**\n * Generate random salt for paymentInfo\n */\nexport function generateSalt(): `0x${string}` {\n const bytes = new Uint8Array(32)\n crypto.getRandomValues(bytes)\n return toHex(bytes)\n}\n","export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const\nexport const MAX_UINT48 = 281474976710655\nexport const MAX_UINT32 = 4294967295\n\n// PaymentInfo struct for AuthCaptureEscrow (matches commerce-payments contract)\nexport const PAYMENT_INFO_COMPONENTS = [\n { name: 'operator', type: 'address' },\n { name: 'payer', type: 'address' },\n { name: 'receiver', type: 'address' },\n { name: 'token', type: 'address' },\n { name: 'maxAmount', type: 'uint120' },\n { name: 'preApprovalExpiry', type: 'uint48' },\n { name: 'authorizationExpiry', type: 'uint48' },\n { name: 'refundExpiry', type: 'uint48' },\n { name: 'minFeeBps', type: 'uint16' },\n { name: 'maxFeeBps', type: 'uint16' },\n { name: 'feeReceiver', type: 'address' },\n { name: 'salt', type: 'uint256' },\n] as const\n\nexport const OPERATOR_ABI = [\n {\n name: 'authorize',\n type: 'function',\n stateMutability: 'nonpayable',\n inputs: [\n {\n name: 'paymentInfo',\n type: 'tuple',\n components: PAYMENT_INFO_COMPONENTS,\n },\n { name: 'amount', type: 'uint256' },\n { name: 'tokenCollector', type: 'address' },\n { name: 'collectorData', type: 'bytes' },\n ],\n outputs: [],\n },\n] as const\n\n// ERC-3009 TransferWithAuthorization type hash\nexport const TRANSFER_WITH_AUTHORIZATION_TYPEHASH =\n '0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267' as const\n\n// ERC-3009 ReceiveWithAuthorization EIP-712 types\nexport const RECEIVE_AUTHORIZATION_TYPES = {\n ReceiveWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n} as const\n\n// ERC-20 balanceOf ABI for balance checks\nexport const ERC20_BALANCE_OF_ABI = [\n {\n name: 'balanceOf',\n type: 'function',\n stateMutability: 'view',\n inputs: [{ name: 'account', type: 'address' }],\n outputs: [{ name: 'balance', type: 'uint256' }],\n },\n] as const\n","/**\n * Parse chainId from CAIP-2 network identifier\n * @param network - CAIP-2 network identifier (e.g., 'eip155:84532')\n * @returns The chain ID as a number\n */\nexport function parseChainId(network: string): number {\n const parts = network.split(':')\n if (parts.length !== 2 || parts[0] !== 'eip155') {\n throw new Error(`Invalid network format: ${network}. Expected 'eip155:<chainId>'`)\n }\n const chainId = parseInt(parts[1], 10)\n if (isNaN(chainId)) {\n throw new Error(`Invalid chainId in network: ${network}`)\n }\n return chainId\n}\n","/**\n * Escrow Scheme - Client\n * Creates payment payloads for escrow payments.\n *\n * Implements x402's SchemeNetworkClient interface so it can be registered\n * on an x402Client via client.register('eip155:84532', new EscrowEvmScheme(signer)).\n */\n\nimport type {\n PaymentPayloadContext,\n PaymentPayloadResult,\n PaymentRequirements,\n SchemeNetworkClient,\n} from '@x402/core/types'\nimport type { ClientEvmSigner } from '@x402/evm'\nimport { computeEscrowNonce, signERC3009, generateSalt } from '../shared/nonce'\nimport { MAX_UINT48 } from '../shared/constants'\nimport type { EscrowExtra } from '../shared/types'\nimport { parseChainId } from '../shared/utils'\n\n/**\n * Escrow Client Scheme - implements x402's SchemeNetworkClient\n */\nexport class EscrowEvmScheme implements SchemeNetworkClient {\n readonly scheme = 'escrow'\n\n constructor(private readonly signer: ClientEvmSigner) {}\n\n async createPaymentPayload(\n x402Version: number,\n requirements: PaymentRequirements,\n _context?: PaymentPayloadContext,\n ): Promise<PaymentPayloadResult> {\n if (x402Version !== 2) {\n throw new Error(`Unsupported x402Version: ${x402Version}. Only version 2 is supported.`)\n }\n\n const extra = requirements.extra as unknown as EscrowExtra\n\n // Validate required EIP-712 domain parameters (M3, M10)\n if (!extra.name) {\n throw new Error(\n `EIP-712 domain parameter 'name' is required in payment requirements for asset ${requirements.asset}`,\n )\n }\n if (!extra.version) {\n throw new Error(\n `EIP-712 domain parameter 'version' is required in payment requirements for asset ${requirements.asset}`,\n )\n }\n\n const {\n escrowAddress,\n operatorAddress,\n tokenCollector,\n minFeeBps = 0,\n maxFeeBps = 0,\n feeReceiver,\n preApprovalExpirySeconds,\n refundExpirySeconds,\n authorizationExpirySeconds,\n } = extra\n\n const chainId = parseChainId(requirements.network)\n const maxAmount = requirements.amount\n\n const paymentInfo = {\n operator: operatorAddress,\n receiver: requirements.payTo as `0x${string}`,\n token: requirements.asset as `0x${string}`,\n maxAmount,\n preApprovalExpiry: preApprovalExpirySeconds ?? MAX_UINT48,\n authorizationExpiry: authorizationExpirySeconds ?? MAX_UINT48,\n refundExpiry: refundExpirySeconds ?? MAX_UINT48,\n minFeeBps,\n maxFeeBps,\n feeReceiver: feeReceiver ?? operatorAddress,\n salt: generateSalt(),\n }\n\n const nonce = computeEscrowNonce(chainId, escrowAddress, paymentInfo)\n\n // ERC-3009 authorization - validBefore MUST match what contract passes to receiveWithAuthorization\n // The contract uses paymentInfo.preApprovalExpiry as validBefore\n const authorization = {\n from: this.signer.address,\n to: tokenCollector,\n value: maxAmount,\n validAfter: '0',\n validBefore: String(paymentInfo.preApprovalExpiry),\n nonce,\n }\n\n const signature = await signERC3009(\n this.signer,\n authorization,\n extra,\n requirements.asset as `0x${string}`,\n chainId,\n )\n\n return {\n x402Version,\n payload: { authorization, signature, paymentInfo },\n }\n }\n}\n","/**\n * Type guard for EscrowPayload\n */\nexport function isEscrowPayload(value: unknown): value is EscrowPayload {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'authorization' in value &&\n 'signature' in value &&\n 'paymentInfo' in value\n )\n}\n\n/**\n * Type guard for EscrowExtra\n */\nexport function isEscrowExtra(value: unknown): value is EscrowExtra {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'escrowAddress' in value &&\n 'operatorAddress' in value &&\n 'tokenCollector' in value\n )\n}\n\n// EscrowExtra - fields in PaymentRequirements.extra\nexport interface EscrowExtra {\n escrowAddress: `0x${string}`\n operatorAddress: `0x${string}`\n tokenCollector: `0x${string}`\n authorizeAddress?: `0x${string}`\n minDeposit?: string\n maxDeposit?: string\n preApprovalExpirySeconds?: number\n authorizationExpirySeconds?: number\n refundExpirySeconds?: number\n minFeeBps?: number\n maxFeeBps?: number\n feeReceiver?: `0x${string}`\n name: string // EIP-712 domain name (e.g., \"USDC\" for Base USDC)\n version: string // EIP-712 domain version (e.g., \"2\" for USDC)\n}\n\n// EscrowPayload - the payload field in PaymentPayload\nexport interface EscrowPayload {\n authorization: {\n from: `0x${string}`\n to: `0x${string}`\n value: string\n validAfter: string\n validBefore: string\n nonce: `0x${string}`\n }\n signature: `0x${string}`\n paymentInfo: {\n operator: `0x${string}`\n receiver: `0x${string}`\n token: `0x${string}`\n maxAmount: string\n preApprovalExpiry: number\n authorizationExpiry: number\n refundExpiry: number\n minFeeBps: number\n maxFeeBps: number\n feeReceiver: `0x${string}`\n salt: `0x${string}`\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,kBAAkE;;;ACL3D,IAAM,eAAe;AACrB,IAAM,aAAa;AA2CnB,IAAM,8BAA8B;AAAA,EACzC,0BAA0B;AAAA,IACxB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC,EAAE,MAAM,MAAM,MAAM,UAAU;AAAA,IAC9B,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,IACjC,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,IACtC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,IACvC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,EACnC;AACF;;;ADxCA,IAAM,4BAAwB;AAAA,EAC5B,IAAI,YAAY,EAAE;AAAA,IAChB;AAAA,EACF;AACF;AAMO,SAAS,mBACd,SACA,eACA,aACe;AAEf,QAAM,yBAAqB;AAAA,IACzB;AAAA,MACE,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,MACpC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,MACpC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,MACpC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,MACrC,EAAE,MAAM,qBAAqB,MAAM,SAAS;AAAA,MAC5C,EAAE,MAAM,uBAAuB,MAAM,SAAS;AAAA,MAC9C,EAAE,MAAM,gBAAgB,MAAM,SAAS;AAAA,MACvC,EAAE,MAAM,aAAa,MAAM,SAAS;AAAA,MACpC,EAAE,MAAM,aAAa,MAAM,SAAS;AAAA,MACpC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,MACvC,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAClC;AAAA,IACA;AAAA,MACE;AAAA,MACA,YAAY;AAAA,MACZ;AAAA;AAAA,MACA,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,OAAO,YAAY,SAAS;AAAA,MAC5B,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,OAAO,YAAY,IAAI;AAAA,IACzB;AAAA,EACF;AACA,QAAM,sBAAkB,uBAAU,kBAAkB;AAGpD,QAAM,mBAAe;AAAA,IACnB;AAAA,MACE,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,MACnC,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,MAClC,EAAE,MAAM,mBAAmB,MAAM,UAAU;AAAA,IAC7C;AAAA,IACA,CAAC,OAAO,OAAO,GAAG,eAAe,eAAe;AAAA,EAClD;AAEA,aAAO,uBAAU,YAAY;AAC/B;AAMA,eAAsB,YACpB,QACA,eACA,OACA,cACA,SACwB;AAGxB,QAAM,SAAS;AAAA,IACb,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM;AAAA,IACf;AAAA,IACA,uBAAmB,wBAAW,YAAY;AAAA,EAC5C;AAEA,QAAM,UAAU;AAAA,IACd,UAAM,wBAAW,cAAc,IAAI;AAAA,IACnC,QAAI,wBAAW,cAAc,EAAE;AAAA,IAC/B,OAAO,OAAO,cAAc,KAAK;AAAA,IACjC,YAAY,OAAO,cAAc,UAAU;AAAA,IAC3C,aAAa,OAAO,cAAc,WAAW;AAAA,IAC7C,OAAO,cAAc;AAAA,EACvB;AAEA,SAAO,OAAO,cAAc;AAAA,IAC1B;AAAA,IACA,OAAO;AAAA,IACP,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AACH;AA2DO,SAAS,eAA8B;AAC5C,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,aAAO,mBAAM,KAAK;AACpB;;;AEzKO,SAAS,aAAa,SAAyB;AACpD,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,UAAU;AAC/C,UAAM,IAAI,MAAM,2BAA2B,OAAO,+BAA+B;AAAA,EACnF;AACA,QAAM,UAAU,SAAS,MAAM,CAAC,GAAG,EAAE;AACrC,MAAI,MAAM,OAAO,GAAG;AAClB,UAAM,IAAI,MAAM,+BAA+B,OAAO,EAAE;AAAA,EAC1D;AACA,SAAO;AACT;;;ACQO,IAAM,kBAAN,MAAqD;AAAA,EAG1D,YAA6B,QAAyB;AAAzB;AAF7B,wBAAS,UAAS;AAAA,EAEqC;AAAA,EAEvD,MAAM,qBACJ,aACA,cACA,UAC+B;AAC/B,QAAI,gBAAgB,GAAG;AACrB,YAAM,IAAI,MAAM,4BAA4B,WAAW,gCAAgC;AAAA,IACzF;AAEA,UAAM,QAAQ,aAAa;AAG3B,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI;AAAA,QACR,iFAAiF,aAAa,KAAK;AAAA,MACrG;AAAA,IACF;AACA,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,IAAI;AAAA,QACR,oFAAoF,aAAa,KAAK;AAAA,MACxG;AAAA,IACF;AAEA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI;AAEJ,UAAM,UAAU,aAAa,aAAa,OAAO;AACjD,UAAM,YAAY,aAAa;AAE/B,UAAM,cAAc;AAAA,MAClB,UAAU;AAAA,MACV,UAAU,aAAa;AAAA,MACvB,OAAO,aAAa;AAAA,MACpB;AAAA,MACA,mBAAmB,4BAA4B;AAAA,MAC/C,qBAAqB,8BAA8B;AAAA,MACnD,cAAc,uBAAuB;AAAA,MACrC;AAAA,MACA;AAAA,MACA,aAAa,eAAe;AAAA,MAC5B,MAAM,aAAa;AAAA,IACrB;AAEA,UAAM,QAAQ,mBAAmB,SAAS,eAAe,WAAW;AAIpE,UAAM,gBAAgB;AAAA,MACpB,MAAM,KAAK,OAAO;AAAA,MAClB,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,aAAa,OAAO,YAAY,iBAAiB;AAAA,MACjD;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AAAA,MACtB,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,SAAS,EAAE,eAAe,WAAW,YAAY;AAAA,IACnD;AAAA,EACF;AACF;;;ACvGO,SAAS,gBAAgB,OAAwC;AACtE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,mBAAmB,SACnB,eAAe,SACf,iBAAiB;AAErB;AAKO,SAAS,cAAc,OAAsC;AAClE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,mBAAmB,SACnB,qBAAqB,SACrB,oBAAoB;AAExB;","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SchemeNetworkClient, PaymentRequirements, PaymentPayloadContext, PaymentPayloadResult } from '@x402/core/types';
|
|
2
|
+
import { ClientEvmSigner } from '@x402/evm';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Escrow Scheme - Client
|
|
6
|
+
* Creates payment payloads for escrow payments.
|
|
7
|
+
*
|
|
8
|
+
* Implements x402's SchemeNetworkClient interface so it can be registered
|
|
9
|
+
* on an x402Client via client.register('eip155:84532', new EscrowEvmScheme(signer)).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Escrow Client Scheme - implements x402's SchemeNetworkClient
|
|
14
|
+
*/
|
|
15
|
+
declare class EscrowEvmScheme implements SchemeNetworkClient {
|
|
16
|
+
private readonly signer;
|
|
17
|
+
readonly scheme = "escrow";
|
|
18
|
+
constructor(signer: ClientEvmSigner);
|
|
19
|
+
createPaymentPayload(x402Version: number, requirements: PaymentRequirements, _context?: PaymentPayloadContext): Promise<PaymentPayloadResult>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { EscrowEvmScheme as E };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_UINT48,
|
|
3
|
+
computeEscrowNonce,
|
|
4
|
+
generateSalt,
|
|
5
|
+
parseChainId,
|
|
6
|
+
signERC3009
|
|
7
|
+
} from "./chunk-IYUU7AJZ.mjs";
|
|
8
|
+
import {
|
|
9
|
+
__publicField
|
|
10
|
+
} from "./chunk-NSSMTXJJ.mjs";
|
|
11
|
+
|
|
12
|
+
// src/escrow/client/scheme.ts
|
|
13
|
+
var EscrowEvmScheme = class {
|
|
14
|
+
constructor(signer) {
|
|
15
|
+
this.signer = signer;
|
|
16
|
+
__publicField(this, "scheme", "escrow");
|
|
17
|
+
}
|
|
18
|
+
async createPaymentPayload(x402Version, requirements, _context) {
|
|
19
|
+
if (x402Version !== 2) {
|
|
20
|
+
throw new Error(`Unsupported x402Version: ${x402Version}. Only version 2 is supported.`);
|
|
21
|
+
}
|
|
22
|
+
const extra = requirements.extra;
|
|
23
|
+
if (!extra.name) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`EIP-712 domain parameter 'name' is required in payment requirements for asset ${requirements.asset}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (!extra.version) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`EIP-712 domain parameter 'version' is required in payment requirements for asset ${requirements.asset}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const {
|
|
34
|
+
escrowAddress,
|
|
35
|
+
operatorAddress,
|
|
36
|
+
tokenCollector,
|
|
37
|
+
minFeeBps = 0,
|
|
38
|
+
maxFeeBps = 0,
|
|
39
|
+
feeReceiver,
|
|
40
|
+
preApprovalExpirySeconds,
|
|
41
|
+
refundExpirySeconds,
|
|
42
|
+
authorizationExpirySeconds
|
|
43
|
+
} = extra;
|
|
44
|
+
const chainId = parseChainId(requirements.network);
|
|
45
|
+
const maxAmount = requirements.amount;
|
|
46
|
+
const paymentInfo = {
|
|
47
|
+
operator: operatorAddress,
|
|
48
|
+
receiver: requirements.payTo,
|
|
49
|
+
token: requirements.asset,
|
|
50
|
+
maxAmount,
|
|
51
|
+
preApprovalExpiry: preApprovalExpirySeconds ?? MAX_UINT48,
|
|
52
|
+
authorizationExpiry: authorizationExpirySeconds ?? MAX_UINT48,
|
|
53
|
+
refundExpiry: refundExpirySeconds ?? MAX_UINT48,
|
|
54
|
+
minFeeBps,
|
|
55
|
+
maxFeeBps,
|
|
56
|
+
feeReceiver: feeReceiver ?? operatorAddress,
|
|
57
|
+
salt: generateSalt()
|
|
58
|
+
};
|
|
59
|
+
const nonce = computeEscrowNonce(chainId, escrowAddress, paymentInfo);
|
|
60
|
+
const authorization = {
|
|
61
|
+
from: this.signer.address,
|
|
62
|
+
to: tokenCollector,
|
|
63
|
+
value: maxAmount,
|
|
64
|
+
validAfter: "0",
|
|
65
|
+
validBefore: String(paymentInfo.preApprovalExpiry),
|
|
66
|
+
nonce
|
|
67
|
+
};
|
|
68
|
+
const signature = await signERC3009(
|
|
69
|
+
this.signer,
|
|
70
|
+
authorization,
|
|
71
|
+
extra,
|
|
72
|
+
requirements.asset,
|
|
73
|
+
chainId
|
|
74
|
+
);
|
|
75
|
+
return {
|
|
76
|
+
x402Version,
|
|
77
|
+
payload: { authorization, signature, paymentInfo }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
EscrowEvmScheme
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=chunk-DLIBGHEY.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/escrow/client/scheme.ts"],"sourcesContent":["/**\n * Escrow Scheme - Client\n * Creates payment payloads for escrow payments.\n *\n * Implements x402's SchemeNetworkClient interface so it can be registered\n * on an x402Client via client.register('eip155:84532', new EscrowEvmScheme(signer)).\n */\n\nimport type {\n PaymentPayloadContext,\n PaymentPayloadResult,\n PaymentRequirements,\n SchemeNetworkClient,\n} from '@x402/core/types'\nimport type { ClientEvmSigner } from '@x402/evm'\nimport { computeEscrowNonce, signERC3009, generateSalt } from '../shared/nonce'\nimport { MAX_UINT48 } from '../shared/constants'\nimport type { EscrowExtra } from '../shared/types'\nimport { parseChainId } from '../shared/utils'\n\n/**\n * Escrow Client Scheme - implements x402's SchemeNetworkClient\n */\nexport class EscrowEvmScheme implements SchemeNetworkClient {\n readonly scheme = 'escrow'\n\n constructor(private readonly signer: ClientEvmSigner) {}\n\n async createPaymentPayload(\n x402Version: number,\n requirements: PaymentRequirements,\n _context?: PaymentPayloadContext,\n ): Promise<PaymentPayloadResult> {\n if (x402Version !== 2) {\n throw new Error(`Unsupported x402Version: ${x402Version}. Only version 2 is supported.`)\n }\n\n const extra = requirements.extra as unknown as EscrowExtra\n\n // Validate required EIP-712 domain parameters (M3, M10)\n if (!extra.name) {\n throw new Error(\n `EIP-712 domain parameter 'name' is required in payment requirements for asset ${requirements.asset}`,\n )\n }\n if (!extra.version) {\n throw new Error(\n `EIP-712 domain parameter 'version' is required in payment requirements for asset ${requirements.asset}`,\n )\n }\n\n const {\n escrowAddress,\n operatorAddress,\n tokenCollector,\n minFeeBps = 0,\n maxFeeBps = 0,\n feeReceiver,\n preApprovalExpirySeconds,\n refundExpirySeconds,\n authorizationExpirySeconds,\n } = extra\n\n const chainId = parseChainId(requirements.network)\n const maxAmount = requirements.amount\n\n const paymentInfo = {\n operator: operatorAddress,\n receiver: requirements.payTo as `0x${string}`,\n token: requirements.asset as `0x${string}`,\n maxAmount,\n preApprovalExpiry: preApprovalExpirySeconds ?? MAX_UINT48,\n authorizationExpiry: authorizationExpirySeconds ?? MAX_UINT48,\n refundExpiry: refundExpirySeconds ?? MAX_UINT48,\n minFeeBps,\n maxFeeBps,\n feeReceiver: feeReceiver ?? operatorAddress,\n salt: generateSalt(),\n }\n\n const nonce = computeEscrowNonce(chainId, escrowAddress, paymentInfo)\n\n // ERC-3009 authorization - validBefore MUST match what contract passes to receiveWithAuthorization\n // The contract uses paymentInfo.preApprovalExpiry as validBefore\n const authorization = {\n from: this.signer.address,\n to: tokenCollector,\n value: maxAmount,\n validAfter: '0',\n validBefore: String(paymentInfo.preApprovalExpiry),\n nonce,\n }\n\n const signature = await signERC3009(\n this.signer,\n authorization,\n extra,\n requirements.asset as `0x${string}`,\n chainId,\n )\n\n return {\n x402Version,\n payload: { authorization, signature, paymentInfo },\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;AAuBO,IAAM,kBAAN,MAAqD;AAAA,EAG1D,YAA6B,QAAyB;AAAzB;AAF7B,wBAAS,UAAS;AAAA,EAEqC;AAAA,EAEvD,MAAM,qBACJ,aACA,cACA,UAC+B;AAC/B,QAAI,gBAAgB,GAAG;AACrB,YAAM,IAAI,MAAM,4BAA4B,WAAW,gCAAgC;AAAA,IACzF;AAEA,UAAM,QAAQ,aAAa;AAG3B,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI;AAAA,QACR,iFAAiF,aAAa,KAAK;AAAA,MACrG;AAAA,IACF;AACA,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,IAAI;AAAA,QACR,oFAAoF,aAAa,KAAK;AAAA,MACxG;AAAA,IACF;AAEA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI;AAEJ,UAAM,UAAU,aAAa,aAAa,OAAO;AACjD,UAAM,YAAY,aAAa;AAE/B,UAAM,cAAc;AAAA,MAClB,UAAU;AAAA,MACV,UAAU,aAAa;AAAA,MACvB,OAAO,aAAa;AAAA,MACpB;AAAA,MACA,mBAAmB,4BAA4B;AAAA,MAC/C,qBAAqB,8BAA8B;AAAA,MACnD,cAAc,uBAAuB;AAAA,MACrC;AAAA,MACA;AAAA,MACA,aAAa,eAAe;AAAA,MAC5B,MAAM,aAAa;AAAA,IACrB;AAEA,UAAM,QAAQ,mBAAmB,SAAS,eAAe,WAAW;AAIpE,UAAM,gBAAgB;AAAA,MACpB,MAAM,KAAK,OAAO;AAAA,MAClB,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,aAAa,OAAO,YAAY,iBAAiB;AAAA,MACjD;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AAAA,MACtB,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,SAAS,EAAE,eAAe,WAAW,YAAY;AAAA,IACnD;AAAA,EACF;AACF;","names":[]}
|