@x402r/evm 0.0.1
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 +44 -0
- package/dist/escrow/client/index.d.ts +24 -0
- package/dist/escrow/client/index.d.ts.map +1 -0
- package/dist/escrow/client/index.js +45 -0
- package/dist/escrow/client/index.js.map +1 -0
- package/dist/escrow/facilitator/index.d.ts +49 -0
- package/dist/escrow/facilitator/index.d.ts.map +1 -0
- package/dist/escrow/facilitator/index.js +146 -0
- package/dist/escrow/facilitator/index.js.map +1 -0
- package/dist/escrow/server/index.d.ts +68 -0
- package/dist/escrow/server/index.d.ts.map +1 -0
- package/dist/escrow/server/index.js +80 -0
- package/dist/escrow/server/index.js.map +1 -0
- package/dist/shared/constants.d.ts +98 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +39 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/nonce.d.ts +41 -0
- package/dist/shared/nonce.d.ts.map +1 -0
- package/dist/shared/nonce.js +155 -0
- package/dist/shared/nonce.js.map +1 -0
- package/dist/shared/types.d.ts +41 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +58 -0
- package/src/escrow/client/index.ts +88 -0
- package/src/escrow/facilitator/index.ts +204 -0
- package/src/escrow/server/index.ts +133 -0
- package/src/shared/constants.ts +43 -0
- package/src/shared/nonce.ts +202 -0
- package/src/shared/types.ts +43 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nonce computation and ERC-3009 signing utilities
|
|
3
|
+
* Adapted from @agentokratia/x402-escrow (MIT)
|
|
4
|
+
*/
|
|
5
|
+
import { type WalletClient } from "viem";
|
|
6
|
+
import type { EscrowExtra, EscrowPayload } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Compute escrow nonce for ERC-3009 authorization
|
|
9
|
+
* Must match AuthCaptureEscrow.getHash() with payer=address(0)
|
|
10
|
+
*/
|
|
11
|
+
export declare function computeEscrowNonce(chainId: number, escrowAddress: `0x${string}`, paymentInfo: EscrowPayload["paymentInfo"]): `0x${string}`;
|
|
12
|
+
/**
|
|
13
|
+
* Sign ERC-3009 ReceiveWithAuthorization
|
|
14
|
+
* Note: receiveWithAuthorization uses a different primary type than transferWithAuthorization
|
|
15
|
+
*/
|
|
16
|
+
export declare function signERC3009(wallet: WalletClient, authorization: EscrowPayload["authorization"], extra: EscrowExtra, tokenAddress: `0x${string}`): Promise<`0x${string}`>;
|
|
17
|
+
/**
|
|
18
|
+
* Verify ERC-3009 signature (facilitator-side)
|
|
19
|
+
* @param signer - The signer with verifyTypedData method
|
|
20
|
+
* @param authorization - ERC-3009 authorization data
|
|
21
|
+
* @param signature - The signature to verify
|
|
22
|
+
* @param extra - Extra configuration including chainId
|
|
23
|
+
* @param tokenAddress - The token contract address (verifyingContract for EIP-712)
|
|
24
|
+
*/
|
|
25
|
+
export declare function verifyERC3009Signature(signer: {
|
|
26
|
+
verifyTypedData: (args: {
|
|
27
|
+
address: `0x${string}`;
|
|
28
|
+
domain: Record<string, unknown>;
|
|
29
|
+
types: Record<string, unknown>;
|
|
30
|
+
primaryType: string;
|
|
31
|
+
message: Record<string, unknown>;
|
|
32
|
+
signature: `0x${string}`;
|
|
33
|
+
}) => Promise<boolean>;
|
|
34
|
+
}, authorization: EscrowPayload["authorization"], signature: `0x${string}`, extra: EscrowExtra & {
|
|
35
|
+
chainId: number;
|
|
36
|
+
}, tokenAddress: `0x${string}`): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Generate random salt for paymentInfo
|
|
39
|
+
*/
|
|
40
|
+
export declare function generateSalt(): `0x${string}`;
|
|
41
|
+
//# sourceMappingURL=nonce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nonce.d.ts","sourceRoot":"","sources":["../../src/shared/nonce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAW7D;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,KAAK,MAAM,EAAE,EAC5B,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,GACxC,KAAK,MAAM,EAAE,CA+Cf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,EAC7C,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,KAAK,MAAM,EAAE,GAC1B,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC,CAsCxB;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE;IACN,eAAe,EAAE,CAAC,IAAI,EAAE;QACtB,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;KAC1B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB,EACD,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,EAC7C,SAAS,EAAE,KAAK,MAAM,EAAE,EACxB,KAAK,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EACxC,YAAY,EAAE,KAAK,MAAM,EAAE,GAC1B,OAAO,CAAC,OAAO,CAAC,CA2ClB;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,KAAK,MAAM,EAAE,CAM5C"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nonce computation and ERC-3009 signing utilities
|
|
3
|
+
* Adapted from @agentokratia/x402-escrow (MIT)
|
|
4
|
+
*/
|
|
5
|
+
import { encodeAbiParameters, keccak256 } from "viem";
|
|
6
|
+
import { ZERO_ADDRESS } from "./constants.js";
|
|
7
|
+
/**
|
|
8
|
+
* PaymentInfo typehash - must match AuthCaptureEscrow.PAYMENT_INFO_TYPEHASH
|
|
9
|
+
*/
|
|
10
|
+
const PAYMENT_INFO_TYPEHASH = keccak256(new TextEncoder().encode("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)"));
|
|
11
|
+
/**
|
|
12
|
+
* Compute escrow nonce for ERC-3009 authorization
|
|
13
|
+
* Must match AuthCaptureEscrow.getHash() with payer=address(0)
|
|
14
|
+
*/
|
|
15
|
+
export function computeEscrowNonce(chainId, escrowAddress, paymentInfo) {
|
|
16
|
+
// Step 1: Encode paymentInfo with payer=0 (payer-agnostic)
|
|
17
|
+
const paymentInfoEncoded = encodeAbiParameters([
|
|
18
|
+
{ name: "typehash", type: "bytes32" },
|
|
19
|
+
{ name: "operator", type: "address" },
|
|
20
|
+
{ name: "payer", type: "address" },
|
|
21
|
+
{ name: "receiver", type: "address" },
|
|
22
|
+
{ name: "token", type: "address" },
|
|
23
|
+
{ name: "maxAmount", type: "uint120" },
|
|
24
|
+
{ name: "preApprovalExpiry", type: "uint48" },
|
|
25
|
+
{ name: "authorizationExpiry", type: "uint48" },
|
|
26
|
+
{ name: "refundExpiry", type: "uint48" },
|
|
27
|
+
{ name: "minFeeBps", type: "uint16" },
|
|
28
|
+
{ name: "maxFeeBps", type: "uint16" },
|
|
29
|
+
{ name: "feeReceiver", type: "address" },
|
|
30
|
+
{ name: "salt", type: "uint256" },
|
|
31
|
+
], [
|
|
32
|
+
PAYMENT_INFO_TYPEHASH,
|
|
33
|
+
paymentInfo.operator,
|
|
34
|
+
ZERO_ADDRESS, // payer-agnostic
|
|
35
|
+
paymentInfo.receiver,
|
|
36
|
+
paymentInfo.token,
|
|
37
|
+
BigInt(paymentInfo.maxAmount),
|
|
38
|
+
paymentInfo.preApprovalExpiry,
|
|
39
|
+
paymentInfo.authorizationExpiry,
|
|
40
|
+
paymentInfo.refundExpiry,
|
|
41
|
+
paymentInfo.minFeeBps,
|
|
42
|
+
paymentInfo.maxFeeBps,
|
|
43
|
+
paymentInfo.feeReceiver,
|
|
44
|
+
BigInt(paymentInfo.salt),
|
|
45
|
+
]);
|
|
46
|
+
const paymentInfoHash = keccak256(paymentInfoEncoded);
|
|
47
|
+
// Step 2: Encode (chainId, escrow, paymentInfoHash) and hash
|
|
48
|
+
const outerEncoded = encodeAbiParameters([
|
|
49
|
+
{ name: "chainId", type: "uint256" },
|
|
50
|
+
{ name: "escrow", type: "address" },
|
|
51
|
+
{ name: "paymentInfoHash", type: "bytes32" },
|
|
52
|
+
], [BigInt(chainId), escrowAddress, paymentInfoHash]);
|
|
53
|
+
return keccak256(outerEncoded);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sign ERC-3009 ReceiveWithAuthorization
|
|
57
|
+
* Note: receiveWithAuthorization uses a different primary type than transferWithAuthorization
|
|
58
|
+
*/
|
|
59
|
+
export async function signERC3009(wallet, authorization, extra, tokenAddress) {
|
|
60
|
+
// EIP-712 domain - name must match the token's EIP-712 domain
|
|
61
|
+
// (e.g., "USDC" for Base USDC, not "USD Coin")
|
|
62
|
+
const domain = {
|
|
63
|
+
name: extra.name,
|
|
64
|
+
version: extra.version ?? "2",
|
|
65
|
+
chainId: await wallet.getChainId(),
|
|
66
|
+
verifyingContract: tokenAddress,
|
|
67
|
+
};
|
|
68
|
+
// ERC-3009 uses ReceiveWithAuthorization for receiveWithAuthorization()
|
|
69
|
+
const types = {
|
|
70
|
+
ReceiveWithAuthorization: [
|
|
71
|
+
{ name: "from", type: "address" },
|
|
72
|
+
{ name: "to", type: "address" },
|
|
73
|
+
{ name: "value", type: "uint256" },
|
|
74
|
+
{ name: "validAfter", type: "uint256" },
|
|
75
|
+
{ name: "validBefore", type: "uint256" },
|
|
76
|
+
{ name: "nonce", type: "bytes32" },
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
const message = {
|
|
80
|
+
from: authorization.from,
|
|
81
|
+
to: authorization.to,
|
|
82
|
+
value: BigInt(authorization.value),
|
|
83
|
+
validAfter: BigInt(authorization.validAfter),
|
|
84
|
+
validBefore: BigInt(authorization.validBefore),
|
|
85
|
+
nonce: authorization.nonce,
|
|
86
|
+
};
|
|
87
|
+
return wallet.signTypedData({
|
|
88
|
+
account: wallet.account,
|
|
89
|
+
domain,
|
|
90
|
+
types,
|
|
91
|
+
primaryType: "ReceiveWithAuthorization",
|
|
92
|
+
message,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Verify ERC-3009 signature (facilitator-side)
|
|
97
|
+
* @param signer - The signer with verifyTypedData method
|
|
98
|
+
* @param authorization - ERC-3009 authorization data
|
|
99
|
+
* @param signature - The signature to verify
|
|
100
|
+
* @param extra - Extra configuration including chainId
|
|
101
|
+
* @param tokenAddress - The token contract address (verifyingContract for EIP-712)
|
|
102
|
+
*/
|
|
103
|
+
export async function verifyERC3009Signature(signer, authorization, signature, extra, tokenAddress) {
|
|
104
|
+
// EIP-712 domain - name must match the token's EIP-712 domain
|
|
105
|
+
// (e.g., "USDC" for Base USDC, not "USD Coin")
|
|
106
|
+
const domain = {
|
|
107
|
+
name: extra.name,
|
|
108
|
+
version: extra.version ?? "2",
|
|
109
|
+
chainId: extra.chainId,
|
|
110
|
+
verifyingContract: tokenAddress,
|
|
111
|
+
};
|
|
112
|
+
// Must use ReceiveWithAuthorization to match what was signed
|
|
113
|
+
const types = {
|
|
114
|
+
ReceiveWithAuthorization: [
|
|
115
|
+
{ name: "from", type: "address" },
|
|
116
|
+
{ name: "to", type: "address" },
|
|
117
|
+
{ name: "value", type: "uint256" },
|
|
118
|
+
{ name: "validAfter", type: "uint256" },
|
|
119
|
+
{ name: "validBefore", type: "uint256" },
|
|
120
|
+
{ name: "nonce", type: "bytes32" },
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
const message = {
|
|
124
|
+
from: authorization.from,
|
|
125
|
+
to: authorization.to,
|
|
126
|
+
value: BigInt(authorization.value),
|
|
127
|
+
validAfter: BigInt(authorization.validAfter),
|
|
128
|
+
validBefore: BigInt(authorization.validBefore),
|
|
129
|
+
nonce: authorization.nonce,
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
return await signer.verifyTypedData({
|
|
133
|
+
address: authorization.from,
|
|
134
|
+
domain,
|
|
135
|
+
types,
|
|
136
|
+
primaryType: "ReceiveWithAuthorization",
|
|
137
|
+
message,
|
|
138
|
+
signature,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Generate random salt for paymentInfo
|
|
147
|
+
*/
|
|
148
|
+
export function generateSalt() {
|
|
149
|
+
const bytes = new Uint8Array(32);
|
|
150
|
+
crypto.getRandomValues(bytes);
|
|
151
|
+
return `0x${Array.from(bytes)
|
|
152
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
153
|
+
.join("")}`;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=nonce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nonce.js","sourceRoot":"","sources":["../../src/shared/nonce.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface EscrowExtra {
|
|
2
|
+
escrowAddress: `0x${string}`;
|
|
3
|
+
operatorAddress: `0x${string}`;
|
|
4
|
+
tokenCollector: `0x${string}`;
|
|
5
|
+
authorizeAddress?: `0x${string}`;
|
|
6
|
+
minDeposit?: string;
|
|
7
|
+
maxDeposit?: string;
|
|
8
|
+
preApprovalExpirySeconds?: number;
|
|
9
|
+
authorizationExpirySeconds?: number;
|
|
10
|
+
refundExpirySeconds?: number;
|
|
11
|
+
minFeeBps?: number;
|
|
12
|
+
maxFeeBps?: number;
|
|
13
|
+
feeReceiver?: `0x${string}`;
|
|
14
|
+
name: string;
|
|
15
|
+
version?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface EscrowPayload {
|
|
18
|
+
authorization: {
|
|
19
|
+
from: `0x${string}`;
|
|
20
|
+
to: `0x${string}`;
|
|
21
|
+
value: string;
|
|
22
|
+
validAfter: string;
|
|
23
|
+
validBefore: string;
|
|
24
|
+
nonce: `0x${string}`;
|
|
25
|
+
};
|
|
26
|
+
signature: `0x${string}`;
|
|
27
|
+
paymentInfo: {
|
|
28
|
+
operator: `0x${string}`;
|
|
29
|
+
receiver: `0x${string}`;
|
|
30
|
+
token: `0x${string}`;
|
|
31
|
+
maxAmount: string;
|
|
32
|
+
preApprovalExpiry: number;
|
|
33
|
+
authorizationExpiry: number;
|
|
34
|
+
refundExpiry: number;
|
|
35
|
+
minFeeBps: number;
|
|
36
|
+
maxFeeBps: number;
|
|
37
|
+
feeReceiver: `0x${string}`;
|
|
38
|
+
salt: `0x${string}`;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,KAAK,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,KAAK,MAAM,EAAE,CAAC;IAC/B,cAAc,EAAE,KAAK,MAAM,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE;QACb,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;QACpB,EAAE,EAAE,KAAK,MAAM,EAAE,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;KACtB,CAAC;IACF,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE;QACX,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAC;QACxB,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAC;QACxB,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,mBAAmB,EAAE,MAAM,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAC3B,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;KACrB,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@x402r/evm",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Escrow payment scheme for x402 using Base Commerce Payments",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "x402r",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/BackTrackCo/x402r-scheme.git",
|
|
11
|
+
"directory": "packages/evm"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
"./escrow/client": {
|
|
15
|
+
"types": "./dist/escrow/client/index.d.ts",
|
|
16
|
+
"import": "./dist/escrow/client/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./escrow/server": {
|
|
19
|
+
"types": "./dist/escrow/server/index.d.ts",
|
|
20
|
+
"import": "./dist/escrow/server/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./escrow/facilitator": {
|
|
23
|
+
"types": "./dist/escrow/facilitator/index.d.ts",
|
|
24
|
+
"import": "./dist/escrow/facilitator/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./escrow/types": {
|
|
27
|
+
"types": "./dist/shared/types.d.ts",
|
|
28
|
+
"import": "./dist/shared/types.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"src",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"clean": "rm -rf dist",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"lint": "eslint src --ext .ts",
|
|
46
|
+
"format": "prettier --write \"src/**/*.ts\""
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@x402/core": "^2.0.0",
|
|
50
|
+
"@x402/evm": "^2.0.0",
|
|
51
|
+
"viem": "^2.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^22.0.0",
|
|
55
|
+
"typescript": "^5.7.0",
|
|
56
|
+
"vitest": "^4.0.18"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escrow Scheme - Client
|
|
3
|
+
* Creates payment payloads for escrow payments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { WalletClient } from "viem";
|
|
7
|
+
import {
|
|
8
|
+
computeEscrowNonce,
|
|
9
|
+
signERC3009,
|
|
10
|
+
generateSalt,
|
|
11
|
+
} from "../../shared/nonce.js";
|
|
12
|
+
import { MAX_UINT48 } from "../../shared/constants.js";
|
|
13
|
+
import type { EscrowExtra, EscrowPayload } from "../../shared/types.js";
|
|
14
|
+
|
|
15
|
+
export interface PaymentRequirements {
|
|
16
|
+
scheme: string;
|
|
17
|
+
network: string;
|
|
18
|
+
amount: string;
|
|
19
|
+
asset: `0x${string}`;
|
|
20
|
+
payTo: `0x${string}`;
|
|
21
|
+
extra: EscrowExtra;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create an escrow payment payload from payment requirements
|
|
26
|
+
*/
|
|
27
|
+
export async function createPaymentPayload(
|
|
28
|
+
requirements: PaymentRequirements,
|
|
29
|
+
wallet: WalletClient,
|
|
30
|
+
): Promise<EscrowPayload> {
|
|
31
|
+
const {
|
|
32
|
+
escrowAddress,
|
|
33
|
+
operatorAddress,
|
|
34
|
+
tokenCollector,
|
|
35
|
+
minFeeBps = 0,
|
|
36
|
+
maxFeeBps = 0,
|
|
37
|
+
feeReceiver,
|
|
38
|
+
preApprovalExpirySeconds,
|
|
39
|
+
refundExpirySeconds,
|
|
40
|
+
authorizationExpirySeconds,
|
|
41
|
+
} = requirements.extra;
|
|
42
|
+
|
|
43
|
+
const chainId = await wallet.getChainId();
|
|
44
|
+
const maxAmount = requirements.amount;
|
|
45
|
+
|
|
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
|
+
|
|
60
|
+
const nonce = computeEscrowNonce(chainId, escrowAddress, paymentInfo);
|
|
61
|
+
|
|
62
|
+
// ERC-3009 authorization - validBefore MUST match what contract passes to receiveWithAuthorization
|
|
63
|
+
// The contract uses paymentInfo.preApprovalExpiry as validBefore
|
|
64
|
+
const authorization = {
|
|
65
|
+
from: wallet.account!.address,
|
|
66
|
+
to: tokenCollector,
|
|
67
|
+
value: maxAmount,
|
|
68
|
+
validAfter: "0",
|
|
69
|
+
validBefore: String(paymentInfo.preApprovalExpiry),
|
|
70
|
+
nonce,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const signature = await signERC3009(
|
|
74
|
+
wallet,
|
|
75
|
+
authorization,
|
|
76
|
+
requirements.extra,
|
|
77
|
+
requirements.asset,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return { authorization, signature, paymentInfo };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const EscrowScheme = {
|
|
84
|
+
scheme: "escrow" as const,
|
|
85
|
+
createPaymentPayload,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type { EscrowExtra, EscrowPayload } from "../../shared/types.js";
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escrow Scheme - Facilitator
|
|
3
|
+
* Handles verification and settlement of escrow payments.
|
|
4
|
+
*
|
|
5
|
+
* Implements x402's SchemeNetworkFacilitator interface so the escrow scheme
|
|
6
|
+
* is a drop-in for the x402 facilitator, just like ExactEvmScheme.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
Network,
|
|
11
|
+
PaymentPayload,
|
|
12
|
+
PaymentRequirements,
|
|
13
|
+
SchemeNetworkFacilitator,
|
|
14
|
+
SettleResponse,
|
|
15
|
+
VerifyResponse,
|
|
16
|
+
} from "@x402/core/types";
|
|
17
|
+
import type { FacilitatorEvmSigner } from "@x402/evm";
|
|
18
|
+
import { x402Facilitator } from "@x402/core/facilitator";
|
|
19
|
+
import { OPERATOR_ABI } from "../../shared/constants.js";
|
|
20
|
+
import { verifyERC3009Signature } from "../../shared/nonce.js";
|
|
21
|
+
import type { EscrowExtra, EscrowPayload } from "../../shared/types.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse chainId from CAIP-2 network identifier
|
|
25
|
+
* @param network - CAIP-2 network identifier (e.g., 'eip155:84532')
|
|
26
|
+
* @returns The chain ID as a number
|
|
27
|
+
*/
|
|
28
|
+
function parseChainId(network: string): number {
|
|
29
|
+
const parts = network.split(":");
|
|
30
|
+
if (parts.length !== 2 || parts[0] !== "eip155") {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Invalid network format: ${network}. Expected 'eip155:<chainId>'`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const chainId = parseInt(parts[1], 10);
|
|
36
|
+
if (isNaN(chainId)) {
|
|
37
|
+
throw new Error(`Invalid chainId in network: ${network}`);
|
|
38
|
+
}
|
|
39
|
+
return chainId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Escrow Facilitator Scheme - implements x402's SchemeNetworkFacilitator
|
|
44
|
+
*
|
|
45
|
+
* The facilitator is operator-agnostic: it does not store operator/escrow/tokenCollector
|
|
46
|
+
* config. Those values are set by the merchant via `refundable()` and arrive in
|
|
47
|
+
* `requirements.extra` at verify/settle time.
|
|
48
|
+
*/
|
|
49
|
+
export class EscrowFacilitatorScheme implements SchemeNetworkFacilitator {
|
|
50
|
+
readonly scheme = "escrow";
|
|
51
|
+
readonly caipFamily = "eip155:*";
|
|
52
|
+
|
|
53
|
+
constructor(private signer: FacilitatorEvmSigner) {}
|
|
54
|
+
|
|
55
|
+
getSigners(_network: string): string[] {
|
|
56
|
+
return [...this.signer.getAddresses()];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getExtra(_network: string): Record<string, unknown> {
|
|
60
|
+
return { name: "USDC", version: "2" };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async verify(
|
|
64
|
+
payload: PaymentPayload,
|
|
65
|
+
requirements: PaymentRequirements,
|
|
66
|
+
): Promise<VerifyResponse> {
|
|
67
|
+
const escrowPayload = payload.payload as unknown as EscrowPayload;
|
|
68
|
+
const extra = requirements.extra as unknown as EscrowExtra;
|
|
69
|
+
const chainId = parseChainId(requirements.network);
|
|
70
|
+
|
|
71
|
+
// Verify ERC-3009 signature
|
|
72
|
+
const isValidSignature = await verifyERC3009Signature(
|
|
73
|
+
this.signer,
|
|
74
|
+
escrowPayload.authorization,
|
|
75
|
+
escrowPayload.signature,
|
|
76
|
+
{ ...extra, chainId },
|
|
77
|
+
requirements.asset as `0x${string}`,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!isValidSignature) {
|
|
81
|
+
return { isValid: false, invalidReason: "Invalid ERC-3009 signature" };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Verify amount meets requirements
|
|
85
|
+
if (
|
|
86
|
+
BigInt(escrowPayload.authorization.value) <
|
|
87
|
+
BigInt(requirements.amount)
|
|
88
|
+
) {
|
|
89
|
+
return { isValid: false, invalidReason: "Insufficient payment amount" };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Verify token matches
|
|
93
|
+
if (
|
|
94
|
+
escrowPayload.paymentInfo.token.toLowerCase() !==
|
|
95
|
+
requirements.asset.toLowerCase()
|
|
96
|
+
) {
|
|
97
|
+
return { isValid: false, invalidReason: "Token mismatch" };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Verify receiver matches
|
|
101
|
+
if (
|
|
102
|
+
escrowPayload.paymentInfo.receiver.toLowerCase() !==
|
|
103
|
+
requirements.payTo.toLowerCase()
|
|
104
|
+
) {
|
|
105
|
+
return { isValid: false, invalidReason: "Receiver mismatch" };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
isValid: true,
|
|
110
|
+
payer: escrowPayload.authorization.from,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async settle(
|
|
115
|
+
payload: PaymentPayload,
|
|
116
|
+
requirements: PaymentRequirements,
|
|
117
|
+
): Promise<SettleResponse> {
|
|
118
|
+
const escrowPayload = payload.payload as unknown as EscrowPayload;
|
|
119
|
+
const extra = requirements.extra as unknown as EscrowExtra;
|
|
120
|
+
const { authorizeAddress, operatorAddress, tokenCollector } = extra;
|
|
121
|
+
|
|
122
|
+
const paymentInfo = {
|
|
123
|
+
operator: escrowPayload.paymentInfo.operator,
|
|
124
|
+
payer: escrowPayload.authorization.from,
|
|
125
|
+
receiver: escrowPayload.paymentInfo.receiver,
|
|
126
|
+
token: escrowPayload.paymentInfo.token,
|
|
127
|
+
maxAmount: BigInt(escrowPayload.paymentInfo.maxAmount),
|
|
128
|
+
preApprovalExpiry: escrowPayload.paymentInfo.preApprovalExpiry,
|
|
129
|
+
authorizationExpiry: escrowPayload.paymentInfo.authorizationExpiry,
|
|
130
|
+
refundExpiry: escrowPayload.paymentInfo.refundExpiry,
|
|
131
|
+
minFeeBps: escrowPayload.paymentInfo.minFeeBps,
|
|
132
|
+
maxFeeBps: escrowPayload.paymentInfo.maxFeeBps,
|
|
133
|
+
feeReceiver: escrowPayload.paymentInfo.feeReceiver,
|
|
134
|
+
salt: BigInt(escrowPayload.paymentInfo.salt),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Pass raw signature - ERC3009PaymentCollector expects raw bytes, not ABI-encoded
|
|
138
|
+
const collectorData = escrowPayload.signature;
|
|
139
|
+
|
|
140
|
+
const target = authorizeAddress ?? operatorAddress;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const txHash = await this.signer.writeContract({
|
|
144
|
+
address: target,
|
|
145
|
+
abi: OPERATOR_ABI,
|
|
146
|
+
functionName: "authorize",
|
|
147
|
+
args: [
|
|
148
|
+
paymentInfo,
|
|
149
|
+
BigInt(escrowPayload.authorization.value),
|
|
150
|
+
tokenCollector,
|
|
151
|
+
collectorData,
|
|
152
|
+
],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
transaction: txHash,
|
|
158
|
+
network: requirements.network,
|
|
159
|
+
payer: escrowPayload.authorization.from,
|
|
160
|
+
};
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
errorReason:
|
|
165
|
+
error instanceof Error ? error.message : "Settlement failed",
|
|
166
|
+
transaction: "",
|
|
167
|
+
network: requirements.network,
|
|
168
|
+
payer: escrowPayload.authorization.from,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Register escrow scheme with x402Facilitator
|
|
176
|
+
*
|
|
177
|
+
* The facilitator is operator-agnostic — it supports any operator. Operator,
|
|
178
|
+
* escrow, and tokenCollector addresses are provided per-request by the merchant
|
|
179
|
+
* via `refundable()` and arrive in `requirements.extra`.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const facilitator = new x402Facilitator();
|
|
184
|
+
* registerEscrowScheme(facilitator, {
|
|
185
|
+
* signer: evmSigner,
|
|
186
|
+
* networks: "eip155:84532",
|
|
187
|
+
* });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export function registerEscrowScheme(
|
|
191
|
+
facilitator: x402Facilitator,
|
|
192
|
+
config: {
|
|
193
|
+
signer: FacilitatorEvmSigner;
|
|
194
|
+
networks: Network | Network[];
|
|
195
|
+
},
|
|
196
|
+
): x402Facilitator {
|
|
197
|
+
facilitator.register(
|
|
198
|
+
config.networks,
|
|
199
|
+
new EscrowFacilitatorScheme(config.signer),
|
|
200
|
+
);
|
|
201
|
+
return facilitator;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export type { EscrowExtra, EscrowPayload } from "../../shared/types.js";
|