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