@x402x/facilitator-sdk 2.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.
- package/README.md +307 -0
- package/dist/index.d.mts +189 -0
- package/dist/index.d.ts +189 -0
- package/dist/index.js +994 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +948 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
import { FacilitatorValidationError, toCanonicalNetworkKey, getNetworkName, getNetworkConfig, SETTLEMENT_ROUTER_ABI, isSettlementMode, parseSettlementExtra, SettlementRouterError, calculateCommitment } from '@x402x/extensions';
|
|
2
|
+
export { FacilitatorValidationError, SETTLEMENT_ROUTER_ABI, SettlementRouterError, getNetworkConfig, isSettlementMode, parseSettlementExtra } from '@x402x/extensions';
|
|
3
|
+
import { createPublicClient, http, createWalletClient, parseErc6492Signature, verifyTypedData } from 'viem';
|
|
4
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
5
|
+
|
|
6
|
+
// src/facilitator.ts
|
|
7
|
+
function isValidEthereumAddress(address) {
|
|
8
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
9
|
+
}
|
|
10
|
+
function isValidHex(hex) {
|
|
11
|
+
return /^0x[a-fA-F0-9]*$/.test(hex) && hex.length % 2 === 0 && hex.length >= 2;
|
|
12
|
+
}
|
|
13
|
+
function isValid32ByteHex(hex) {
|
|
14
|
+
return /^0x[a-fA-F0-9]{64}$/.test(hex);
|
|
15
|
+
}
|
|
16
|
+
function isValid256BitHex(hex) {
|
|
17
|
+
return /^0x[a-fA-F0-9]{1,64}$/.test(hex);
|
|
18
|
+
}
|
|
19
|
+
function validateSettlementRouter(network, router, allowedRouters, networkConfig) {
|
|
20
|
+
if (!isValidEthereumAddress(router)) {
|
|
21
|
+
throw new FacilitatorValidationError(`Invalid SettlementRouter address: ${router}`);
|
|
22
|
+
}
|
|
23
|
+
if (allowedRouters && network in allowedRouters) {
|
|
24
|
+
const networkAllowedRouters = allowedRouters[network];
|
|
25
|
+
if (networkAllowedRouters.length > 0 && !networkAllowedRouters.includes(router)) {
|
|
26
|
+
throw new FacilitatorValidationError(
|
|
27
|
+
`SettlementRouter ${router} not allowed for network ${network}. Allowed routers: ${networkAllowedRouters.join(", ")}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (networkConfig?.settlementRouter && router !== networkConfig.settlementRouter) {
|
|
32
|
+
throw new FacilitatorValidationError(
|
|
33
|
+
`SettlementRouter ${router} does not match network config expected router ${networkConfig.settlementRouter}`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return router;
|
|
37
|
+
}
|
|
38
|
+
function validateSettlementExtra(extra) {
|
|
39
|
+
if (!extra || typeof extra !== "object") {
|
|
40
|
+
throw new FacilitatorValidationError("Missing or invalid extra field");
|
|
41
|
+
}
|
|
42
|
+
const e = extra;
|
|
43
|
+
if (!e.settlementRouter || typeof e.settlementRouter !== "string") {
|
|
44
|
+
throw new FacilitatorValidationError("Missing or invalid settlementRouter");
|
|
45
|
+
}
|
|
46
|
+
if (!isValidEthereumAddress(e.settlementRouter)) {
|
|
47
|
+
throw new FacilitatorValidationError("Invalid settlementRouter address format");
|
|
48
|
+
}
|
|
49
|
+
if (!e.salt || typeof e.salt !== "string") {
|
|
50
|
+
throw new FacilitatorValidationError("Missing or invalid salt");
|
|
51
|
+
}
|
|
52
|
+
if (!isValid32ByteHex(e.salt)) {
|
|
53
|
+
throw new FacilitatorValidationError("Salt must be a 32-byte hex string");
|
|
54
|
+
}
|
|
55
|
+
if (!e.payTo || typeof e.payTo !== "string") {
|
|
56
|
+
throw new FacilitatorValidationError("Missing or invalid payTo");
|
|
57
|
+
}
|
|
58
|
+
if (!isValidEthereumAddress(e.payTo)) {
|
|
59
|
+
throw new FacilitatorValidationError("Invalid payTo address format");
|
|
60
|
+
}
|
|
61
|
+
if (!e.facilitatorFee || typeof e.facilitatorFee !== "string") {
|
|
62
|
+
throw new FacilitatorValidationError("Missing or invalid facilitatorFee");
|
|
63
|
+
}
|
|
64
|
+
if (!isValid256BitHex(e.facilitatorFee)) {
|
|
65
|
+
throw new FacilitatorValidationError("Facilitator fee must be a valid hex number");
|
|
66
|
+
}
|
|
67
|
+
if (!e.hook || typeof e.hook !== "string") {
|
|
68
|
+
throw new FacilitatorValidationError("Missing or invalid hook");
|
|
69
|
+
}
|
|
70
|
+
if (!isValidEthereumAddress(e.hook)) {
|
|
71
|
+
throw new FacilitatorValidationError("Invalid hook address format");
|
|
72
|
+
}
|
|
73
|
+
if (!e.hookData || typeof e.hookData !== "string") {
|
|
74
|
+
throw new FacilitatorValidationError("Missing or invalid hookData");
|
|
75
|
+
}
|
|
76
|
+
if (!isValidHex(e.hookData)) {
|
|
77
|
+
throw new FacilitatorValidationError("Hook data must be valid hex");
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
settlementRouter: e.settlementRouter,
|
|
81
|
+
salt: e.salt,
|
|
82
|
+
payTo: e.payTo,
|
|
83
|
+
facilitatorFee: e.facilitatorFee,
|
|
84
|
+
hook: e.hook,
|
|
85
|
+
hookData: e.hookData
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function validateNetwork(network) {
|
|
89
|
+
if (!network || typeof network !== "string") {
|
|
90
|
+
throw new FacilitatorValidationError("Invalid network: must be a non-empty string");
|
|
91
|
+
}
|
|
92
|
+
if (!/^(eip155:\d+|[a-z][a-z0-9-]*[a-z0-9])$/.test(network)) {
|
|
93
|
+
throw new FacilitatorValidationError(`Invalid network format: ${network}`);
|
|
94
|
+
}
|
|
95
|
+
return network;
|
|
96
|
+
}
|
|
97
|
+
function validateFacilitatorConfig(config) {
|
|
98
|
+
if (!config.signer && !config.privateKey) {
|
|
99
|
+
throw new FacilitatorValidationError(
|
|
100
|
+
"Missing signer or privateKey in facilitator configuration"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (config.signer) {
|
|
104
|
+
if (!isValidEthereumAddress(config.signer)) {
|
|
105
|
+
throw new FacilitatorValidationError(`Invalid signer address: ${config.signer}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (config.privateKey) {
|
|
109
|
+
const privateKey = config.privateKey;
|
|
110
|
+
const hasPrefix = privateKey.startsWith("0x") || privateKey.startsWith("0X");
|
|
111
|
+
const hexBody = hasPrefix ? privateKey.slice(2) : privateKey;
|
|
112
|
+
if (!/^[a-fA-F0-9]{64}$/.test(hexBody)) {
|
|
113
|
+
throw new FacilitatorValidationError(
|
|
114
|
+
"Invalid private key format: must be 32-byte hex string (64 hex chars, with optional 0x prefix)"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (config.allowedRouters) {
|
|
119
|
+
for (const [network, routers] of Object.entries(config.allowedRouters)) {
|
|
120
|
+
validateNetwork(network);
|
|
121
|
+
if (!Array.isArray(routers)) {
|
|
122
|
+
throw new FacilitatorValidationError(`Allowed routers for ${network} must be an array`);
|
|
123
|
+
}
|
|
124
|
+
for (const router of routers) {
|
|
125
|
+
if (!isValidEthereumAddress(router)) {
|
|
126
|
+
throw new FacilitatorValidationError(`Invalid router address for ${network}: ${router}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (config.rpcUrls) {
|
|
132
|
+
for (const [network, rpcUrl] of Object.entries(config.rpcUrls)) {
|
|
133
|
+
validateNetwork(network);
|
|
134
|
+
if (typeof rpcUrl !== "string" || !rpcUrl.startsWith("http")) {
|
|
135
|
+
throw new FacilitatorValidationError(`Invalid RPC URL for ${network}: ${rpcUrl}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function validateGasLimit(gasLimit) {
|
|
141
|
+
if (gasLimit <= 0n) {
|
|
142
|
+
throw new FacilitatorValidationError("Gas limit must be positive");
|
|
143
|
+
}
|
|
144
|
+
if (gasLimit > 10000000n) {
|
|
145
|
+
throw new FacilitatorValidationError("Gas limit too large (> 10M)");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function validateGasMultiplier(multiplier) {
|
|
149
|
+
if (multiplier <= 0) {
|
|
150
|
+
throw new FacilitatorValidationError("Gas multiplier must be positive");
|
|
151
|
+
}
|
|
152
|
+
if (multiplier > 5) {
|
|
153
|
+
throw new FacilitatorValidationError("Gas multiplier too large (> 5x)");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function validateFeeAmount(fee, minFee, maxFee) {
|
|
157
|
+
let feeBigInt;
|
|
158
|
+
try {
|
|
159
|
+
feeBigInt = BigInt(fee);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new FacilitatorValidationError(`Invalid fee amount: ${fee}. Must be a valid number.`);
|
|
162
|
+
}
|
|
163
|
+
if (feeBigInt < 0n) {
|
|
164
|
+
throw new FacilitatorValidationError("Fee cannot be negative");
|
|
165
|
+
}
|
|
166
|
+
if (minFee && feeBigInt < BigInt(minFee)) {
|
|
167
|
+
throw new FacilitatorValidationError(`Fee below minimum: ${fee} < ${minFee}`);
|
|
168
|
+
}
|
|
169
|
+
if (maxFee && feeBigInt > BigInt(maxFee)) {
|
|
170
|
+
throw new FacilitatorValidationError(`Fee above maximum: ${fee} > ${maxFee}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function networkConfigToChain(networkConfig, rpcUrl) {
|
|
174
|
+
return {
|
|
175
|
+
id: networkConfig.chainId,
|
|
176
|
+
name: networkConfig.name,
|
|
177
|
+
nativeCurrency: {
|
|
178
|
+
name: networkConfig.metadata?.nativeToken || "ETH",
|
|
179
|
+
symbol: networkConfig.metadata?.nativeToken || "ETH",
|
|
180
|
+
decimals: 18
|
|
181
|
+
},
|
|
182
|
+
rpcUrls: {
|
|
183
|
+
default: {
|
|
184
|
+
http: [rpcUrl]
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
blockExplorers: {
|
|
188
|
+
default: {
|
|
189
|
+
name: "Explorer",
|
|
190
|
+
url: (() => {
|
|
191
|
+
const addressSuffix = "/address/";
|
|
192
|
+
const baseUrl = networkConfig.addressExplorerBaseUrl;
|
|
193
|
+
return baseUrl.endsWith(addressSuffix) ? baseUrl.slice(0, -addressSuffix.length) : baseUrl;
|
|
194
|
+
})()
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
testnet: networkConfig.type === "testnet"
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function createPublicClientForNetwork(network, rpcUrls) {
|
|
201
|
+
const canonicalNetwork = toCanonicalNetworkKey(network);
|
|
202
|
+
const v1NetworkName = getNetworkName(canonicalNetwork);
|
|
203
|
+
const networkConfig = getNetworkConfig(v1NetworkName);
|
|
204
|
+
if (!networkConfig) {
|
|
205
|
+
throw new Error(`Network configuration not found for network: ${network}`);
|
|
206
|
+
}
|
|
207
|
+
const rpcUrl = rpcUrls?.[network] || rpcUrls?.[v1NetworkName] || rpcUrls?.[canonicalNetwork];
|
|
208
|
+
if (!rpcUrl) {
|
|
209
|
+
throw new Error(`No RPC URL available for network: ${network}. Please provide RPC URL in config.`);
|
|
210
|
+
}
|
|
211
|
+
const chain = networkConfigToChain(networkConfig, rpcUrl);
|
|
212
|
+
return createPublicClient({
|
|
213
|
+
chain,
|
|
214
|
+
transport: http(rpcUrl)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function createWalletClientForNetwork(network, signer, rpcUrls, transport, privateKey) {
|
|
218
|
+
const canonicalNetwork = toCanonicalNetworkKey(network);
|
|
219
|
+
const v1NetworkName = getNetworkName(canonicalNetwork);
|
|
220
|
+
const networkConfig = getNetworkConfig(v1NetworkName);
|
|
221
|
+
const rpcUrl = rpcUrls?.[network] || rpcUrls?.[v1NetworkName] || rpcUrls?.[canonicalNetwork];
|
|
222
|
+
if (!rpcUrl) {
|
|
223
|
+
throw new Error(`No RPC URL available for network: ${network}. Please provide RPC URL in config.`);
|
|
224
|
+
}
|
|
225
|
+
if (!signer && !privateKey) {
|
|
226
|
+
throw new Error("Either signer or privateKey must be provided to create wallet client");
|
|
227
|
+
}
|
|
228
|
+
let account;
|
|
229
|
+
if (privateKey) {
|
|
230
|
+
account = privateKeyToAccount(privateKey);
|
|
231
|
+
} else if (signer) {
|
|
232
|
+
account = signer;
|
|
233
|
+
} else {
|
|
234
|
+
throw new Error("Failed to create account: neither signer nor privateKey provided");
|
|
235
|
+
}
|
|
236
|
+
const chain = networkConfigToChain(networkConfig, rpcUrl);
|
|
237
|
+
return createWalletClient({
|
|
238
|
+
account,
|
|
239
|
+
chain,
|
|
240
|
+
transport: transport || http(rpcUrl)
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function calculateGasLimit(baseFee, facilitatorFee, gasMultiplier = 1.2) {
|
|
244
|
+
validateGasMultiplier(gasMultiplier);
|
|
245
|
+
const baseGas = 200000n;
|
|
246
|
+
const hookGas = facilitatorFee !== "0x0" ? 100000n : 0n;
|
|
247
|
+
const totalGas = (baseGas + hookGas) * BigInt(Math.ceil(gasMultiplier * 100)) / 100n;
|
|
248
|
+
validateGasLimit(totalGas);
|
|
249
|
+
return totalGas;
|
|
250
|
+
}
|
|
251
|
+
async function checkIfSettled(publicClient, router, contextKey) {
|
|
252
|
+
try {
|
|
253
|
+
const isSettled = await publicClient.readContract({
|
|
254
|
+
address: router,
|
|
255
|
+
abi: SETTLEMENT_ROUTER_ABI,
|
|
256
|
+
functionName: "isSettled",
|
|
257
|
+
args: [contextKey]
|
|
258
|
+
});
|
|
259
|
+
return isSettled;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Failed to check settlement status: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function executeSettlementWithRouter(walletClient, params, config = {}) {
|
|
267
|
+
const gasLimit = config.gasLimit || calculateGasLimit("0x0", params.facilitatorFee, config.gasMultiplier);
|
|
268
|
+
console.log("[executeSettlementWithRouter] Settlement params:", {
|
|
269
|
+
token: params.token,
|
|
270
|
+
from: params.from,
|
|
271
|
+
value: params.value,
|
|
272
|
+
validAfter: params.validAfter,
|
|
273
|
+
validBefore: params.validBefore,
|
|
274
|
+
nonce: params.nonce,
|
|
275
|
+
signature: params.signature ? `${params.signature.slice(0, 10)}...` : void 0,
|
|
276
|
+
salt: params.salt,
|
|
277
|
+
payTo: params.payTo,
|
|
278
|
+
facilitatorFee: params.facilitatorFee,
|
|
279
|
+
hook: params.hook,
|
|
280
|
+
hookData: params.hookData,
|
|
281
|
+
settlementRouter: params.settlementRouter
|
|
282
|
+
});
|
|
283
|
+
try {
|
|
284
|
+
const txHash = await walletClient.writeContract({
|
|
285
|
+
address: params.settlementRouter,
|
|
286
|
+
abi: SETTLEMENT_ROUTER_ABI,
|
|
287
|
+
functionName: "settleAndExecute",
|
|
288
|
+
args: [
|
|
289
|
+
params.token,
|
|
290
|
+
params.from,
|
|
291
|
+
BigInt(params.value),
|
|
292
|
+
BigInt(params.validAfter),
|
|
293
|
+
BigInt(params.validBefore),
|
|
294
|
+
params.nonce,
|
|
295
|
+
params.signature,
|
|
296
|
+
params.salt,
|
|
297
|
+
params.payTo,
|
|
298
|
+
BigInt(params.facilitatorFee),
|
|
299
|
+
params.hook,
|
|
300
|
+
params.hookData
|
|
301
|
+
],
|
|
302
|
+
gas: gasLimit,
|
|
303
|
+
chain: walletClient.chain,
|
|
304
|
+
account: walletClient.account ?? null
|
|
305
|
+
});
|
|
306
|
+
return txHash;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (error instanceof Error) {
|
|
309
|
+
let errorMessage = `SettlementRouter execution failed: ${error.message}`;
|
|
310
|
+
if ("cause" in error && error.cause) {
|
|
311
|
+
errorMessage += ` (cause: ${error.cause})`;
|
|
312
|
+
}
|
|
313
|
+
throw new Error(errorMessage);
|
|
314
|
+
}
|
|
315
|
+
throw new Error("Unknown error during SettlementRouter execution");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function waitForSettlementReceipt(publicClient, txHash, timeoutMs = 3e4) {
|
|
319
|
+
try {
|
|
320
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
321
|
+
hash: txHash,
|
|
322
|
+
timeout: timeoutMs
|
|
323
|
+
});
|
|
324
|
+
return {
|
|
325
|
+
success: receipt.status === "success",
|
|
326
|
+
blockNumber: receipt.blockNumber,
|
|
327
|
+
gasUsed: receipt.gasUsed,
|
|
328
|
+
effectiveGasPrice: receipt.effectiveGasPrice
|
|
329
|
+
};
|
|
330
|
+
} catch (error) {
|
|
331
|
+
throw new Error(
|
|
332
|
+
`Failed to get transaction receipt: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function parseEvmExactPayload(payload) {
|
|
337
|
+
const evmPayload = payload.payload;
|
|
338
|
+
if (!evmPayload || !evmPayload.signature || !evmPayload.authorization) {
|
|
339
|
+
throw new Error("Invalid EVM exact payload structure");
|
|
340
|
+
}
|
|
341
|
+
return evmPayload;
|
|
342
|
+
}
|
|
343
|
+
function parseSettlementRouterParams(paymentRequirements, paymentPayload) {
|
|
344
|
+
if (!isSettlementMode(paymentRequirements)) {
|
|
345
|
+
throw new Error("Payment requirements are not in SettlementRouter mode");
|
|
346
|
+
}
|
|
347
|
+
const evmPayload = parseEvmExactPayload(paymentPayload);
|
|
348
|
+
const extra = parseSettlementExtra(paymentRequirements.extra);
|
|
349
|
+
return {
|
|
350
|
+
token: paymentRequirements.asset,
|
|
351
|
+
from: evmPayload.authorization.from,
|
|
352
|
+
value: paymentRequirements.amount,
|
|
353
|
+
// V2 uses 'amount', not 'maxAmountRequired'
|
|
354
|
+
validAfter: evmPayload.authorization.validAfter || "0x0",
|
|
355
|
+
validBefore: evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF",
|
|
356
|
+
nonce: evmPayload.authorization.nonce,
|
|
357
|
+
signature: evmPayload.signature,
|
|
358
|
+
salt: extra.salt,
|
|
359
|
+
payTo: extra.payTo,
|
|
360
|
+
facilitatorFee: extra.facilitatorFee,
|
|
361
|
+
hook: extra.hook,
|
|
362
|
+
hookData: extra.hookData,
|
|
363
|
+
settlementRouter: extra.settlementRouter
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
async function executeSettlementWithWalletClient(walletClient, publicClient, paymentRequirements, paymentPayload, config = {}) {
|
|
367
|
+
try {
|
|
368
|
+
const canonicalNetwork = toCanonicalNetworkKey(paymentRequirements.network);
|
|
369
|
+
const v1NetworkName = getNetworkName(canonicalNetwork);
|
|
370
|
+
const networkConfig = getNetworkConfig(v1NetworkName);
|
|
371
|
+
const settlementRouter = paymentRequirements.extra?.settlementRouter;
|
|
372
|
+
if (!settlementRouter) {
|
|
373
|
+
throw new Error("Missing settlementRouter in payment requirements");
|
|
374
|
+
}
|
|
375
|
+
validateSettlementRouter(
|
|
376
|
+
paymentRequirements.network,
|
|
377
|
+
settlementRouter,
|
|
378
|
+
config.allowedRouters,
|
|
379
|
+
networkConfig
|
|
380
|
+
);
|
|
381
|
+
const params = parseSettlementRouterParams(paymentRequirements, paymentPayload);
|
|
382
|
+
const txHash = await executeSettlementWithRouter(walletClient, params, {
|
|
383
|
+
gasLimit: config.gasLimit,
|
|
384
|
+
gasMultiplier: config.gasMultiplier
|
|
385
|
+
});
|
|
386
|
+
const receipt = await waitForSettlementReceipt(publicClient, txHash, config.timeoutMs || 3e4);
|
|
387
|
+
return {
|
|
388
|
+
success: receipt.success,
|
|
389
|
+
transaction: txHash,
|
|
390
|
+
network: paymentRequirements.network,
|
|
391
|
+
payer: params.from,
|
|
392
|
+
// Use params.from for consistency
|
|
393
|
+
errorReason: receipt.success ? void 0 : "Transaction failed"
|
|
394
|
+
};
|
|
395
|
+
} catch (error) {
|
|
396
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
397
|
+
let payer;
|
|
398
|
+
try {
|
|
399
|
+
const params = parseSettlementRouterParams(paymentRequirements, paymentPayload);
|
|
400
|
+
payer = params.from;
|
|
401
|
+
} catch (parseError) {
|
|
402
|
+
console.error("[executeSettlementWithWalletClient] Failed to parse params:", parseError);
|
|
403
|
+
try {
|
|
404
|
+
const evmPayload = parseEvmExactPayload(paymentPayload);
|
|
405
|
+
payer = evmPayload.authorization.from;
|
|
406
|
+
} catch {
|
|
407
|
+
payer = void 0;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
console.error("[executeSettlementWithWalletClient] Settlement failed:", {
|
|
411
|
+
error: errorMessage,
|
|
412
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
413
|
+
network: paymentRequirements.network,
|
|
414
|
+
asset: paymentRequirements.asset,
|
|
415
|
+
payer
|
|
416
|
+
});
|
|
417
|
+
return {
|
|
418
|
+
success: false,
|
|
419
|
+
transaction: "",
|
|
420
|
+
network: paymentRequirements.network,
|
|
421
|
+
payer,
|
|
422
|
+
errorReason: errorMessage
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async function settleWithSettlementRouter(paymentRequirements, paymentPayload, config, options = {}) {
|
|
427
|
+
try {
|
|
428
|
+
const networkConfig = getNetworkConfig(paymentRequirements.network);
|
|
429
|
+
validateSettlementRouter(
|
|
430
|
+
paymentRequirements.network,
|
|
431
|
+
paymentRequirements.extra?.settlementRouter,
|
|
432
|
+
config.allowedRouters,
|
|
433
|
+
networkConfig
|
|
434
|
+
);
|
|
435
|
+
const params = parseSettlementRouterParams(paymentRequirements, paymentPayload);
|
|
436
|
+
const publicClient = createPublicClientForNetwork(paymentRequirements.network, config.rpcUrls);
|
|
437
|
+
const walletClient = createWalletClientForNetwork(
|
|
438
|
+
paymentRequirements.network,
|
|
439
|
+
config.signer,
|
|
440
|
+
config.rpcUrls,
|
|
441
|
+
void 0,
|
|
442
|
+
config.privateKey
|
|
443
|
+
);
|
|
444
|
+
const txHash = await executeSettlementWithRouter(walletClient, params, {
|
|
445
|
+
gasLimit: options.gasLimit,
|
|
446
|
+
gasMultiplier: options.gasMultiplier
|
|
447
|
+
});
|
|
448
|
+
const receipt = await waitForSettlementReceipt(
|
|
449
|
+
publicClient,
|
|
450
|
+
txHash,
|
|
451
|
+
options.timeoutMs || 3e4
|
|
452
|
+
);
|
|
453
|
+
return {
|
|
454
|
+
success: receipt.success,
|
|
455
|
+
transaction: txHash,
|
|
456
|
+
network: paymentRequirements.network,
|
|
457
|
+
payer: params.from,
|
|
458
|
+
errorReason: receipt.success ? void 0 : "Transaction failed"
|
|
459
|
+
};
|
|
460
|
+
} catch (error) {
|
|
461
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
462
|
+
let payer;
|
|
463
|
+
try {
|
|
464
|
+
const evmPayload = parseEvmExactPayload(paymentPayload);
|
|
465
|
+
payer = evmPayload.authorization.from;
|
|
466
|
+
} catch {
|
|
467
|
+
payer = void 0;
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
transaction: "",
|
|
472
|
+
network: paymentRequirements.network,
|
|
473
|
+
payer,
|
|
474
|
+
errorReason: errorMessage
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
var authorizationTypes = {
|
|
479
|
+
TransferWithAuthorization: [
|
|
480
|
+
{ name: "from", type: "address" },
|
|
481
|
+
{ name: "to", type: "address" },
|
|
482
|
+
{ name: "value", type: "uint256" },
|
|
483
|
+
{ name: "validAfter", type: "uint256" },
|
|
484
|
+
{ name: "validBefore", type: "uint256" },
|
|
485
|
+
{ name: "nonce", type: "bytes32" }
|
|
486
|
+
]
|
|
487
|
+
};
|
|
488
|
+
function parseEvmExactPayload2(payload) {
|
|
489
|
+
const evmPayload = payload.payload;
|
|
490
|
+
if (!evmPayload.signature) {
|
|
491
|
+
throw new FacilitatorValidationError("Missing signature in EVM exact payload");
|
|
492
|
+
}
|
|
493
|
+
if (!evmPayload.authorization) {
|
|
494
|
+
throw new FacilitatorValidationError("Missing authorization in EVM exact payload");
|
|
495
|
+
}
|
|
496
|
+
const auth = evmPayload.authorization;
|
|
497
|
+
if (!auth.from || !auth.to || !auth.value || !auth.nonce) {
|
|
498
|
+
throw new FacilitatorValidationError("Invalid authorization structure in EVM exact payload");
|
|
499
|
+
}
|
|
500
|
+
return evmPayload;
|
|
501
|
+
}
|
|
502
|
+
var eip3009ABI = [
|
|
503
|
+
{
|
|
504
|
+
inputs: [
|
|
505
|
+
{ name: "from", type: "address" },
|
|
506
|
+
{ name: "to", type: "address" },
|
|
507
|
+
{ name: "value", type: "uint256" },
|
|
508
|
+
{ name: "validAfter", type: "uint256" },
|
|
509
|
+
{ name: "validBefore", type: "uint256" },
|
|
510
|
+
{ name: "nonce", type: "bytes32" },
|
|
511
|
+
{ name: "signature", type: "bytes" }
|
|
512
|
+
],
|
|
513
|
+
name: "transferWithAuthorization",
|
|
514
|
+
outputs: [],
|
|
515
|
+
stateMutability: "nonpayable",
|
|
516
|
+
type: "function"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
inputs: [{ name: "account", type: "address" }],
|
|
520
|
+
name: "balanceOf",
|
|
521
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
522
|
+
stateMutability: "view",
|
|
523
|
+
type: "function"
|
|
524
|
+
}
|
|
525
|
+
];
|
|
526
|
+
var RouterSettlementFacilitator = class {
|
|
527
|
+
constructor(config) {
|
|
528
|
+
this.scheme = "exact";
|
|
529
|
+
this.caipFamily = "eip155:*";
|
|
530
|
+
validateFacilitatorConfig(config);
|
|
531
|
+
this.config = {
|
|
532
|
+
// Default values
|
|
533
|
+
gasConfig: {
|
|
534
|
+
maxGasLimit: 5000000n,
|
|
535
|
+
gasMultiplier: 1.2
|
|
536
|
+
},
|
|
537
|
+
feeConfig: {
|
|
538
|
+
minFee: "0x0",
|
|
539
|
+
maxFee: "0xFFFFFFFFFFFFFFFF"
|
|
540
|
+
},
|
|
541
|
+
timeouts: {
|
|
542
|
+
verify: 5e3,
|
|
543
|
+
// 5 seconds
|
|
544
|
+
settle: 3e4
|
|
545
|
+
// 30 seconds
|
|
546
|
+
},
|
|
547
|
+
// Override with user config
|
|
548
|
+
...config
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Get scheme-specific extra data for responses
|
|
553
|
+
*/
|
|
554
|
+
getExtra(network) {
|
|
555
|
+
try {
|
|
556
|
+
if (!network || typeof network !== "string" || network.trim() === "") {
|
|
557
|
+
return void 0;
|
|
558
|
+
}
|
|
559
|
+
const networkConfig = getNetworkConfig(network);
|
|
560
|
+
if (!networkConfig) {
|
|
561
|
+
return void 0;
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
scheme: this.scheme,
|
|
565
|
+
caipFamily: this.caipFamily,
|
|
566
|
+
settlementRouter: networkConfig?.settlementRouter,
|
|
567
|
+
defaultAsset: networkConfig?.defaultAsset,
|
|
568
|
+
supportedNetworks: [network]
|
|
569
|
+
// Can be expanded for multi-network support
|
|
570
|
+
};
|
|
571
|
+
} catch (error) {
|
|
572
|
+
return void 0;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Get signer addresses for the network
|
|
577
|
+
* Derives from privateKey if signer address not explicitly provided
|
|
578
|
+
*/
|
|
579
|
+
getSigners(network) {
|
|
580
|
+
validateNetwork(network);
|
|
581
|
+
if (this.config.signer) {
|
|
582
|
+
return [this.config.signer];
|
|
583
|
+
}
|
|
584
|
+
if (this.config.privateKey) {
|
|
585
|
+
const account = privateKeyToAccount(this.config.privateKey);
|
|
586
|
+
return [account.address];
|
|
587
|
+
}
|
|
588
|
+
throw new Error("Either signer or privateKey must be provided in FacilitatorConfig");
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Verify payment payload without executing settlement
|
|
592
|
+
*/
|
|
593
|
+
async verify(payload, requirements) {
|
|
594
|
+
try {
|
|
595
|
+
this.validateBasicPayload(payload, requirements);
|
|
596
|
+
const isRouterSettlement = isSettlementMode(requirements);
|
|
597
|
+
if (isRouterSettlement) {
|
|
598
|
+
return await this.verifySettlementRouter(payload, requirements);
|
|
599
|
+
} else {
|
|
600
|
+
return await this.verifyStandard(payload, requirements);
|
|
601
|
+
}
|
|
602
|
+
} catch (error) {
|
|
603
|
+
let payer;
|
|
604
|
+
try {
|
|
605
|
+
const evmPayload = parseEvmExactPayload2(payload);
|
|
606
|
+
payer = evmPayload.authorization.from;
|
|
607
|
+
} catch {
|
|
608
|
+
payer = void 0;
|
|
609
|
+
}
|
|
610
|
+
if (error instanceof FacilitatorValidationError || error instanceof SettlementRouterError) {
|
|
611
|
+
return {
|
|
612
|
+
isValid: false,
|
|
613
|
+
invalidReason: error.message,
|
|
614
|
+
payer: payer || ""
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
isValid: false,
|
|
619
|
+
invalidReason: `Verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
620
|
+
payer: payer || ""
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Settle payment by executing blockchain transaction
|
|
626
|
+
*/
|
|
627
|
+
async settle(payload, requirements) {
|
|
628
|
+
try {
|
|
629
|
+
const verification = await this.verify(payload, requirements);
|
|
630
|
+
if (!verification.isValid) {
|
|
631
|
+
return {
|
|
632
|
+
success: false,
|
|
633
|
+
transaction: "",
|
|
634
|
+
network: requirements.network,
|
|
635
|
+
payer: verification.payer,
|
|
636
|
+
errorReason: verification.invalidReason || "Payment verification failed"
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
const isRouterSettlement = isSettlementMode(requirements);
|
|
640
|
+
if (isRouterSettlement) {
|
|
641
|
+
return await this.settleWithRouter(payload, requirements);
|
|
642
|
+
} else {
|
|
643
|
+
return await this.settleStandard(payload, requirements);
|
|
644
|
+
}
|
|
645
|
+
} catch (error) {
|
|
646
|
+
let payer;
|
|
647
|
+
try {
|
|
648
|
+
const evmPayload = parseEvmExactPayload2(payload);
|
|
649
|
+
payer = evmPayload.authorization.from;
|
|
650
|
+
} catch {
|
|
651
|
+
payer = void 0;
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
success: false,
|
|
655
|
+
transaction: "",
|
|
656
|
+
network: requirements.network,
|
|
657
|
+
payer: payer || "",
|
|
658
|
+
errorReason: error instanceof Error ? error.message : "Unknown settlement error"
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Validate basic payload and requirements
|
|
664
|
+
*/
|
|
665
|
+
validateBasicPayload(payload, requirements) {
|
|
666
|
+
validateNetwork(requirements.network);
|
|
667
|
+
if (requirements.scheme !== this.scheme) {
|
|
668
|
+
throw new FacilitatorValidationError(
|
|
669
|
+
`Scheme mismatch: expected ${this.scheme}, got ${requirements.scheme}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
if (!requirements.network.startsWith("eip155:")) {
|
|
673
|
+
throw new FacilitatorValidationError(
|
|
674
|
+
`Unsupported network family: ${requirements.network}. Only EVM networks (eip155:*) are supported`
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
parseEvmExactPayload2(payload);
|
|
678
|
+
if (!requirements.asset) {
|
|
679
|
+
throw new FacilitatorValidationError("Missing asset in payment requirements");
|
|
680
|
+
}
|
|
681
|
+
if (!requirements.payTo) {
|
|
682
|
+
throw new FacilitatorValidationError("Missing payTo address in payment requirements");
|
|
683
|
+
}
|
|
684
|
+
if (!requirements.amount) {
|
|
685
|
+
throw new FacilitatorValidationError("Missing amount in payment requirements");
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Verify payment for SettlementRouter mode
|
|
690
|
+
*/
|
|
691
|
+
async verifySettlementRouter(payload, requirements) {
|
|
692
|
+
const evmPayload = parseEvmExactPayload2(payload);
|
|
693
|
+
const payer = evmPayload.authorization.from;
|
|
694
|
+
const settlementExtra = validateSettlementExtra(requirements.extra);
|
|
695
|
+
const networkConfig = getNetworkConfig(requirements.network);
|
|
696
|
+
validateSettlementRouter(
|
|
697
|
+
requirements.network,
|
|
698
|
+
settlementExtra.settlementRouter,
|
|
699
|
+
this.config.allowedRouters,
|
|
700
|
+
networkConfig
|
|
701
|
+
);
|
|
702
|
+
validateFeeAmount(
|
|
703
|
+
settlementExtra.facilitatorFee,
|
|
704
|
+
this.config.feeConfig?.minFee,
|
|
705
|
+
this.config.feeConfig?.maxFee
|
|
706
|
+
);
|
|
707
|
+
const publicClient = createPublicClientForNetwork(requirements.network, this.config.rpcUrls);
|
|
708
|
+
const extraWithDomain = requirements.extra;
|
|
709
|
+
const eip712Name = extraWithDomain?.name || "USD Coin";
|
|
710
|
+
const eip712Version = extraWithDomain?.version || "2";
|
|
711
|
+
try {
|
|
712
|
+
const parsedSignature = parseErc6492Signature(evmPayload.signature);
|
|
713
|
+
const typedData = {
|
|
714
|
+
types: authorizationTypes,
|
|
715
|
+
primaryType: "TransferWithAuthorization",
|
|
716
|
+
domain: {
|
|
717
|
+
name: eip712Name,
|
|
718
|
+
version: eip712Version,
|
|
719
|
+
chainId: parseInt(requirements.network.split(":")[1]),
|
|
720
|
+
verifyingContract: requirements.asset
|
|
721
|
+
},
|
|
722
|
+
message: {
|
|
723
|
+
from: payer,
|
|
724
|
+
to: evmPayload.authorization.to,
|
|
725
|
+
value: BigInt(evmPayload.authorization.value),
|
|
726
|
+
validAfter: BigInt(evmPayload.authorization.validAfter || "0x0"),
|
|
727
|
+
validBefore: BigInt(evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF"),
|
|
728
|
+
nonce: evmPayload.authorization.nonce
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
const isValidSignature = await verifyTypedData({
|
|
732
|
+
address: payer,
|
|
733
|
+
...typedData,
|
|
734
|
+
signature: parsedSignature.signature
|
|
735
|
+
});
|
|
736
|
+
if (!isValidSignature) {
|
|
737
|
+
return {
|
|
738
|
+
isValid: false,
|
|
739
|
+
invalidReason: "Invalid signature",
|
|
740
|
+
payer
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
} catch (error) {
|
|
744
|
+
return {
|
|
745
|
+
isValid: false,
|
|
746
|
+
invalidReason: `Signature verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
747
|
+
payer
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
const chainId = parseInt(requirements.network.split(":")[1]);
|
|
752
|
+
const calculatedCommitment = calculateCommitment({
|
|
753
|
+
chainId,
|
|
754
|
+
hub: settlementExtra.settlementRouter,
|
|
755
|
+
asset: requirements.asset,
|
|
756
|
+
from: payer,
|
|
757
|
+
value: evmPayload.authorization.value,
|
|
758
|
+
validAfter: evmPayload.authorization.validAfter || "0x0",
|
|
759
|
+
validBefore: evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF",
|
|
760
|
+
salt: settlementExtra.salt,
|
|
761
|
+
payTo: settlementExtra.payTo,
|
|
762
|
+
facilitatorFee: settlementExtra.facilitatorFee,
|
|
763
|
+
hook: settlementExtra.hook,
|
|
764
|
+
hookData: settlementExtra.hookData
|
|
765
|
+
});
|
|
766
|
+
if (evmPayload.authorization.nonce !== calculatedCommitment) {
|
|
767
|
+
return {
|
|
768
|
+
isValid: false,
|
|
769
|
+
invalidReason: "Commitment mismatch: nonce does not match calculated commitment",
|
|
770
|
+
payer
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
} catch (error) {
|
|
774
|
+
return {
|
|
775
|
+
isValid: false,
|
|
776
|
+
invalidReason: `Commitment verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
777
|
+
payer
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
const balance = await publicClient.readContract({
|
|
782
|
+
address: requirements.asset,
|
|
783
|
+
abi: [
|
|
784
|
+
{
|
|
785
|
+
type: "function",
|
|
786
|
+
name: "balanceOf",
|
|
787
|
+
inputs: [{ name: "account", type: "address" }],
|
|
788
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
789
|
+
stateMutability: "view"
|
|
790
|
+
}
|
|
791
|
+
],
|
|
792
|
+
functionName: "balanceOf",
|
|
793
|
+
args: [payer]
|
|
794
|
+
});
|
|
795
|
+
const totalRequired = BigInt(requirements.amount) + BigInt(settlementExtra.facilitatorFee);
|
|
796
|
+
if (balance < totalRequired) {
|
|
797
|
+
return {
|
|
798
|
+
isValid: false,
|
|
799
|
+
invalidReason: `Insufficient balance: have ${balance}, need ${totalRequired}`,
|
|
800
|
+
payer
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
} catch (error) {
|
|
804
|
+
return {
|
|
805
|
+
isValid: false,
|
|
806
|
+
invalidReason: `Balance check failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
807
|
+
payer
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
isValid: true,
|
|
812
|
+
payer
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Verify payment for standard mode (fallback)
|
|
817
|
+
*/
|
|
818
|
+
async verifyStandard(payload, requirements) {
|
|
819
|
+
const evmPayload = parseEvmExactPayload2(payload);
|
|
820
|
+
const payer = evmPayload.authorization.from;
|
|
821
|
+
const publicClient = createPublicClientForNetwork(requirements.network, this.config.rpcUrls);
|
|
822
|
+
try {
|
|
823
|
+
const parsedSignature = parseErc6492Signature(evmPayload.signature);
|
|
824
|
+
const extraAny = requirements.extra;
|
|
825
|
+
const domainName = extraAny?.name || "USD Coin";
|
|
826
|
+
const domainVersion = extraAny?.version || "3";
|
|
827
|
+
const typedData = {
|
|
828
|
+
types: authorizationTypes,
|
|
829
|
+
primaryType: "TransferWithAuthorization",
|
|
830
|
+
domain: {
|
|
831
|
+
name: domainName,
|
|
832
|
+
version: domainVersion,
|
|
833
|
+
chainId: parseInt(requirements.network.split(":")[1]),
|
|
834
|
+
verifyingContract: requirements.asset
|
|
835
|
+
},
|
|
836
|
+
message: {
|
|
837
|
+
from: payer,
|
|
838
|
+
to: requirements.payTo,
|
|
839
|
+
value: BigInt(requirements.amount),
|
|
840
|
+
validAfter: BigInt(evmPayload.authorization.validAfter || "0x0"),
|
|
841
|
+
validBefore: BigInt(evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF"),
|
|
842
|
+
nonce: evmPayload.authorization.nonce
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
const isValidSignature = await verifyTypedData({
|
|
846
|
+
address: payer,
|
|
847
|
+
...typedData,
|
|
848
|
+
signature: parsedSignature.signature
|
|
849
|
+
});
|
|
850
|
+
if (!isValidSignature) {
|
|
851
|
+
return {
|
|
852
|
+
isValid: false,
|
|
853
|
+
invalidReason: "Invalid signature",
|
|
854
|
+
payer
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
const balance = await publicClient.readContract({
|
|
858
|
+
address: requirements.asset,
|
|
859
|
+
abi: eip3009ABI,
|
|
860
|
+
functionName: "balanceOf",
|
|
861
|
+
args: [payer]
|
|
862
|
+
});
|
|
863
|
+
if (BigInt(balance) < BigInt(requirements.amount)) {
|
|
864
|
+
return {
|
|
865
|
+
isValid: false,
|
|
866
|
+
invalidReason: "Insufficient balance",
|
|
867
|
+
payer
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
isValid: true,
|
|
872
|
+
payer
|
|
873
|
+
};
|
|
874
|
+
} catch (error) {
|
|
875
|
+
return {
|
|
876
|
+
isValid: false,
|
|
877
|
+
invalidReason: `Standard verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
878
|
+
payer
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Settle payment using SettlementRouter
|
|
884
|
+
*/
|
|
885
|
+
async settleWithRouter(payload, requirements) {
|
|
886
|
+
return await settleWithSettlementRouter(requirements, payload, this.config, {
|
|
887
|
+
gasMultiplier: this.config.gasConfig?.gasMultiplier,
|
|
888
|
+
timeoutMs: this.config.timeouts?.settle
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Settle payment using standard method (fallback)
|
|
893
|
+
*/
|
|
894
|
+
async settleStandard(payload, requirements) {
|
|
895
|
+
const evmPayload = parseEvmExactPayload2(payload);
|
|
896
|
+
const payer = evmPayload.authorization.from;
|
|
897
|
+
const walletClient = createWalletClientForNetwork(
|
|
898
|
+
requirements.network,
|
|
899
|
+
this.config.signer,
|
|
900
|
+
this.config.rpcUrls,
|
|
901
|
+
void 0,
|
|
902
|
+
this.config.privateKey
|
|
903
|
+
);
|
|
904
|
+
const publicClient = createPublicClientForNetwork(requirements.network, this.config.rpcUrls);
|
|
905
|
+
try {
|
|
906
|
+
const parsedSignature = parseErc6492Signature(evmPayload.signature);
|
|
907
|
+
const txHash = await walletClient.writeContract({
|
|
908
|
+
address: requirements.asset,
|
|
909
|
+
abi: eip3009ABI,
|
|
910
|
+
functionName: "transferWithAuthorization",
|
|
911
|
+
args: [
|
|
912
|
+
payer,
|
|
913
|
+
requirements.payTo,
|
|
914
|
+
BigInt(requirements.amount),
|
|
915
|
+
BigInt(evmPayload.authorization.validAfter || "0x0"),
|
|
916
|
+
BigInt(evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF"),
|
|
917
|
+
evmPayload.authorization.nonce,
|
|
918
|
+
parsedSignature.signature
|
|
919
|
+
],
|
|
920
|
+
chain: walletClient.chain,
|
|
921
|
+
account: walletClient.account ?? null
|
|
922
|
+
});
|
|
923
|
+
const receipt = await waitForSettlementReceipt(publicClient, txHash);
|
|
924
|
+
return {
|
|
925
|
+
success: receipt.success,
|
|
926
|
+
transaction: txHash,
|
|
927
|
+
network: requirements.network,
|
|
928
|
+
payer,
|
|
929
|
+
errorReason: receipt.success ? void 0 : "Transaction failed"
|
|
930
|
+
};
|
|
931
|
+
} catch (error) {
|
|
932
|
+
return {
|
|
933
|
+
success: false,
|
|
934
|
+
transaction: "",
|
|
935
|
+
network: requirements.network,
|
|
936
|
+
payer,
|
|
937
|
+
errorReason: error instanceof Error ? error.message : "Unknown error"
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
function createRouterSettlementFacilitator(config) {
|
|
943
|
+
return new RouterSettlementFacilitator(config);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
export { RouterSettlementFacilitator, calculateGasLimit, checkIfSettled, createPublicClientForNetwork, createRouterSettlementFacilitator, createWalletClientForNetwork, executeSettlementWithRouter, executeSettlementWithWalletClient, isValid256BitHex, isValid32ByteHex, isValidEthereumAddress, isValidHex, parseSettlementRouterParams, settleWithSettlementRouter, validateFacilitatorConfig, validateFeeAmount, validateGasLimit, validateGasMultiplier, validateNetwork, validateSettlementExtra, validateSettlementRouter, waitForSettlementReceipt };
|
|
947
|
+
//# sourceMappingURL=index.mjs.map
|
|
948
|
+
//# sourceMappingURL=index.mjs.map
|