@payai/x402-evm 2.4.1 → 2.4.2
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/dist/cjs/batch-settlement/client/file-storage.d.ts +47 -0
- package/dist/cjs/batch-settlement/client/file-storage.js +116 -0
- package/dist/cjs/batch-settlement/client/file-storage.js.map +1 -0
- package/dist/cjs/batch-settlement/client/index.d.ts +111 -0
- package/dist/cjs/batch-settlement/client/index.js +1565 -0
- package/dist/cjs/batch-settlement/client/index.js.map +1 -0
- package/dist/cjs/batch-settlement/facilitator/index.d.ts +71 -0
- package/dist/cjs/batch-settlement/facilitator/index.js +2032 -0
- package/dist/cjs/batch-settlement/facilitator/index.js.map +1 -0
- package/dist/cjs/batch-settlement/server/file-storage.d.ts +53 -0
- package/dist/cjs/batch-settlement/server/file-storage.js +181 -0
- package/dist/cjs/batch-settlement/server/file-storage.js.map +1 -0
- package/dist/cjs/batch-settlement/server/index.d.ts +491 -0
- package/dist/cjs/batch-settlement/server/index.js +1960 -0
- package/dist/cjs/batch-settlement/server/index.js.map +1 -0
- package/dist/cjs/batch-settlement/server/redis-storage.d.ts +87 -0
- package/dist/cjs/batch-settlement/server/redis-storage.js +181 -0
- package/dist/cjs/batch-settlement/server/redis-storage.js.map +1 -0
- package/dist/cjs/exact/client/index.d.ts +6 -4
- package/dist/cjs/exact/client/index.js +7 -5
- package/dist/cjs/exact/client/index.js.map +1 -1
- package/dist/cjs/exact/facilitator/index.d.ts +16 -9
- package/dist/cjs/exact/facilitator/index.js +35 -7
- package/dist/cjs/exact/facilitator/index.js.map +1 -1
- package/dist/cjs/exact/server/index.js +40 -1
- package/dist/cjs/exact/server/index.js.map +1 -1
- package/dist/cjs/exact/v1/client/index.d.ts +2 -1
- package/dist/cjs/exact/v1/client/index.js.map +1 -1
- package/dist/cjs/exact/v1/facilitator/index.d.ts +11 -5
- package/dist/cjs/exact/v1/facilitator/index.js +16 -2
- package/dist/cjs/exact/v1/facilitator/index.js.map +1 -1
- package/dist/cjs/index.d.ts +113 -7
- package/dist/cjs/index.js +1353 -5
- package/dist/cjs/index.js.map +1 -1
- package/dist/{esm/permit2-CyZxwngN.d.mts → cjs/permit2-DhJRUcgY.d.ts} +1 -13
- package/dist/cjs/rpc-DULZzRne.d.ts +13 -0
- package/dist/cjs/scheme-CvkPJXBD.d.ts +307 -0
- package/dist/{esm/scheme-DCR7hsa3.d.mts → cjs/scheme-DTQFE9xp.d.ts} +2 -2
- package/dist/{esm/signer-D912R4mq.d.mts → cjs/signer-tYS6Y46X.d.ts} +3 -0
- package/dist/cjs/storage-6W5MO46W.d.ts +50 -0
- package/dist/cjs/storage-Bl6aD0Xg.d.ts +81 -0
- package/dist/cjs/types-CF8P2-NM.d.ts +180 -0
- package/dist/cjs/upto/client/index.d.ts +5 -3
- package/dist/cjs/upto/client/index.js +7 -5
- package/dist/cjs/upto/client/index.js.map +1 -1
- package/dist/cjs/upto/facilitator/index.d.ts +2 -1
- package/dist/cjs/upto/facilitator/index.js +2 -1
- package/dist/cjs/upto/facilitator/index.js.map +1 -1
- package/dist/cjs/upto/server/index.js +40 -1
- package/dist/cjs/upto/server/index.js.map +1 -1
- package/dist/cjs/v1/index.d.ts +2 -1
- package/dist/cjs/v1/index.js.map +1 -1
- package/dist/esm/batch-settlement/client/file-storage.d.mts +47 -0
- package/dist/esm/batch-settlement/client/file-storage.mjs +63 -0
- package/dist/esm/batch-settlement/client/file-storage.mjs.map +1 -0
- package/dist/esm/batch-settlement/client/index.d.mts +111 -0
- package/dist/esm/batch-settlement/client/index.mjs +59 -0
- package/dist/esm/batch-settlement/client/index.mjs.map +1 -0
- package/dist/esm/batch-settlement/facilitator/index.d.mts +71 -0
- package/dist/esm/batch-settlement/facilitator/index.mjs +1235 -0
- package/dist/esm/batch-settlement/facilitator/index.mjs.map +1 -0
- package/dist/esm/batch-settlement/server/file-storage.d.mts +53 -0
- package/dist/esm/batch-settlement/server/file-storage.mjs +128 -0
- package/dist/esm/batch-settlement/server/file-storage.mjs.map +1 -0
- package/dist/esm/batch-settlement/server/index.d.mts +491 -0
- package/dist/esm/batch-settlement/server/index.mjs +1645 -0
- package/dist/esm/batch-settlement/server/index.mjs.map +1 -0
- package/dist/esm/batch-settlement/server/redis-storage.d.mts +87 -0
- package/dist/esm/batch-settlement/server/redis-storage.mjs +156 -0
- package/dist/esm/batch-settlement/server/redis-storage.mjs.map +1 -0
- package/dist/esm/chunk-2EUQTNJO.mjs +38 -0
- package/dist/esm/chunk-2EUQTNJO.mjs.map +1 -0
- package/dist/esm/chunk-53USC5VE.mjs +47 -0
- package/dist/esm/chunk-53USC5VE.mjs.map +1 -0
- package/dist/esm/{chunk-GJ57SZGI.mjs → chunk-6WQOGWBE.mjs} +7 -5
- package/dist/esm/{chunk-GJ57SZGI.mjs.map → chunk-6WQOGWBE.mjs.map} +1 -1
- package/dist/esm/{chunk-F3OOHBAW.mjs → chunk-BTYNCDNS.mjs} +42 -2
- package/dist/esm/{chunk-F3OOHBAW.mjs.map → chunk-BTYNCDNS.mjs.map} +1 -1
- package/dist/esm/{chunk-ERK2ZPOY.mjs → chunk-CSQS7ZON.mjs} +27 -7
- package/dist/esm/chunk-CSQS7ZON.mjs.map +1 -0
- package/dist/esm/chunk-GD4MKCN7.mjs +57 -0
- package/dist/esm/chunk-GD4MKCN7.mjs.map +1 -0
- package/dist/esm/chunk-HYABYUBD.mjs +432 -0
- package/dist/esm/chunk-HYABYUBD.mjs.map +1 -0
- package/dist/esm/chunk-IN5YIT5C.mjs +159 -0
- package/dist/esm/chunk-IN5YIT5C.mjs.map +1 -0
- package/dist/esm/{chunk-JII456TS.mjs → chunk-JK7SLLF7.mjs} +1 -1
- package/dist/esm/chunk-JK7SLLF7.mjs.map +1 -0
- package/dist/esm/{chunk-C4ZQMS77.mjs → chunk-MACPBXCT.mjs} +2 -216
- package/dist/esm/chunk-MACPBXCT.mjs.map +1 -0
- package/dist/esm/chunk-NKYVYGRA.mjs +911 -0
- package/dist/esm/chunk-NKYVYGRA.mjs.map +1 -0
- package/dist/esm/{chunk-FQJR4RCF.mjs → chunk-R7I3RZFF.mjs} +10 -6
- package/dist/esm/{chunk-FQJR4RCF.mjs.map → chunk-R7I3RZFF.mjs.map} +1 -1
- package/dist/esm/{chunk-CRT6YNY5.mjs → chunk-RWLVVO3B.mjs} +21 -61
- package/dist/esm/chunk-RWLVVO3B.mjs.map +1 -0
- package/dist/esm/chunk-TGFAVNUD.mjs +111 -0
- package/dist/esm/chunk-TGFAVNUD.mjs.map +1 -0
- package/dist/esm/chunk-TW7Z65AO.mjs +34 -0
- package/dist/esm/chunk-TW7Z65AO.mjs.map +1 -0
- package/dist/esm/chunk-U4HCGTLU.mjs +35 -0
- package/dist/esm/chunk-U4HCGTLU.mjs.map +1 -0
- package/dist/esm/chunk-VS3RYAYE.mjs +80 -0
- package/dist/esm/chunk-VS3RYAYE.mjs.map +1 -0
- package/dist/esm/chunk-W6ON4LG2.mjs +39 -0
- package/dist/esm/chunk-W6ON4LG2.mjs.map +1 -0
- package/dist/esm/{chunk-WKBC5YMI.mjs → chunk-YMQCTKDU.mjs} +23 -55
- package/dist/esm/chunk-YMQCTKDU.mjs.map +1 -0
- package/dist/esm/exact/client/index.d.mts +6 -4
- package/dist/esm/exact/client/index.mjs +10 -5
- package/dist/esm/exact/facilitator/index.d.mts +16 -9
- package/dist/esm/exact/facilitator/index.mjs +36 -14
- package/dist/esm/exact/facilitator/index.mjs.map +1 -1
- package/dist/esm/exact/server/index.mjs +1 -1
- package/dist/esm/exact/v1/client/index.d.mts +2 -1
- package/dist/esm/exact/v1/client/index.mjs +5 -2
- package/dist/esm/exact/v1/facilitator/index.d.mts +11 -5
- package/dist/esm/exact/v1/facilitator/index.mjs +5 -2
- package/dist/esm/index.d.mts +113 -7
- package/dist/esm/index.mjs +53 -7
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/permit2-DhJRUcgY.d.mts +729 -0
- package/dist/esm/rpc-DULZzRne.d.mts +13 -0
- package/dist/esm/scheme-DtbSS4Fk.d.mts +307 -0
- package/dist/esm/scheme-gtqAIYPJ.d.mts +47 -0
- package/dist/esm/signer-tYS6Y46X.d.mts +170 -0
- package/dist/esm/storage-6W5MO46W.d.mts +50 -0
- package/dist/esm/storage-sZ1CDS4P.d.mts +81 -0
- package/dist/esm/types-CF8P2-NM.d.mts +180 -0
- package/dist/esm/upto/client/index.d.mts +5 -3
- package/dist/esm/upto/client/index.mjs +9 -4
- package/dist/esm/upto/facilitator/index.d.mts +2 -1
- package/dist/esm/upto/facilitator/index.mjs +17 -9
- package/dist/esm/upto/facilitator/index.mjs.map +1 -1
- package/dist/esm/upto/server/index.mjs +1 -1
- package/dist/esm/v1/index.d.mts +2 -1
- package/dist/esm/v1/index.mjs +5 -2
- package/package.json +5 -5
- package/dist/esm/chunk-C4ZQMS77.mjs.map +0 -1
- package/dist/esm/chunk-CRT6YNY5.mjs.map +0 -1
- package/dist/esm/chunk-ERK2ZPOY.mjs.map +0 -1
- package/dist/esm/chunk-JII456TS.mjs.map +0 -1
- package/dist/esm/chunk-WKBC5YMI.mjs.map +0 -1
|
@@ -0,0 +1,2032 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/batch-settlement/facilitator/index.ts
|
|
21
|
+
var facilitator_exports = {};
|
|
22
|
+
__export(facilitator_exports, {
|
|
23
|
+
BatchSettlementEvmScheme: () => BatchSettlementEvmScheme
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(facilitator_exports);
|
|
26
|
+
|
|
27
|
+
// src/batch-settlement/constants.ts
|
|
28
|
+
var import_viem = require("viem");
|
|
29
|
+
var BATCH_SETTLEMENT_SCHEME = "batch-settlement";
|
|
30
|
+
var BATCH_SETTLEMENT_ADDRESS = "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003";
|
|
31
|
+
var ERC3009_DEPOSIT_COLLECTOR_ADDRESS = "0x4020806089470a89826cB9fB1f4059150b550004";
|
|
32
|
+
var PERMIT2_DEPOSIT_COLLECTOR_ADDRESS = "0x4020425FAf3B746C082C2f942b4E5159887B0005";
|
|
33
|
+
var MIN_WITHDRAW_DELAY = 900;
|
|
34
|
+
var MAX_WITHDRAW_DELAY = 2592e3;
|
|
35
|
+
var BATCH_SETTLEMENT_DOMAIN = {
|
|
36
|
+
name: "x402 Batch Settlement",
|
|
37
|
+
version: "1"
|
|
38
|
+
};
|
|
39
|
+
var CHANNEL_CONFIG_TYPEHASH = (0, import_viem.keccak256)(
|
|
40
|
+
(0, import_viem.toBytes)(
|
|
41
|
+
"ChannelConfig(address payer,address payerAuthorizer,address receiver,address receiverAuthorizer,address token,uint40 withdrawDelay,bytes32 salt)"
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
var channelConfigTypes = {
|
|
45
|
+
ChannelConfig: [
|
|
46
|
+
{ name: "payer", type: "address" },
|
|
47
|
+
{ name: "payerAuthorizer", type: "address" },
|
|
48
|
+
{ name: "receiver", type: "address" },
|
|
49
|
+
{ name: "receiverAuthorizer", type: "address" },
|
|
50
|
+
{ name: "token", type: "address" },
|
|
51
|
+
{ name: "withdrawDelay", type: "uint40" },
|
|
52
|
+
{ name: "salt", type: "bytes32" }
|
|
53
|
+
]
|
|
54
|
+
};
|
|
55
|
+
var voucherTypes = {
|
|
56
|
+
Voucher: [
|
|
57
|
+
{ name: "channelId", type: "bytes32" },
|
|
58
|
+
{ name: "maxClaimableAmount", type: "uint128" }
|
|
59
|
+
]
|
|
60
|
+
};
|
|
61
|
+
var refundTypes = {
|
|
62
|
+
Refund: [
|
|
63
|
+
{ name: "channelId", type: "bytes32" },
|
|
64
|
+
{ name: "nonce", type: "uint256" },
|
|
65
|
+
{ name: "amount", type: "uint128" }
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
var claimBatchTypes = {
|
|
69
|
+
ClaimBatch: [{ name: "claims", type: "ClaimEntry[]" }],
|
|
70
|
+
ClaimEntry: [
|
|
71
|
+
{ name: "channelId", type: "bytes32" },
|
|
72
|
+
{ name: "maxClaimableAmount", type: "uint128" },
|
|
73
|
+
{ name: "totalClaimed", type: "uint128" }
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
var receiveAuthorizationTypes = {
|
|
77
|
+
ReceiveWithAuthorization: [
|
|
78
|
+
{ name: "from", type: "address" },
|
|
79
|
+
{ name: "to", type: "address" },
|
|
80
|
+
{ name: "value", type: "uint256" },
|
|
81
|
+
{ name: "validAfter", type: "uint256" },
|
|
82
|
+
{ name: "validBefore", type: "uint256" },
|
|
83
|
+
{ name: "nonce", type: "bytes32" }
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
var batchPermit2WitnessTypes = {
|
|
87
|
+
PermitWitnessTransferFrom: [
|
|
88
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
89
|
+
{ name: "spender", type: "address" },
|
|
90
|
+
{ name: "nonce", type: "uint256" },
|
|
91
|
+
{ name: "deadline", type: "uint256" },
|
|
92
|
+
{ name: "witness", type: "DepositWitness" }
|
|
93
|
+
],
|
|
94
|
+
TokenPermissions: [
|
|
95
|
+
{ name: "token", type: "address" },
|
|
96
|
+
{ name: "amount", type: "uint256" }
|
|
97
|
+
],
|
|
98
|
+
DepositWitness: [{ name: "channelId", type: "bytes32" }]
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/batch-settlement/types.ts
|
|
102
|
+
function isObject(payload) {
|
|
103
|
+
return typeof payload === "object" && payload !== null;
|
|
104
|
+
}
|
|
105
|
+
function isVoucherFields(payload) {
|
|
106
|
+
return isObject(payload) && "channelId" in payload && "maxClaimableAmount" in payload && "signature" in payload;
|
|
107
|
+
}
|
|
108
|
+
function isBatchSettlementDepositPayload(payload) {
|
|
109
|
+
return isObject(payload) && payload.type === "deposit" && "channelConfig" in payload && isVoucherFields(payload.voucher) && isObject(payload.deposit) && typeof payload.deposit.amount === "string" && isObject(payload.deposit.authorization);
|
|
110
|
+
}
|
|
111
|
+
function isBatchSettlementVoucherPayload(payload) {
|
|
112
|
+
return isObject(payload) && payload.type === "voucher" && "channelConfig" in payload && isVoucherFields(payload.voucher);
|
|
113
|
+
}
|
|
114
|
+
function isBatchSettlementRefundPayload(payload) {
|
|
115
|
+
return isObject(payload) && payload.type === "refund" && "channelConfig" in payload && isVoucherFields(payload.voucher);
|
|
116
|
+
}
|
|
117
|
+
function isBatchSettlementClaimPayload(payload) {
|
|
118
|
+
return isObject(payload) && payload.type === "claim" && "claims" in payload;
|
|
119
|
+
}
|
|
120
|
+
function isBatchSettlementSettlePayload(payload) {
|
|
121
|
+
return isObject(payload) && payload.type === "settle" && "receiver" in payload && "token" in payload;
|
|
122
|
+
}
|
|
123
|
+
function isBatchSettlementEnrichedRefundPayload(payload) {
|
|
124
|
+
return isBatchSettlementRefundPayload(payload) && "amount" in payload && "refundNonce" in payload && "claims" in payload;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/batch-settlement/facilitator/deposit.ts
|
|
128
|
+
var import_viem11 = require("viem");
|
|
129
|
+
|
|
130
|
+
// src/batch-settlement/abi.ts
|
|
131
|
+
var channelConfigComponents = [
|
|
132
|
+
{ name: "payer", type: "address" },
|
|
133
|
+
{ name: "payerAuthorizer", type: "address" },
|
|
134
|
+
{ name: "receiver", type: "address" },
|
|
135
|
+
{ name: "receiverAuthorizer", type: "address" },
|
|
136
|
+
{ name: "token", type: "address" },
|
|
137
|
+
{ name: "withdrawDelay", type: "uint40" },
|
|
138
|
+
{ name: "salt", type: "bytes32" }
|
|
139
|
+
];
|
|
140
|
+
var voucherClaimComponents = [
|
|
141
|
+
{
|
|
142
|
+
name: "voucher",
|
|
143
|
+
type: "tuple",
|
|
144
|
+
components: [
|
|
145
|
+
{
|
|
146
|
+
name: "channel",
|
|
147
|
+
type: "tuple",
|
|
148
|
+
components: channelConfigComponents
|
|
149
|
+
},
|
|
150
|
+
{ name: "maxClaimableAmount", type: "uint128" }
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{ name: "signature", type: "bytes" },
|
|
154
|
+
{ name: "totalClaimed", type: "uint128" }
|
|
155
|
+
];
|
|
156
|
+
var batchSettlementABI = [
|
|
157
|
+
{
|
|
158
|
+
type: "function",
|
|
159
|
+
name: "multicall",
|
|
160
|
+
inputs: [{ name: "data", type: "bytes[]" }],
|
|
161
|
+
outputs: [{ name: "results", type: "bytes[]" }],
|
|
162
|
+
stateMutability: "nonpayable"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: "function",
|
|
166
|
+
name: "deposit",
|
|
167
|
+
inputs: [
|
|
168
|
+
{ name: "config", type: "tuple", components: channelConfigComponents },
|
|
169
|
+
{ name: "amount", type: "uint128" },
|
|
170
|
+
{ name: "collector", type: "address" },
|
|
171
|
+
{ name: "collectorData", type: "bytes" }
|
|
172
|
+
],
|
|
173
|
+
outputs: [],
|
|
174
|
+
stateMutability: "nonpayable"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
type: "function",
|
|
178
|
+
name: "claim",
|
|
179
|
+
inputs: [{ name: "voucherClaims", type: "tuple[]", components: voucherClaimComponents }],
|
|
180
|
+
outputs: [],
|
|
181
|
+
stateMutability: "nonpayable"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: "function",
|
|
185
|
+
name: "claimWithSignature",
|
|
186
|
+
inputs: [
|
|
187
|
+
{ name: "voucherClaims", type: "tuple[]", components: voucherClaimComponents },
|
|
188
|
+
{ name: "authorizerSignature", type: "bytes" }
|
|
189
|
+
],
|
|
190
|
+
outputs: [],
|
|
191
|
+
stateMutability: "nonpayable"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
type: "function",
|
|
195
|
+
name: "settle",
|
|
196
|
+
inputs: [
|
|
197
|
+
{ name: "receiver", type: "address" },
|
|
198
|
+
{ name: "token", type: "address" }
|
|
199
|
+
],
|
|
200
|
+
outputs: [],
|
|
201
|
+
stateMutability: "nonpayable"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
type: "function",
|
|
205
|
+
name: "initiateWithdraw",
|
|
206
|
+
inputs: [
|
|
207
|
+
{ name: "config", type: "tuple", components: channelConfigComponents },
|
|
208
|
+
{ name: "amount", type: "uint128" }
|
|
209
|
+
],
|
|
210
|
+
outputs: [],
|
|
211
|
+
stateMutability: "nonpayable"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
type: "function",
|
|
215
|
+
name: "finalizeWithdraw",
|
|
216
|
+
inputs: [{ name: "config", type: "tuple", components: channelConfigComponents }],
|
|
217
|
+
outputs: [],
|
|
218
|
+
stateMutability: "nonpayable"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: "function",
|
|
222
|
+
name: "refund",
|
|
223
|
+
inputs: [
|
|
224
|
+
{ name: "config", type: "tuple", components: channelConfigComponents },
|
|
225
|
+
{ name: "amount", type: "uint128" }
|
|
226
|
+
],
|
|
227
|
+
outputs: [],
|
|
228
|
+
stateMutability: "nonpayable"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
type: "function",
|
|
232
|
+
name: "refundWithSignature",
|
|
233
|
+
inputs: [
|
|
234
|
+
{ name: "config", type: "tuple", components: channelConfigComponents },
|
|
235
|
+
{ name: "amount", type: "uint128" },
|
|
236
|
+
{ name: "nonce", type: "uint256" },
|
|
237
|
+
{ name: "receiverAuthorizerSignature", type: "bytes" }
|
|
238
|
+
],
|
|
239
|
+
outputs: [],
|
|
240
|
+
stateMutability: "nonpayable"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
type: "function",
|
|
244
|
+
name: "getChannelId",
|
|
245
|
+
inputs: [{ name: "config", type: "tuple", components: channelConfigComponents }],
|
|
246
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
247
|
+
stateMutability: "view"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
type: "function",
|
|
251
|
+
name: "CHANNEL_CONFIG_TYPEHASH",
|
|
252
|
+
inputs: [],
|
|
253
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
254
|
+
stateMutability: "view"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
type: "function",
|
|
258
|
+
name: "channels",
|
|
259
|
+
inputs: [{ name: "channelId", type: "bytes32" }],
|
|
260
|
+
outputs: [
|
|
261
|
+
{ name: "balance", type: "uint128" },
|
|
262
|
+
{ name: "totalClaimed", type: "uint128" }
|
|
263
|
+
],
|
|
264
|
+
stateMutability: "view"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
type: "function",
|
|
268
|
+
name: "pendingWithdrawals",
|
|
269
|
+
inputs: [{ name: "channelId", type: "bytes32" }],
|
|
270
|
+
outputs: [
|
|
271
|
+
{ name: "amount", type: "uint128" },
|
|
272
|
+
{ name: "initiatedAt", type: "uint40" }
|
|
273
|
+
],
|
|
274
|
+
stateMutability: "view"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
type: "function",
|
|
278
|
+
name: "receivers",
|
|
279
|
+
inputs: [
|
|
280
|
+
{ name: "receiver", type: "address" },
|
|
281
|
+
{ name: "token", type: "address" }
|
|
282
|
+
],
|
|
283
|
+
outputs: [
|
|
284
|
+
{ name: "totalClaimed", type: "uint128" },
|
|
285
|
+
{ name: "totalSettled", type: "uint128" }
|
|
286
|
+
],
|
|
287
|
+
stateMutability: "view"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
type: "function",
|
|
291
|
+
name: "getVoucherDigest",
|
|
292
|
+
inputs: [
|
|
293
|
+
{ name: "channelId", type: "bytes32" },
|
|
294
|
+
{ name: "maxClaimableAmount", type: "uint128" }
|
|
295
|
+
],
|
|
296
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
297
|
+
stateMutability: "view"
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
type: "function",
|
|
301
|
+
name: "getRefundDigest",
|
|
302
|
+
inputs: [
|
|
303
|
+
{ name: "channelId", type: "bytes32" },
|
|
304
|
+
{ name: "nonce", type: "uint256" },
|
|
305
|
+
{ name: "amount", type: "uint128" }
|
|
306
|
+
],
|
|
307
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
308
|
+
stateMutability: "view"
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
type: "function",
|
|
312
|
+
name: "refundNonce",
|
|
313
|
+
inputs: [{ name: "channelId", type: "bytes32" }],
|
|
314
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
315
|
+
stateMutability: "view"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
type: "function",
|
|
319
|
+
name: "getClaimBatchDigest",
|
|
320
|
+
inputs: [{ name: "voucherClaims", type: "tuple[]", components: voucherClaimComponents }],
|
|
321
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
322
|
+
stateMutability: "view"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
type: "event",
|
|
326
|
+
name: "Settled",
|
|
327
|
+
inputs: [
|
|
328
|
+
{ name: "receiver", type: "address", indexed: true },
|
|
329
|
+
{ name: "token", type: "address", indexed: true },
|
|
330
|
+
{ name: "sender", type: "address", indexed: true },
|
|
331
|
+
{ name: "amount", type: "uint128", indexed: false }
|
|
332
|
+
],
|
|
333
|
+
anonymous: false
|
|
334
|
+
}
|
|
335
|
+
];
|
|
336
|
+
var erc20BalanceOfABI = [
|
|
337
|
+
{
|
|
338
|
+
type: "function",
|
|
339
|
+
name: "balanceOf",
|
|
340
|
+
inputs: [{ name: "account", type: "address" }],
|
|
341
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
342
|
+
stateMutability: "view"
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
// src/utils.ts
|
|
347
|
+
var import_viem2 = require("viem");
|
|
348
|
+
function getEvmChainId(network) {
|
|
349
|
+
if (network.startsWith("eip155:")) {
|
|
350
|
+
const idStr = network.split(":")[1];
|
|
351
|
+
const chainId = parseInt(idStr, 10);
|
|
352
|
+
if (isNaN(chainId)) {
|
|
353
|
+
throw new Error(`Invalid CAIP-2 chain ID: ${network}`);
|
|
354
|
+
}
|
|
355
|
+
return chainId;
|
|
356
|
+
}
|
|
357
|
+
throw new Error(`Unsupported network format: ${network} (expected eip155:CHAIN_ID)`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/multicall.ts
|
|
361
|
+
var import_viem3 = require("viem");
|
|
362
|
+
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
363
|
+
var multicall3ABI = [
|
|
364
|
+
{
|
|
365
|
+
inputs: [
|
|
366
|
+
{ name: "requireSuccess", type: "bool" },
|
|
367
|
+
{
|
|
368
|
+
name: "calls",
|
|
369
|
+
type: "tuple[]",
|
|
370
|
+
components: [
|
|
371
|
+
{ name: "target", type: "address" },
|
|
372
|
+
{ name: "callData", type: "bytes" }
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
],
|
|
376
|
+
name: "tryAggregate",
|
|
377
|
+
outputs: [
|
|
378
|
+
{
|
|
379
|
+
name: "returnData",
|
|
380
|
+
type: "tuple[]",
|
|
381
|
+
components: [
|
|
382
|
+
{ name: "success", type: "bool" },
|
|
383
|
+
{ name: "returnData", type: "bytes" }
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
],
|
|
387
|
+
stateMutability: "payable",
|
|
388
|
+
type: "function"
|
|
389
|
+
}
|
|
390
|
+
];
|
|
391
|
+
async function multicall(readContract, calls) {
|
|
392
|
+
const aggregateCalls = calls.map((call) => {
|
|
393
|
+
if ("callData" in call) {
|
|
394
|
+
return { target: call.address, callData: call.callData };
|
|
395
|
+
}
|
|
396
|
+
const callData = (0, import_viem3.encodeFunctionData)({
|
|
397
|
+
abi: call.abi,
|
|
398
|
+
functionName: call.functionName,
|
|
399
|
+
args: call.args
|
|
400
|
+
});
|
|
401
|
+
return { target: call.address, callData };
|
|
402
|
+
});
|
|
403
|
+
const rawResults = await readContract({
|
|
404
|
+
address: MULTICALL3_ADDRESS,
|
|
405
|
+
abi: multicall3ABI,
|
|
406
|
+
functionName: "tryAggregate",
|
|
407
|
+
args: [false, aggregateCalls]
|
|
408
|
+
});
|
|
409
|
+
return rawResults.map((raw, i) => {
|
|
410
|
+
if (!raw.success) {
|
|
411
|
+
return {
|
|
412
|
+
status: "failure",
|
|
413
|
+
error: new Error(`multicall: call reverted (returnData: ${raw.returnData})`)
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const call = calls[i];
|
|
417
|
+
if ("callData" in call) {
|
|
418
|
+
return { status: "success", result: void 0 };
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
const decoded = (0, import_viem3.decodeFunctionResult)({
|
|
422
|
+
abi: call.abi,
|
|
423
|
+
functionName: call.functionName,
|
|
424
|
+
data: raw.returnData
|
|
425
|
+
});
|
|
426
|
+
return { status: "success", result: decoded };
|
|
427
|
+
} catch (err) {
|
|
428
|
+
return {
|
|
429
|
+
status: "failure",
|
|
430
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/batch-settlement/errors.ts
|
|
437
|
+
var ErrChannelNotFound = "invalid_batch_settlement_evm_channel_not_found";
|
|
438
|
+
var ErrTokenMismatch = "invalid_batch_settlement_evm_token_mismatch";
|
|
439
|
+
var ErrInvalidVoucherSignature = "invalid_batch_settlement_evm_voucher_signature";
|
|
440
|
+
var ErrCumulativeExceedsBalance = "invalid_batch_settlement_evm_cumulative_exceeds_balance";
|
|
441
|
+
var ErrCumulativeAmountBelowClaimed = "invalid_batch_settlement_evm_cumulative_below_claimed";
|
|
442
|
+
var ErrInsufficientBalance = "invalid_batch_settlement_evm_insufficient_balance";
|
|
443
|
+
var ErrDepositTransactionFailed = "invalid_batch_settlement_evm_deposit_transaction_failed";
|
|
444
|
+
var ErrClaimTransactionFailed = "invalid_batch_settlement_evm_claim_transaction_failed";
|
|
445
|
+
var ErrSettleTransactionFailed = "invalid_batch_settlement_evm_settle_transaction_failed";
|
|
446
|
+
var ErrInvalidScheme = "invalid_batch_settlement_evm_scheme";
|
|
447
|
+
var ErrNetworkMismatch = "invalid_batch_settlement_evm_network_mismatch";
|
|
448
|
+
var ErrMissingEip712Domain = "invalid_batch_settlement_evm_missing_eip712_domain";
|
|
449
|
+
var ErrValidBeforeExpired = "invalid_batch_settlement_evm_payload_authorization_valid_before";
|
|
450
|
+
var ErrValidAfterInFuture = "invalid_batch_settlement_evm_payload_authorization_valid_after";
|
|
451
|
+
var ErrInvalidReceiveAuthorizationSignature = "invalid_batch_settlement_evm_receive_authorization_signature";
|
|
452
|
+
var ErrErc3009AuthorizationRequired = "invalid_batch_settlement_evm_erc3009_authorization_required";
|
|
453
|
+
var ErrRefundTransactionFailed = "invalid_batch_settlement_evm_refund_transaction_failed";
|
|
454
|
+
var ErrInvalidPayloadType = "invalid_batch_settlement_evm_payload_type";
|
|
455
|
+
var ErrWithdrawDelayOutOfRange = "invalid_batch_settlement_evm_withdraw_delay_out_of_range";
|
|
456
|
+
var ErrChannelIdMismatch = "invalid_batch_settlement_evm_channel_id_mismatch";
|
|
457
|
+
var ErrReceiverMismatch = "invalid_batch_settlement_evm_receiver_mismatch";
|
|
458
|
+
var ErrReceiverAuthorizerMismatch = "invalid_batch_settlement_evm_receiver_authorizer_mismatch";
|
|
459
|
+
var ErrWithdrawDelayMismatch = "invalid_batch_settlement_evm_withdraw_delay_mismatch";
|
|
460
|
+
var ErrAuthorizerAddressMismatch = "invalid_batch_settlement_evm_authorizer_address_mismatch";
|
|
461
|
+
var ErrDepositSimulationFailed = "invalid_batch_settlement_evm_deposit_simulation_failed";
|
|
462
|
+
var ErrClaimSimulationFailed = "invalid_batch_settlement_evm_claim_simulation_failed";
|
|
463
|
+
var ErrSettleSimulationFailed = "invalid_batch_settlement_evm_settle_simulation_failed";
|
|
464
|
+
var ErrNothingToSettle = "invalid_batch_settlement_evm_nothing_to_settle";
|
|
465
|
+
var ErrRefundSimulationFailed = "invalid_batch_settlement_evm_refund_simulation_failed";
|
|
466
|
+
var ErrRpcReadFailed = "invalid_batch_settlement_evm_rpc_read_failed";
|
|
467
|
+
var ErrPermit2AuthorizationRequired = "invalid_batch_settlement_evm_permit2_authorization_required";
|
|
468
|
+
var ErrPermit2InvalidSpender = "invalid_batch_settlement_evm_permit2_invalid_spender";
|
|
469
|
+
var ErrPermit2AmountMismatch = "invalid_batch_settlement_evm_permit2_amount_mismatch";
|
|
470
|
+
var ErrPermit2DeadlineExpired = "invalid_batch_settlement_evm_permit2_deadline_expired";
|
|
471
|
+
var ErrPermit2InvalidSignature = "invalid_batch_settlement_evm_permit2_invalid_signature";
|
|
472
|
+
var ErrPermit2AllowanceRequired = "invalid_batch_settlement_evm_permit2_allowance_required";
|
|
473
|
+
var ErrEip2612AmountMismatch = "invalid_batch_settlement_evm_eip2612_amount_mismatch";
|
|
474
|
+
var ErrErc20ApprovalUnavailable = "invalid_batch_settlement_evm_erc20_approval_unavailable";
|
|
475
|
+
var ErrRefundNoBalance = "invalid_batch_settlement_evm_refund_no_balance";
|
|
476
|
+
|
|
477
|
+
// src/batch-settlement/facilitator/utils.ts
|
|
478
|
+
var import_viem5 = require("viem");
|
|
479
|
+
|
|
480
|
+
// src/batch-settlement/utils.ts
|
|
481
|
+
var import_viem4 = require("viem");
|
|
482
|
+
function computeChannelId(config, networkOrChainId) {
|
|
483
|
+
const chainId = typeof networkOrChainId === "number" ? networkOrChainId : getEvmChainId(networkOrChainId);
|
|
484
|
+
return (0, import_viem4.hashTypedData)({
|
|
485
|
+
domain: getBatchSettlementEip712Domain(chainId),
|
|
486
|
+
types: channelConfigTypes,
|
|
487
|
+
primaryType: "ChannelConfig",
|
|
488
|
+
message: {
|
|
489
|
+
payer: config.payer,
|
|
490
|
+
payerAuthorizer: config.payerAuthorizer,
|
|
491
|
+
receiver: config.receiver,
|
|
492
|
+
receiverAuthorizer: config.receiverAuthorizer,
|
|
493
|
+
token: config.token,
|
|
494
|
+
withdrawDelay: config.withdrawDelay,
|
|
495
|
+
salt: config.salt
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
function getBatchSettlementEip712Domain(chainId) {
|
|
500
|
+
return {
|
|
501
|
+
...BATCH_SETTLEMENT_DOMAIN,
|
|
502
|
+
chainId,
|
|
503
|
+
verifyingContract: (0, import_viem4.getAddress)(BATCH_SETTLEMENT_ADDRESS)
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/batch-settlement/facilitator/utils.ts
|
|
508
|
+
function toContractChannelConfig(config) {
|
|
509
|
+
return {
|
|
510
|
+
payer: (0, import_viem5.getAddress)(config.payer),
|
|
511
|
+
payerAuthorizer: (0, import_viem5.getAddress)(config.payerAuthorizer),
|
|
512
|
+
receiver: (0, import_viem5.getAddress)(config.receiver),
|
|
513
|
+
receiverAuthorizer: (0, import_viem5.getAddress)(config.receiverAuthorizer),
|
|
514
|
+
token: (0, import_viem5.getAddress)(config.token),
|
|
515
|
+
withdrawDelay: config.withdrawDelay,
|
|
516
|
+
salt: config.salt
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function erc3009AuthorizationTimeInvalidReason(validAfter, validBefore) {
|
|
520
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
521
|
+
if (validBefore < BigInt(now + 6)) return ErrValidBeforeExpired;
|
|
522
|
+
if (validAfter > BigInt(now)) return ErrValidAfterInFuture;
|
|
523
|
+
return void 0;
|
|
524
|
+
}
|
|
525
|
+
async function verifyBatchSettlementVoucherTypedData(signer, params, chainId) {
|
|
526
|
+
const domain = getBatchSettlementEip712Domain(chainId);
|
|
527
|
+
const message = {
|
|
528
|
+
channelId: params.channelId,
|
|
529
|
+
maxClaimableAmount: BigInt(params.maxClaimableAmount)
|
|
530
|
+
};
|
|
531
|
+
const zeroAddress = "0x0000000000000000000000000000000000000000";
|
|
532
|
+
if (params.payerAuthorizer !== zeroAddress) {
|
|
533
|
+
return (0, import_viem5.verifyTypedData)({
|
|
534
|
+
address: (0, import_viem5.getAddress)(params.payerAuthorizer),
|
|
535
|
+
domain,
|
|
536
|
+
types: voucherTypes,
|
|
537
|
+
primaryType: "Voucher",
|
|
538
|
+
message,
|
|
539
|
+
signature: params.signature
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return signer.verifyTypedData({
|
|
543
|
+
address: (0, import_viem5.getAddress)(params.payer),
|
|
544
|
+
domain,
|
|
545
|
+
types: voucherTypes,
|
|
546
|
+
primaryType: "Voucher",
|
|
547
|
+
message,
|
|
548
|
+
signature: params.signature
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function validateChannelConfig(config, channelId, requirements) {
|
|
552
|
+
const computedId = computeChannelId(config, requirements.network);
|
|
553
|
+
if (computedId.toLowerCase() !== channelId.toLowerCase()) {
|
|
554
|
+
return ErrChannelIdMismatch;
|
|
555
|
+
}
|
|
556
|
+
if ((0, import_viem5.getAddress)(config.receiver) !== (0, import_viem5.getAddress)(requirements.payTo)) {
|
|
557
|
+
return ErrReceiverMismatch;
|
|
558
|
+
}
|
|
559
|
+
const extra = requirements.extra;
|
|
560
|
+
const requiredReceiverAuthorizer = extra?.receiverAuthorizer;
|
|
561
|
+
if (!requiredReceiverAuthorizer || (0, import_viem5.getAddress)(requiredReceiverAuthorizer) === "0x0000000000000000000000000000000000000000" || (0, import_viem5.getAddress)(config.receiverAuthorizer) !== (0, import_viem5.getAddress)(requiredReceiverAuthorizer)) {
|
|
562
|
+
return ErrReceiverAuthorizerMismatch;
|
|
563
|
+
}
|
|
564
|
+
if ((0, import_viem5.getAddress)(config.token) !== (0, import_viem5.getAddress)(requirements.asset)) {
|
|
565
|
+
return ErrTokenMismatch;
|
|
566
|
+
}
|
|
567
|
+
if (extra?.withdrawDelay !== void 0 && config.withdrawDelay !== Number(extra.withdrawDelay)) {
|
|
568
|
+
return ErrWithdrawDelayMismatch;
|
|
569
|
+
}
|
|
570
|
+
if (config.withdrawDelay < MIN_WITHDRAW_DELAY || config.withdrawDelay > MAX_WITHDRAW_DELAY) {
|
|
571
|
+
return ErrWithdrawDelayOutOfRange;
|
|
572
|
+
}
|
|
573
|
+
return void 0;
|
|
574
|
+
}
|
|
575
|
+
async function readChannelState(signer, channelId) {
|
|
576
|
+
const target = (0, import_viem5.getAddress)(BATCH_SETTLEMENT_ADDRESS);
|
|
577
|
+
const mcResults = await multicall(signer.readContract.bind(signer), [
|
|
578
|
+
{ address: target, abi: batchSettlementABI, functionName: "channels", args: [channelId] },
|
|
579
|
+
{
|
|
580
|
+
address: target,
|
|
581
|
+
abi: batchSettlementABI,
|
|
582
|
+
functionName: "pendingWithdrawals",
|
|
583
|
+
args: [channelId]
|
|
584
|
+
},
|
|
585
|
+
{ address: target, abi: batchSettlementABI, functionName: "refundNonce", args: [channelId] }
|
|
586
|
+
]);
|
|
587
|
+
const [chRes, wdRes, rnRes] = mcResults;
|
|
588
|
+
if (chRes.status === "failure" || wdRes.status === "failure" || rnRes.status === "failure") {
|
|
589
|
+
throw new Error(`${ErrRpcReadFailed}: multicall returned failure for ${channelId}`);
|
|
590
|
+
}
|
|
591
|
+
const [balance, totalClaimed] = chRes.result;
|
|
592
|
+
const [, wdInitiatedAt] = wdRes.result;
|
|
593
|
+
const refundNonce = rnRes.result;
|
|
594
|
+
return { balance, totalClaimed, withdrawRequestedAt: Number(wdInitiatedAt), refundNonce };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/batch-settlement/facilitator/deposit-eip3009.ts
|
|
598
|
+
var import_viem7 = require("viem");
|
|
599
|
+
|
|
600
|
+
// src/batch-settlement/encoding.ts
|
|
601
|
+
var import_viem6 = require("viem");
|
|
602
|
+
function buildErc3009DepositNonce(channelId, salt) {
|
|
603
|
+
return (0, import_viem6.keccak256)(
|
|
604
|
+
(0, import_viem6.encodeAbiParameters)([{ type: "bytes32" }, { type: "uint256" }], [channelId, BigInt(salt)])
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
function buildErc3009CollectorData(validAfter, validBefore, salt, signature) {
|
|
608
|
+
return (0, import_viem6.encodeAbiParameters)(
|
|
609
|
+
[{ type: "uint256" }, { type: "uint256" }, { type: "uint256" }, { type: "bytes" }],
|
|
610
|
+
[BigInt(validAfter), BigInt(validBefore), BigInt(salt), signature]
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
function buildEip2612PermitData(params) {
|
|
614
|
+
return (0, import_viem6.encodeAbiParameters)(
|
|
615
|
+
[
|
|
616
|
+
{ type: "uint256" },
|
|
617
|
+
{ type: "uint256" },
|
|
618
|
+
{ type: "uint8" },
|
|
619
|
+
{ type: "bytes32" },
|
|
620
|
+
{ type: "bytes32" }
|
|
621
|
+
],
|
|
622
|
+
[BigInt(params.value), BigInt(params.deadline), params.v, params.r, params.s]
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
function buildPermit2CollectorData(nonce, deadline, permit2Signature, eip2612PermitData = "0x") {
|
|
626
|
+
return (0, import_viem6.encodeAbiParameters)(
|
|
627
|
+
[{ type: "uint256" }, { type: "uint256" }, { type: "bytes" }, { type: "bytes" }],
|
|
628
|
+
[BigInt(nonce), BigInt(deadline), permit2Signature, eip2612PermitData]
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/batch-settlement/facilitator/deposit-eip3009.ts
|
|
633
|
+
function getEip3009DepositCollectorAddress() {
|
|
634
|
+
return (0, import_viem7.getAddress)(ERC3009_DEPOSIT_COLLECTOR_ADDRESS);
|
|
635
|
+
}
|
|
636
|
+
function buildEip3009DepositCollectorData(payload) {
|
|
637
|
+
const auth = payload.deposit.authorization.erc3009Authorization;
|
|
638
|
+
if (!auth) {
|
|
639
|
+
throw new Error(ErrErc3009AuthorizationRequired);
|
|
640
|
+
}
|
|
641
|
+
const { signature } = (0, import_viem7.parseErc6492Signature)(auth.signature);
|
|
642
|
+
return buildErc3009CollectorData(auth.validAfter, auth.validBefore, auth.salt, signature);
|
|
643
|
+
}
|
|
644
|
+
async function verifyEip3009DepositAuthorization(signer, payload, requirements, chainId) {
|
|
645
|
+
const { deposit, voucher } = payload;
|
|
646
|
+
const payer = payload.channelConfig.payer;
|
|
647
|
+
const auth = deposit.authorization.erc3009Authorization;
|
|
648
|
+
if (!auth) {
|
|
649
|
+
return { isValid: false, invalidReason: ErrErc3009AuthorizationRequired, payer };
|
|
650
|
+
}
|
|
651
|
+
const extra = requirements.extra;
|
|
652
|
+
if (!extra?.name || !extra?.version) {
|
|
653
|
+
return { isValid: false, invalidReason: ErrMissingEip712Domain, payer };
|
|
654
|
+
}
|
|
655
|
+
const validAfter = BigInt(auth.validAfter);
|
|
656
|
+
const validBefore = BigInt(auth.validBefore);
|
|
657
|
+
const timeInvalid = erc3009AuthorizationTimeInvalidReason(validAfter, validBefore);
|
|
658
|
+
if (timeInvalid) {
|
|
659
|
+
return { isValid: false, invalidReason: timeInvalid, payer };
|
|
660
|
+
}
|
|
661
|
+
const erc3009Nonce = buildErc3009DepositNonce(voucher.channelId, auth.salt);
|
|
662
|
+
const receiveAuthOk = await verifyReceiveAuth(signer, {
|
|
663
|
+
payer,
|
|
664
|
+
asset: requirements.asset,
|
|
665
|
+
name: extra.name,
|
|
666
|
+
version: extra.version,
|
|
667
|
+
chainId,
|
|
668
|
+
amount: deposit.amount,
|
|
669
|
+
validAfter,
|
|
670
|
+
validBefore,
|
|
671
|
+
nonce: erc3009Nonce,
|
|
672
|
+
signature: auth.signature
|
|
673
|
+
});
|
|
674
|
+
if (!receiveAuthOk) {
|
|
675
|
+
return { isValid: false, invalidReason: ErrInvalidReceiveAuthorizationSignature, payer };
|
|
676
|
+
}
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
async function verifyReceiveAuth(signer, params) {
|
|
680
|
+
try {
|
|
681
|
+
return await signer.verifyTypedData({
|
|
682
|
+
address: (0, import_viem7.getAddress)(params.payer),
|
|
683
|
+
domain: {
|
|
684
|
+
name: params.name,
|
|
685
|
+
version: params.version,
|
|
686
|
+
chainId: params.chainId,
|
|
687
|
+
verifyingContract: (0, import_viem7.getAddress)(params.asset)
|
|
688
|
+
},
|
|
689
|
+
types: receiveAuthorizationTypes,
|
|
690
|
+
primaryType: "ReceiveWithAuthorization",
|
|
691
|
+
message: {
|
|
692
|
+
from: (0, import_viem7.getAddress)(params.payer),
|
|
693
|
+
to: (0, import_viem7.getAddress)(ERC3009_DEPOSIT_COLLECTOR_ADDRESS),
|
|
694
|
+
value: BigInt(params.amount),
|
|
695
|
+
validAfter: params.validAfter,
|
|
696
|
+
validBefore: params.validBefore,
|
|
697
|
+
nonce: params.nonce
|
|
698
|
+
},
|
|
699
|
+
signature: params.signature
|
|
700
|
+
});
|
|
701
|
+
} catch {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// src/batch-settlement/facilitator/deposit-permit2.ts
|
|
707
|
+
var import_viem10 = require("viem");
|
|
708
|
+
|
|
709
|
+
// src/exact/extensions.ts
|
|
710
|
+
var EIP2612_GAS_SPONSORING_KEY = "eip2612GasSponsoring";
|
|
711
|
+
var ERC20_APPROVAL_GAS_SPONSORING_KEY = "erc20ApprovalGasSponsoring";
|
|
712
|
+
function _extractInfo(payload, extensionKey) {
|
|
713
|
+
const extensions = payload.extensions;
|
|
714
|
+
if (!extensions) return null;
|
|
715
|
+
const extension = extensions[extensionKey];
|
|
716
|
+
if (!extension?.info) return null;
|
|
717
|
+
return extension.info;
|
|
718
|
+
}
|
|
719
|
+
function extractEip2612GasSponsoringInfo(payload) {
|
|
720
|
+
const info = _extractInfo(payload, EIP2612_GAS_SPONSORING_KEY);
|
|
721
|
+
if (!info) return null;
|
|
722
|
+
if (!info.from || !info.asset || !info.spender || !info.amount || !info.nonce || !info.deadline || !info.signature || !info.version) {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
return info;
|
|
726
|
+
}
|
|
727
|
+
function validateEip2612GasSponsoringInfo(info) {
|
|
728
|
+
const addressPattern = /^0x[a-fA-F0-9]{40}$/;
|
|
729
|
+
const numericPattern = /^[0-9]+$/;
|
|
730
|
+
const hexPattern = /^0x[a-fA-F0-9]+$/;
|
|
731
|
+
const versionPattern = /^[0-9]+(\.[0-9]+)*$/;
|
|
732
|
+
return addressPattern.test(info.from) && addressPattern.test(info.asset) && addressPattern.test(info.spender) && numericPattern.test(info.amount) && numericPattern.test(info.nonce) && numericPattern.test(info.deadline) && hexPattern.test(info.signature) && versionPattern.test(info.version);
|
|
733
|
+
}
|
|
734
|
+
function extractErc20ApprovalGasSponsoringInfo(payload) {
|
|
735
|
+
const info = _extractInfo(payload, ERC20_APPROVAL_GAS_SPONSORING_KEY);
|
|
736
|
+
if (!info) return null;
|
|
737
|
+
if (!info.from || !info.asset || !info.spender || !info.amount || !info.signedTransaction || !info.version) {
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
return info;
|
|
741
|
+
}
|
|
742
|
+
function validateErc20ApprovalGasSponsoringInfo(info) {
|
|
743
|
+
const addressPattern = /^0x[a-fA-F0-9]{40}$/;
|
|
744
|
+
const numericPattern = /^[0-9]+$/;
|
|
745
|
+
const hexPattern = /^0x[a-fA-F0-9]+$/;
|
|
746
|
+
const versionPattern = /^[0-9]+(\.[0-9]+)*$/;
|
|
747
|
+
return addressPattern.test(info.from) && addressPattern.test(info.asset) && addressPattern.test(info.spender) && numericPattern.test(info.amount) && hexPattern.test(info.signedTransaction) && versionPattern.test(info.version);
|
|
748
|
+
}
|
|
749
|
+
function resolveErc20ApprovalExtensionSigner(extension, network) {
|
|
750
|
+
if (!extension) return void 0;
|
|
751
|
+
return extension.signerForNetwork?.(network) ?? extension.signer;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/shared/erc20approval.ts
|
|
755
|
+
var import_viem8 = require("viem");
|
|
756
|
+
|
|
757
|
+
// src/constants.ts
|
|
758
|
+
var erc20ApproveAbi = [
|
|
759
|
+
{
|
|
760
|
+
type: "function",
|
|
761
|
+
name: "approve",
|
|
762
|
+
inputs: [
|
|
763
|
+
{ name: "spender", type: "address" },
|
|
764
|
+
{ name: "amount", type: "uint256" }
|
|
765
|
+
],
|
|
766
|
+
outputs: [{ type: "bool" }],
|
|
767
|
+
stateMutability: "nonpayable"
|
|
768
|
+
}
|
|
769
|
+
];
|
|
770
|
+
var erc20AllowanceAbi = [
|
|
771
|
+
{
|
|
772
|
+
type: "function",
|
|
773
|
+
name: "allowance",
|
|
774
|
+
inputs: [
|
|
775
|
+
{ name: "owner", type: "address" },
|
|
776
|
+
{ name: "spender", type: "address" }
|
|
777
|
+
],
|
|
778
|
+
outputs: [{ type: "uint256" }],
|
|
779
|
+
stateMutability: "view"
|
|
780
|
+
}
|
|
781
|
+
];
|
|
782
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
783
|
+
|
|
784
|
+
// src/exact/facilitator/errors.ts
|
|
785
|
+
var ErrErc20ApprovalInvalidFormat = "invalid_erc20_approval_extension_format";
|
|
786
|
+
var ErrErc20ApprovalFromMismatch = "erc20_approval_from_mismatch";
|
|
787
|
+
var ErrErc20ApprovalAssetMismatch = "erc20_approval_asset_mismatch";
|
|
788
|
+
var ErrErc20ApprovalSpenderNotPermit2 = "erc20_approval_spender_not_permit2";
|
|
789
|
+
var ErrErc20ApprovalTxWrongTarget = "erc20_approval_tx_wrong_target";
|
|
790
|
+
var ErrErc20ApprovalTxWrongSelector = "erc20_approval_tx_wrong_selector";
|
|
791
|
+
var ErrErc20ApprovalTxWrongSpender = "erc20_approval_tx_wrong_spender";
|
|
792
|
+
var ErrErc20ApprovalTxInvalidCalldata = "erc20_approval_tx_invalid_calldata";
|
|
793
|
+
var ErrErc20ApprovalTxSignerMismatch = "erc20_approval_tx_signer_mismatch";
|
|
794
|
+
var ErrErc20ApprovalTxInvalidSignature = "erc20_approval_tx_invalid_signature";
|
|
795
|
+
var ErrErc20ApprovalTxParseFailed = "erc20_approval_tx_parse_failed";
|
|
796
|
+
var ErrInvalidEip2612ExtensionFormat = "invalid_eip2612_extension_format";
|
|
797
|
+
var ErrEip2612FromMismatch = "eip2612_from_mismatch";
|
|
798
|
+
var ErrEip2612AssetMismatch = "eip2612_asset_mismatch";
|
|
799
|
+
var ErrEip2612SpenderNotPermit2 = "eip2612_spender_not_permit2";
|
|
800
|
+
var ErrEip2612DeadlineExpired = "eip2612_deadline_expired";
|
|
801
|
+
|
|
802
|
+
// src/shared/erc20approval.ts
|
|
803
|
+
var APPROVE_SELECTOR = "0x095ea7b3";
|
|
804
|
+
async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
|
|
805
|
+
if (!validateErc20ApprovalGasSponsoringInfo(info)) {
|
|
806
|
+
return {
|
|
807
|
+
isValid: false,
|
|
808
|
+
invalidReason: ErrErc20ApprovalInvalidFormat,
|
|
809
|
+
invalidMessage: "ERC-20 approval extension info failed schema validation"
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if ((0, import_viem8.getAddress)(info.from) !== (0, import_viem8.getAddress)(payer)) {
|
|
813
|
+
return {
|
|
814
|
+
isValid: false,
|
|
815
|
+
invalidReason: ErrErc20ApprovalFromMismatch,
|
|
816
|
+
invalidMessage: `Expected from=${payer}, got ${info.from}`
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
if ((0, import_viem8.getAddress)(info.asset) !== tokenAddress) {
|
|
820
|
+
return {
|
|
821
|
+
isValid: false,
|
|
822
|
+
invalidReason: ErrErc20ApprovalAssetMismatch,
|
|
823
|
+
invalidMessage: `Expected asset=${tokenAddress}, got ${info.asset}`
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
if ((0, import_viem8.getAddress)(info.spender) !== (0, import_viem8.getAddress)(PERMIT2_ADDRESS)) {
|
|
827
|
+
return {
|
|
828
|
+
isValid: false,
|
|
829
|
+
invalidReason: ErrErc20ApprovalSpenderNotPermit2,
|
|
830
|
+
invalidMessage: `Expected spender=${PERMIT2_ADDRESS}, got ${info.spender}`
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
try {
|
|
834
|
+
const serializedTx = info.signedTransaction;
|
|
835
|
+
const tx = (0, import_viem8.parseTransaction)(serializedTx);
|
|
836
|
+
if (!tx.to || (0, import_viem8.getAddress)(tx.to) !== tokenAddress) {
|
|
837
|
+
return {
|
|
838
|
+
isValid: false,
|
|
839
|
+
invalidReason: ErrErc20ApprovalTxWrongTarget,
|
|
840
|
+
invalidMessage: `Transaction targets ${tx.to ?? "null"}, expected ${tokenAddress}`
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
const data = tx.data ?? "0x";
|
|
844
|
+
if (!data.startsWith(APPROVE_SELECTOR)) {
|
|
845
|
+
return {
|
|
846
|
+
isValid: false,
|
|
847
|
+
invalidReason: ErrErc20ApprovalTxWrongSelector,
|
|
848
|
+
invalidMessage: `Transaction calldata does not start with approve() selector ${APPROVE_SELECTOR}`
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
const decoded = (0, import_viem8.decodeFunctionData)({
|
|
853
|
+
abi: erc20ApproveAbi,
|
|
854
|
+
data
|
|
855
|
+
});
|
|
856
|
+
const calldataSpender = (0, import_viem8.getAddress)(decoded.args[0]);
|
|
857
|
+
if (calldataSpender !== (0, import_viem8.getAddress)(PERMIT2_ADDRESS)) {
|
|
858
|
+
return {
|
|
859
|
+
isValid: false,
|
|
860
|
+
invalidReason: ErrErc20ApprovalTxWrongSpender,
|
|
861
|
+
invalidMessage: `approve() spender is ${calldataSpender}, expected Permit2 ${PERMIT2_ADDRESS}`
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
} catch {
|
|
865
|
+
return {
|
|
866
|
+
isValid: false,
|
|
867
|
+
invalidReason: ErrErc20ApprovalTxInvalidCalldata,
|
|
868
|
+
invalidMessage: "Failed to decode approve() calldata from the signed transaction"
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
const recoveredAddress = await (0, import_viem8.recoverTransactionAddress)({
|
|
873
|
+
serializedTransaction: serializedTx
|
|
874
|
+
});
|
|
875
|
+
if ((0, import_viem8.getAddress)(recoveredAddress) !== (0, import_viem8.getAddress)(payer)) {
|
|
876
|
+
return {
|
|
877
|
+
isValid: false,
|
|
878
|
+
invalidReason: ErrErc20ApprovalTxSignerMismatch,
|
|
879
|
+
invalidMessage: `Transaction signed by ${recoveredAddress}, expected payer ${payer}`
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
} catch {
|
|
883
|
+
return {
|
|
884
|
+
isValid: false,
|
|
885
|
+
invalidReason: ErrErc20ApprovalTxInvalidSignature,
|
|
886
|
+
invalidMessage: "Failed to recover signer from the signed transaction"
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
} catch {
|
|
890
|
+
return {
|
|
891
|
+
isValid: false,
|
|
892
|
+
invalidReason: ErrErc20ApprovalTxParseFailed,
|
|
893
|
+
invalidMessage: "Failed to parse the signed transaction"
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
return { isValid: true };
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// src/shared/permit2.ts
|
|
900
|
+
var import_viem9 = require("viem");
|
|
901
|
+
function validateEip2612PermitForPayment(info, payer, tokenAddress) {
|
|
902
|
+
if (!validateEip2612GasSponsoringInfo(info)) {
|
|
903
|
+
return { isValid: false, invalidReason: ErrInvalidEip2612ExtensionFormat };
|
|
904
|
+
}
|
|
905
|
+
if ((0, import_viem9.getAddress)(info.from) !== (0, import_viem9.getAddress)(payer)) {
|
|
906
|
+
return { isValid: false, invalidReason: ErrEip2612FromMismatch };
|
|
907
|
+
}
|
|
908
|
+
if ((0, import_viem9.getAddress)(info.asset) !== tokenAddress) {
|
|
909
|
+
return { isValid: false, invalidReason: ErrEip2612AssetMismatch };
|
|
910
|
+
}
|
|
911
|
+
if ((0, import_viem9.getAddress)(info.spender) !== (0, import_viem9.getAddress)(PERMIT2_ADDRESS)) {
|
|
912
|
+
return { isValid: false, invalidReason: ErrEip2612SpenderNotPermit2 };
|
|
913
|
+
}
|
|
914
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
915
|
+
if (BigInt(info.deadline) < BigInt(now + 6)) {
|
|
916
|
+
return { isValid: false, invalidReason: ErrEip2612DeadlineExpired };
|
|
917
|
+
}
|
|
918
|
+
return { isValid: true };
|
|
919
|
+
}
|
|
920
|
+
function splitEip2612Signature(signature) {
|
|
921
|
+
const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
|
|
922
|
+
if (sig.length !== 130) {
|
|
923
|
+
throw new Error(
|
|
924
|
+
`invalid EIP-2612 signature length: expected 65 bytes (130 hex chars), got ${sig.length / 2} bytes`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
const r = `0x${sig.slice(0, 64)}`;
|
|
928
|
+
const s = `0x${sig.slice(64, 128)}`;
|
|
929
|
+
const v = parseInt(sig.slice(128, 130), 16);
|
|
930
|
+
return { v, r, s };
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// src/batch-settlement/facilitator/deposit-permit2.ts
|
|
934
|
+
function getPermit2DepositCollectorAddress() {
|
|
935
|
+
return (0, import_viem10.getAddress)(PERMIT2_DEPOSIT_COLLECTOR_ADDRESS);
|
|
936
|
+
}
|
|
937
|
+
function buildPermit2DepositCollectorData(payload, eip2612PermitData = "0x") {
|
|
938
|
+
const auth = payload.deposit.authorization.permit2Authorization;
|
|
939
|
+
if (!auth) {
|
|
940
|
+
throw new Error(ErrPermit2AuthorizationRequired);
|
|
941
|
+
}
|
|
942
|
+
const { signature } = (0, import_viem10.parseErc6492Signature)(auth.signature);
|
|
943
|
+
return buildPermit2CollectorData(auth.nonce, auth.deadline, signature, eip2612PermitData);
|
|
944
|
+
}
|
|
945
|
+
async function verifyPermit2DepositAuthorization(signer, payment, payload, requirements, chainId, context) {
|
|
946
|
+
const authResult = await verifyPermit2TypedData(signer, payload, requirements, chainId);
|
|
947
|
+
if (authResult) {
|
|
948
|
+
return authResult;
|
|
949
|
+
}
|
|
950
|
+
const branchResult = await resolvePermit2DepositBranch(
|
|
951
|
+
signer,
|
|
952
|
+
payment,
|
|
953
|
+
payload,
|
|
954
|
+
requirements,
|
|
955
|
+
context
|
|
956
|
+
);
|
|
957
|
+
if ("isValid" in branchResult) {
|
|
958
|
+
return branchResult;
|
|
959
|
+
}
|
|
960
|
+
if (branchResult.kind !== "erc20Approval" || !branchResult.extensionSigner.simulateTransactions) {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
const ok = await branchResult.extensionSigner.simulateTransactions([
|
|
964
|
+
branchResult.signedTransaction,
|
|
965
|
+
buildDepositTransaction(payload, branchResult.collectorData)
|
|
966
|
+
]);
|
|
967
|
+
if (!ok) {
|
|
968
|
+
return {
|
|
969
|
+
isValid: false,
|
|
970
|
+
invalidReason: ErrDepositSimulationFailed,
|
|
971
|
+
payer: payload.channelConfig.payer
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
async function resolvePermit2DepositBranch(signer, payment, payload, requirements, context) {
|
|
977
|
+
const payer = payload.channelConfig.payer;
|
|
978
|
+
const tokenAddress = (0, import_viem10.getAddress)(requirements.asset);
|
|
979
|
+
const eip2612Info = extractEip2612GasSponsoringInfo(payment);
|
|
980
|
+
if (eip2612Info) {
|
|
981
|
+
const result = validateBatchEip2612Permit(
|
|
982
|
+
eip2612Info,
|
|
983
|
+
payer,
|
|
984
|
+
tokenAddress,
|
|
985
|
+
payload.deposit.amount
|
|
986
|
+
);
|
|
987
|
+
if (!result.isValid) {
|
|
988
|
+
return { isValid: false, invalidReason: result.invalidReason, payer };
|
|
989
|
+
}
|
|
990
|
+
const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
|
|
991
|
+
return {
|
|
992
|
+
kind: "eip2612",
|
|
993
|
+
collectorData: buildPermit2DepositCollectorData(
|
|
994
|
+
payload,
|
|
995
|
+
buildEip2612PermitData({
|
|
996
|
+
value: eip2612Info.amount,
|
|
997
|
+
deadline: eip2612Info.deadline,
|
|
998
|
+
v,
|
|
999
|
+
r,
|
|
1000
|
+
s
|
|
1001
|
+
})
|
|
1002
|
+
)
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
const erc20Info = extractErc20ApprovalGasSponsoringInfo(payment);
|
|
1006
|
+
if (erc20Info) {
|
|
1007
|
+
const extensionSigner = resolveErc20ApprovalExtensionSigner(
|
|
1008
|
+
context?.getExtension(
|
|
1009
|
+
ERC20_APPROVAL_GAS_SPONSORING_KEY
|
|
1010
|
+
),
|
|
1011
|
+
requirements.network
|
|
1012
|
+
);
|
|
1013
|
+
if (!extensionSigner) {
|
|
1014
|
+
return { isValid: false, invalidReason: ErrErc20ApprovalUnavailable, payer };
|
|
1015
|
+
}
|
|
1016
|
+
const result = await validateErc20ApprovalForPayment(erc20Info, payer, tokenAddress);
|
|
1017
|
+
if (!result.isValid) {
|
|
1018
|
+
return {
|
|
1019
|
+
isValid: false,
|
|
1020
|
+
invalidReason: result.invalidReason,
|
|
1021
|
+
invalidMessage: result.invalidMessage,
|
|
1022
|
+
payer
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
return {
|
|
1026
|
+
kind: "erc20Approval",
|
|
1027
|
+
collectorData: buildPermit2DepositCollectorData(payload),
|
|
1028
|
+
signedTransaction: erc20Info.signedTransaction,
|
|
1029
|
+
extensionSigner
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
try {
|
|
1033
|
+
const allowance = await signer.readContract({
|
|
1034
|
+
address: tokenAddress,
|
|
1035
|
+
abi: erc20AllowanceAbi,
|
|
1036
|
+
functionName: "allowance",
|
|
1037
|
+
args: [payer, PERMIT2_ADDRESS]
|
|
1038
|
+
});
|
|
1039
|
+
if (allowance < BigInt(payload.deposit.amount)) {
|
|
1040
|
+
return { isValid: false, invalidReason: ErrPermit2AllowanceRequired, payer };
|
|
1041
|
+
}
|
|
1042
|
+
} catch {
|
|
1043
|
+
return { isValid: false, invalidReason: ErrPermit2AllowanceRequired, payer };
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
kind: "standard",
|
|
1047
|
+
collectorData: buildPermit2DepositCollectorData(payload)
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function buildDepositTransaction(payload, collectorData) {
|
|
1051
|
+
return {
|
|
1052
|
+
to: (0, import_viem10.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1053
|
+
data: (0, import_viem10.encodeFunctionData)({
|
|
1054
|
+
abi: batchSettlementABI,
|
|
1055
|
+
functionName: "deposit",
|
|
1056
|
+
args: [
|
|
1057
|
+
toContractChannelConfig(payload.channelConfig),
|
|
1058
|
+
BigInt(payload.deposit.amount),
|
|
1059
|
+
getPermit2DepositCollectorAddress(),
|
|
1060
|
+
collectorData
|
|
1061
|
+
]
|
|
1062
|
+
}),
|
|
1063
|
+
gas: 300000n
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
async function verifyPermit2TypedData(signer, payload, requirements, chainId) {
|
|
1067
|
+
const auth = payload.deposit.authorization.permit2Authorization;
|
|
1068
|
+
const payer = payload.channelConfig.payer;
|
|
1069
|
+
if (!auth) {
|
|
1070
|
+
return { isValid: false, invalidReason: ErrPermit2AuthorizationRequired, payer };
|
|
1071
|
+
}
|
|
1072
|
+
if ((0, import_viem10.getAddress)(auth.from) !== (0, import_viem10.getAddress)(payer)) {
|
|
1073
|
+
return { isValid: false, invalidReason: ErrPermit2InvalidSignature, payer };
|
|
1074
|
+
}
|
|
1075
|
+
if ((0, import_viem10.getAddress)(auth.spender) !== getPermit2DepositCollectorAddress()) {
|
|
1076
|
+
return { isValid: false, invalidReason: ErrPermit2InvalidSpender, payer };
|
|
1077
|
+
}
|
|
1078
|
+
if ((0, import_viem10.getAddress)(auth.permitted.token) !== (0, import_viem10.getAddress)(requirements.asset)) {
|
|
1079
|
+
return { isValid: false, invalidReason: ErrTokenMismatch, payer };
|
|
1080
|
+
}
|
|
1081
|
+
if (BigInt(auth.permitted.amount) !== BigInt(payload.deposit.amount)) {
|
|
1082
|
+
return { isValid: false, invalidReason: ErrPermit2AmountMismatch, payer };
|
|
1083
|
+
}
|
|
1084
|
+
if (auth.witness.channelId !== payload.voucher.channelId) {
|
|
1085
|
+
return { isValid: false, invalidReason: ErrChannelIdMismatch, payer };
|
|
1086
|
+
}
|
|
1087
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1088
|
+
if (BigInt(auth.deadline) < BigInt(now + 6)) {
|
|
1089
|
+
return { isValid: false, invalidReason: ErrPermit2DeadlineExpired, payer };
|
|
1090
|
+
}
|
|
1091
|
+
try {
|
|
1092
|
+
const ok = await signer.verifyTypedData({
|
|
1093
|
+
address: (0, import_viem10.getAddress)(auth.from),
|
|
1094
|
+
domain: { name: "Permit2", chainId, verifyingContract: PERMIT2_ADDRESS },
|
|
1095
|
+
types: batchPermit2WitnessTypes,
|
|
1096
|
+
primaryType: "PermitWitnessTransferFrom",
|
|
1097
|
+
message: {
|
|
1098
|
+
permitted: {
|
|
1099
|
+
token: (0, import_viem10.getAddress)(auth.permitted.token),
|
|
1100
|
+
amount: BigInt(auth.permitted.amount)
|
|
1101
|
+
},
|
|
1102
|
+
spender: (0, import_viem10.getAddress)(auth.spender),
|
|
1103
|
+
nonce: BigInt(auth.nonce),
|
|
1104
|
+
deadline: BigInt(auth.deadline),
|
|
1105
|
+
witness: {
|
|
1106
|
+
channelId: auth.witness.channelId
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
signature: auth.signature
|
|
1110
|
+
});
|
|
1111
|
+
if (!ok) {
|
|
1112
|
+
return { isValid: false, invalidReason: ErrPermit2InvalidSignature, payer };
|
|
1113
|
+
}
|
|
1114
|
+
} catch {
|
|
1115
|
+
return { isValid: false, invalidReason: ErrPermit2InvalidSignature, payer };
|
|
1116
|
+
}
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
function validateBatchEip2612Permit(info, payer, tokenAddress, depositAmount) {
|
|
1120
|
+
const baseline = validateEip2612PermitForPayment(info, payer, tokenAddress);
|
|
1121
|
+
if (!baseline.isValid) {
|
|
1122
|
+
return {
|
|
1123
|
+
isValid: false,
|
|
1124
|
+
invalidReason: baseline.invalidReason ?? ErrInvalidPayloadType
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
if (BigInt(info.amount) !== BigInt(depositAmount)) {
|
|
1128
|
+
return { isValid: false, invalidReason: ErrEip2612AmountMismatch };
|
|
1129
|
+
}
|
|
1130
|
+
return { isValid: true };
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// src/batch-settlement/facilitator/deposit.ts
|
|
1134
|
+
async function verifyDeposit(signer, payment, payload, requirements, context) {
|
|
1135
|
+
const payer = payload.channelConfig.payer;
|
|
1136
|
+
const chainId = getEvmChainId(requirements.network);
|
|
1137
|
+
const configErr = validateChannelConfig(
|
|
1138
|
+
payload.channelConfig,
|
|
1139
|
+
payload.voucher.channelId,
|
|
1140
|
+
requirements
|
|
1141
|
+
);
|
|
1142
|
+
if (configErr) {
|
|
1143
|
+
return { isValid: false, invalidReason: configErr, payer };
|
|
1144
|
+
}
|
|
1145
|
+
const transferMethod = resolveDepositTransferMethod(payload, requirements);
|
|
1146
|
+
if (transferMethod === "permit2" && !payload.deposit.authorization.permit2Authorization) {
|
|
1147
|
+
return { isValid: false, invalidReason: ErrInvalidPayloadType, payer };
|
|
1148
|
+
}
|
|
1149
|
+
const methodErr = transferMethod === "permit2" ? await verifyPermit2DepositAuthorization(
|
|
1150
|
+
signer,
|
|
1151
|
+
payment,
|
|
1152
|
+
payload,
|
|
1153
|
+
requirements,
|
|
1154
|
+
chainId,
|
|
1155
|
+
context
|
|
1156
|
+
) : await verifyEip3009DepositAuthorization(signer, payload, requirements, chainId);
|
|
1157
|
+
if (methodErr) {
|
|
1158
|
+
return methodErr;
|
|
1159
|
+
}
|
|
1160
|
+
const shared = await verifySharedDepositState(signer, payload, requirements);
|
|
1161
|
+
if (!shared.ok) {
|
|
1162
|
+
return shared.response;
|
|
1163
|
+
}
|
|
1164
|
+
const { depositAmount, chBalance, chTotalClaimed, wdInitiatedAt, refundNonceVal } = shared;
|
|
1165
|
+
const execution = await resolveDepositExecution(signer, payment, payload, requirements, context);
|
|
1166
|
+
if ("isValid" in execution) {
|
|
1167
|
+
return execution;
|
|
1168
|
+
}
|
|
1169
|
+
if (!execution.skipDirectSimulation) {
|
|
1170
|
+
try {
|
|
1171
|
+
await signer.readContract({
|
|
1172
|
+
address: (0, import_viem11.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1173
|
+
abi: batchSettlementABI,
|
|
1174
|
+
functionName: "deposit",
|
|
1175
|
+
args: [
|
|
1176
|
+
toContractChannelConfig(payload.channelConfig),
|
|
1177
|
+
depositAmount,
|
|
1178
|
+
execution.collector,
|
|
1179
|
+
execution.collectorData
|
|
1180
|
+
]
|
|
1181
|
+
});
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
return {
|
|
1184
|
+
isValid: false,
|
|
1185
|
+
invalidReason: ErrDepositSimulationFailed,
|
|
1186
|
+
invalidMessage: e instanceof Error ? e.message : String(e),
|
|
1187
|
+
payer
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return {
|
|
1192
|
+
isValid: true,
|
|
1193
|
+
payer,
|
|
1194
|
+
extra: {
|
|
1195
|
+
channelId: payload.voucher.channelId,
|
|
1196
|
+
balance: chBalance.toString(),
|
|
1197
|
+
totalClaimed: chTotalClaimed.toString(),
|
|
1198
|
+
withdrawRequestedAt: Number(wdInitiatedAt),
|
|
1199
|
+
refundNonce: refundNonceVal.toString()
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
async function verifySharedDepositState(signer, payload, requirements) {
|
|
1204
|
+
const { deposit, voucher } = payload;
|
|
1205
|
+
const config = payload.channelConfig;
|
|
1206
|
+
const payer = config.payer;
|
|
1207
|
+
const chainId = getEvmChainId(requirements.network);
|
|
1208
|
+
const configErr = validateChannelConfig(config, voucher.channelId, requirements);
|
|
1209
|
+
if (configErr) {
|
|
1210
|
+
return { ok: false, response: { isValid: false, invalidReason: configErr, payer } };
|
|
1211
|
+
}
|
|
1212
|
+
const voucherOk = await verifyBatchSettlementVoucherTypedData(
|
|
1213
|
+
signer,
|
|
1214
|
+
{
|
|
1215
|
+
channelId: voucher.channelId,
|
|
1216
|
+
maxClaimableAmount: voucher.maxClaimableAmount,
|
|
1217
|
+
payerAuthorizer: config.payerAuthorizer,
|
|
1218
|
+
payer: config.payer,
|
|
1219
|
+
signature: voucher.signature
|
|
1220
|
+
},
|
|
1221
|
+
chainId
|
|
1222
|
+
);
|
|
1223
|
+
if (!voucherOk) {
|
|
1224
|
+
return {
|
|
1225
|
+
ok: false,
|
|
1226
|
+
response: { isValid: false, invalidReason: ErrInvalidVoucherSignature, payer }
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
const mcResults = await multicall(signer.readContract.bind(signer), [
|
|
1230
|
+
{
|
|
1231
|
+
address: (0, import_viem11.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1232
|
+
abi: batchSettlementABI,
|
|
1233
|
+
functionName: "channels",
|
|
1234
|
+
args: [voucher.channelId]
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
address: (0, import_viem11.getAddress)(requirements.asset),
|
|
1238
|
+
abi: erc20BalanceOfABI,
|
|
1239
|
+
functionName: "balanceOf",
|
|
1240
|
+
args: [(0, import_viem11.getAddress)(payer)]
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
address: (0, import_viem11.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1244
|
+
abi: batchSettlementABI,
|
|
1245
|
+
functionName: "pendingWithdrawals",
|
|
1246
|
+
args: [voucher.channelId]
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
address: (0, import_viem11.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1250
|
+
abi: batchSettlementABI,
|
|
1251
|
+
functionName: "refundNonce",
|
|
1252
|
+
args: [voucher.channelId]
|
|
1253
|
+
}
|
|
1254
|
+
]);
|
|
1255
|
+
const [chRes, balRes, wdRes, rnRes] = mcResults;
|
|
1256
|
+
if (chRes.status === "failure" || balRes.status === "failure" || wdRes.status === "failure" || rnRes.status === "failure") {
|
|
1257
|
+
return {
|
|
1258
|
+
ok: false,
|
|
1259
|
+
response: { isValid: false, invalidReason: ErrRpcReadFailed, payer }
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
const [chBalance, chTotalClaimed] = chRes.result;
|
|
1263
|
+
const payerBalance = balRes.result;
|
|
1264
|
+
const [, wdInitiatedAt] = wdRes.result;
|
|
1265
|
+
const refundNonceVal = rnRes.result;
|
|
1266
|
+
const depositAmount = BigInt(deposit.amount);
|
|
1267
|
+
if (payerBalance < depositAmount) {
|
|
1268
|
+
return {
|
|
1269
|
+
ok: false,
|
|
1270
|
+
response: { isValid: false, invalidReason: ErrInsufficientBalance, payer }
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
const effectiveBalance = chBalance + depositAmount;
|
|
1274
|
+
const maxClaimableAmount = BigInt(voucher.maxClaimableAmount);
|
|
1275
|
+
if (maxClaimableAmount > effectiveBalance) {
|
|
1276
|
+
return {
|
|
1277
|
+
ok: false,
|
|
1278
|
+
response: { isValid: false, invalidReason: ErrCumulativeExceedsBalance, payer }
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
if (maxClaimableAmount <= chTotalClaimed) {
|
|
1282
|
+
return {
|
|
1283
|
+
ok: false,
|
|
1284
|
+
response: { isValid: false, invalidReason: ErrCumulativeAmountBelowClaimed, payer }
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
return {
|
|
1288
|
+
ok: true,
|
|
1289
|
+
chainId,
|
|
1290
|
+
depositAmount,
|
|
1291
|
+
payer,
|
|
1292
|
+
chBalance,
|
|
1293
|
+
chTotalClaimed,
|
|
1294
|
+
wdInitiatedAt,
|
|
1295
|
+
refundNonceVal
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
async function settleDeposit(signer, payment, payload, requirements, context) {
|
|
1299
|
+
const { deposit, voucher } = payload;
|
|
1300
|
+
const config = payload.channelConfig;
|
|
1301
|
+
const payer = config.payer;
|
|
1302
|
+
const verified = await verifyDeposit(signer, payment, payload, requirements, context);
|
|
1303
|
+
if (!verified.isValid) {
|
|
1304
|
+
const reason = verified.invalidReason ?? ErrInvalidPayloadType;
|
|
1305
|
+
return {
|
|
1306
|
+
success: false,
|
|
1307
|
+
errorReason: reason,
|
|
1308
|
+
errorMessage: verified.invalidMessage ?? reason,
|
|
1309
|
+
transaction: "",
|
|
1310
|
+
network: requirements.network,
|
|
1311
|
+
payer: verified.payer
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
const execution = await resolveDepositExecution(
|
|
1316
|
+
signer,
|
|
1317
|
+
payment,
|
|
1318
|
+
payload,
|
|
1319
|
+
requirements,
|
|
1320
|
+
context
|
|
1321
|
+
);
|
|
1322
|
+
if ("isValid" in execution) {
|
|
1323
|
+
const reason = execution.invalidReason ?? ErrInvalidPayloadType;
|
|
1324
|
+
return {
|
|
1325
|
+
success: false,
|
|
1326
|
+
errorReason: reason,
|
|
1327
|
+
errorMessage: execution.invalidMessage ?? reason,
|
|
1328
|
+
transaction: "",
|
|
1329
|
+
network: requirements.network,
|
|
1330
|
+
payer: execution.payer
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
const depositTx = buildDepositTransaction(payload, execution.collectorData);
|
|
1334
|
+
const tx = execution.kind === "erc20Approval" ? (await execution.extensionSigner.sendTransactions([
|
|
1335
|
+
execution.signedTransaction,
|
|
1336
|
+
depositTx
|
|
1337
|
+
]))[1] : await signer.writeContract({
|
|
1338
|
+
address: (0, import_viem11.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1339
|
+
abi: batchSettlementABI,
|
|
1340
|
+
functionName: "deposit",
|
|
1341
|
+
args: [
|
|
1342
|
+
toContractChannelConfig(config),
|
|
1343
|
+
BigInt(deposit.amount),
|
|
1344
|
+
execution.collector,
|
|
1345
|
+
execution.collectorData
|
|
1346
|
+
]
|
|
1347
|
+
});
|
|
1348
|
+
const receipt = await signer.waitForTransactionReceipt({ hash: tx });
|
|
1349
|
+
if (receipt.status !== "success") {
|
|
1350
|
+
return {
|
|
1351
|
+
success: false,
|
|
1352
|
+
errorReason: ErrDepositTransactionFailed,
|
|
1353
|
+
errorMessage: `transaction reverted (receipt status ${receipt.status})`,
|
|
1354
|
+
transaction: tx,
|
|
1355
|
+
network: requirements.network,
|
|
1356
|
+
payer
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
const optimisticExtra = {
|
|
1360
|
+
channelState: {
|
|
1361
|
+
channelId: voucher.channelId,
|
|
1362
|
+
balance: (BigInt(String(verified.extra?.balance ?? "0")) + BigInt(deposit.amount)).toString(),
|
|
1363
|
+
totalClaimed: String(verified.extra?.totalClaimed ?? "0"),
|
|
1364
|
+
withdrawRequestedAt: Number(verified.extra?.withdrawRequestedAt ?? 0),
|
|
1365
|
+
refundNonce: String(verified.extra?.refundNonce ?? "0")
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
const expectedMinBalance = BigInt(optimisticExtra.channelState.balance);
|
|
1369
|
+
const rpcDeadline = Date.now() + 2e3;
|
|
1370
|
+
let postState = await readChannelState(signer, voucher.channelId);
|
|
1371
|
+
while (postState.balance < expectedMinBalance && Date.now() < rpcDeadline) {
|
|
1372
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
1373
|
+
postState = await readChannelState(signer, voucher.channelId);
|
|
1374
|
+
}
|
|
1375
|
+
const rpcCaughtUp = postState.balance >= expectedMinBalance;
|
|
1376
|
+
return {
|
|
1377
|
+
success: true,
|
|
1378
|
+
transaction: tx,
|
|
1379
|
+
network: requirements.network,
|
|
1380
|
+
payer,
|
|
1381
|
+
amount: deposit.amount,
|
|
1382
|
+
extra: rpcCaughtUp ? {
|
|
1383
|
+
...optimisticExtra,
|
|
1384
|
+
channelState: {
|
|
1385
|
+
channelId: voucher.channelId,
|
|
1386
|
+
balance: postState.balance.toString(),
|
|
1387
|
+
totalClaimed: postState.totalClaimed.toString(),
|
|
1388
|
+
withdrawRequestedAt: postState.withdrawRequestedAt,
|
|
1389
|
+
refundNonce: postState.refundNonce.toString()
|
|
1390
|
+
}
|
|
1391
|
+
} : optimisticExtra
|
|
1392
|
+
};
|
|
1393
|
+
} catch (e) {
|
|
1394
|
+
return {
|
|
1395
|
+
success: false,
|
|
1396
|
+
errorReason: ErrDepositTransactionFailed,
|
|
1397
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1398
|
+
transaction: "",
|
|
1399
|
+
network: requirements.network,
|
|
1400
|
+
payer
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
async function resolveDepositExecution(signer, payment, payload, requirements, context) {
|
|
1405
|
+
const transferMethod = resolveDepositTransferMethod(payload, requirements);
|
|
1406
|
+
if (transferMethod === "eip3009") {
|
|
1407
|
+
return {
|
|
1408
|
+
kind: "direct",
|
|
1409
|
+
collector: getEip3009DepositCollectorAddress(),
|
|
1410
|
+
collectorData: buildEip3009DepositCollectorData(payload)
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
const branch = await resolvePermit2DepositBranch(signer, payment, payload, requirements, context);
|
|
1414
|
+
if ("isValid" in branch) {
|
|
1415
|
+
return branch;
|
|
1416
|
+
}
|
|
1417
|
+
if (branch.kind === "erc20Approval") {
|
|
1418
|
+
return {
|
|
1419
|
+
kind: "erc20Approval",
|
|
1420
|
+
collector: getPermit2DepositCollectorAddress(),
|
|
1421
|
+
collectorData: branch.collectorData,
|
|
1422
|
+
signedTransaction: branch.signedTransaction,
|
|
1423
|
+
extensionSigner: branch.extensionSigner,
|
|
1424
|
+
skipDirectSimulation: true
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
return {
|
|
1428
|
+
kind: "direct",
|
|
1429
|
+
collector: getPermit2DepositCollectorAddress(),
|
|
1430
|
+
collectorData: branch.collectorData
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
function resolveDepositTransferMethod(payload, requirements) {
|
|
1434
|
+
const hinted = requirements.extra?.assetTransferMethod;
|
|
1435
|
+
if (hinted) {
|
|
1436
|
+
return hinted;
|
|
1437
|
+
}
|
|
1438
|
+
return payload.deposit.authorization.permit2Authorization ? "permit2" : "eip3009";
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// src/batch-settlement/facilitator/voucher.ts
|
|
1442
|
+
async function verifyVoucher(signer, payload, requirements, channelConfig) {
|
|
1443
|
+
const { voucher } = payload;
|
|
1444
|
+
const channelId = voucher.channelId;
|
|
1445
|
+
const chainId = getEvmChainId(requirements.network);
|
|
1446
|
+
const configErr = validateChannelConfig(channelConfig, channelId, requirements);
|
|
1447
|
+
if (configErr) {
|
|
1448
|
+
return { isValid: false, invalidReason: configErr, payer: channelConfig.payer };
|
|
1449
|
+
}
|
|
1450
|
+
const voucherOk = await verifyBatchSettlementVoucherTypedData(
|
|
1451
|
+
signer,
|
|
1452
|
+
{
|
|
1453
|
+
channelId,
|
|
1454
|
+
maxClaimableAmount: voucher.maxClaimableAmount,
|
|
1455
|
+
payerAuthorizer: channelConfig.payerAuthorizer,
|
|
1456
|
+
payer: channelConfig.payer,
|
|
1457
|
+
signature: voucher.signature
|
|
1458
|
+
},
|
|
1459
|
+
chainId
|
|
1460
|
+
);
|
|
1461
|
+
if (!voucherOk) {
|
|
1462
|
+
return {
|
|
1463
|
+
isValid: false,
|
|
1464
|
+
invalidReason: ErrInvalidVoucherSignature,
|
|
1465
|
+
payer: channelConfig.payer
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
const state = await readChannelState(signer, channelId);
|
|
1469
|
+
if (state.balance === 0n) {
|
|
1470
|
+
return { isValid: false, invalidReason: ErrChannelNotFound, payer: channelConfig.payer };
|
|
1471
|
+
}
|
|
1472
|
+
const maxClaimableAmount = BigInt(voucher.maxClaimableAmount);
|
|
1473
|
+
if (maxClaimableAmount > state.balance) {
|
|
1474
|
+
return {
|
|
1475
|
+
isValid: false,
|
|
1476
|
+
invalidReason: ErrCumulativeExceedsBalance,
|
|
1477
|
+
payer: channelConfig.payer
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
const belowClaimed = payload.type === "refund" ? maxClaimableAmount < state.totalClaimed : maxClaimableAmount <= state.totalClaimed;
|
|
1481
|
+
if (belowClaimed) {
|
|
1482
|
+
return {
|
|
1483
|
+
isValid: false,
|
|
1484
|
+
invalidReason: ErrCumulativeAmountBelowClaimed,
|
|
1485
|
+
payer: channelConfig.payer
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
return {
|
|
1489
|
+
isValid: true,
|
|
1490
|
+
payer: channelConfig.payer,
|
|
1491
|
+
extra: {
|
|
1492
|
+
channelId,
|
|
1493
|
+
balance: state.balance.toString(),
|
|
1494
|
+
totalClaimed: state.totalClaimed.toString(),
|
|
1495
|
+
withdrawRequestedAt: state.withdrawRequestedAt,
|
|
1496
|
+
refundNonce: state.refundNonce.toString()
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// src/batch-settlement/facilitator/claim.ts
|
|
1502
|
+
var import_viem12 = require("viem");
|
|
1503
|
+
|
|
1504
|
+
// src/batch-settlement/authorizerSigner.ts
|
|
1505
|
+
async function signClaimBatch(signer, claims, network) {
|
|
1506
|
+
const chainId = getEvmChainId(network);
|
|
1507
|
+
const claimEntries = claims.map((c) => ({
|
|
1508
|
+
channelId: computeChannelId(c.voucher.channel, chainId),
|
|
1509
|
+
maxClaimableAmount: BigInt(c.voucher.maxClaimableAmount),
|
|
1510
|
+
totalClaimed: BigInt(c.totalClaimed)
|
|
1511
|
+
}));
|
|
1512
|
+
return signer.signTypedData({
|
|
1513
|
+
domain: getBatchSettlementEip712Domain(chainId),
|
|
1514
|
+
types: claimBatchTypes,
|
|
1515
|
+
primaryType: "ClaimBatch",
|
|
1516
|
+
message: {
|
|
1517
|
+
claims: claimEntries
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
async function signRefund(signer, channelId, amount, nonce, network) {
|
|
1522
|
+
const chainId = getEvmChainId(network);
|
|
1523
|
+
return signer.signTypedData({
|
|
1524
|
+
domain: getBatchSettlementEip712Domain(chainId),
|
|
1525
|
+
types: refundTypes,
|
|
1526
|
+
primaryType: "Refund",
|
|
1527
|
+
message: {
|
|
1528
|
+
channelId,
|
|
1529
|
+
nonce: BigInt(nonce),
|
|
1530
|
+
amount: BigInt(amount)
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// src/batch-settlement/facilitator/claim.ts
|
|
1536
|
+
function buildVoucherClaimArgs(claims) {
|
|
1537
|
+
return claims.map((c) => ({
|
|
1538
|
+
voucher: {
|
|
1539
|
+
channel: toContractChannelConfig(c.voucher.channel),
|
|
1540
|
+
maxClaimableAmount: BigInt(c.voucher.maxClaimableAmount)
|
|
1541
|
+
},
|
|
1542
|
+
signature: c.signature,
|
|
1543
|
+
totalClaimed: BigInt(c.totalClaimed)
|
|
1544
|
+
}));
|
|
1545
|
+
}
|
|
1546
|
+
async function executeClaimWithSignature(signer, payload, requirements, authorizerSigner) {
|
|
1547
|
+
const network = requirements.network;
|
|
1548
|
+
const claimArgs = buildVoucherClaimArgs(payload.claims);
|
|
1549
|
+
let sig = payload.claimAuthorizerSignature;
|
|
1550
|
+
if (!sig) {
|
|
1551
|
+
for (const claim of payload.claims) {
|
|
1552
|
+
if ((0, import_viem12.getAddress)(claim.voucher.channel.receiverAuthorizer) !== (0, import_viem12.getAddress)(authorizerSigner.address)) {
|
|
1553
|
+
return {
|
|
1554
|
+
success: false,
|
|
1555
|
+
errorReason: ErrAuthorizerAddressMismatch,
|
|
1556
|
+
transaction: "",
|
|
1557
|
+
network
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
sig = await signClaimBatch(authorizerSigner, payload.claims, network);
|
|
1562
|
+
}
|
|
1563
|
+
try {
|
|
1564
|
+
await signer.readContract({
|
|
1565
|
+
address: (0, import_viem12.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1566
|
+
abi: batchSettlementABI,
|
|
1567
|
+
functionName: "claimWithSignature",
|
|
1568
|
+
args: [claimArgs, sig]
|
|
1569
|
+
});
|
|
1570
|
+
} catch (e) {
|
|
1571
|
+
return {
|
|
1572
|
+
success: false,
|
|
1573
|
+
errorReason: ErrClaimSimulationFailed,
|
|
1574
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1575
|
+
transaction: "",
|
|
1576
|
+
network
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
const tx = await signer.writeContract({
|
|
1581
|
+
address: (0, import_viem12.getAddress)(BATCH_SETTLEMENT_ADDRESS),
|
|
1582
|
+
abi: batchSettlementABI,
|
|
1583
|
+
functionName: "claimWithSignature",
|
|
1584
|
+
args: [claimArgs, sig]
|
|
1585
|
+
});
|
|
1586
|
+
const receipt = await signer.waitForTransactionReceipt({ hash: tx });
|
|
1587
|
+
if (receipt.status !== "success") {
|
|
1588
|
+
return {
|
|
1589
|
+
success: false,
|
|
1590
|
+
errorReason: ErrClaimTransactionFailed,
|
|
1591
|
+
errorMessage: `transaction reverted (receipt status ${receipt.status})`,
|
|
1592
|
+
transaction: tx,
|
|
1593
|
+
network
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
success: true,
|
|
1598
|
+
transaction: tx,
|
|
1599
|
+
network,
|
|
1600
|
+
amount: ""
|
|
1601
|
+
};
|
|
1602
|
+
} catch (e) {
|
|
1603
|
+
return {
|
|
1604
|
+
success: false,
|
|
1605
|
+
errorReason: ErrClaimTransactionFailed,
|
|
1606
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1607
|
+
transaction: "",
|
|
1608
|
+
network
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// src/batch-settlement/facilitator/settle.ts
|
|
1614
|
+
var import_viem13 = require("viem");
|
|
1615
|
+
async function executeSettle(signer, payload, requirements) {
|
|
1616
|
+
const network = requirements.network;
|
|
1617
|
+
const contractAddr = (0, import_viem13.getAddress)(BATCH_SETTLEMENT_ADDRESS);
|
|
1618
|
+
const receiver = (0, import_viem13.getAddress)(payload.receiver);
|
|
1619
|
+
const token = (0, import_viem13.getAddress)(payload.token);
|
|
1620
|
+
try {
|
|
1621
|
+
const [totalClaimed, totalSettled] = await signer.readContract({
|
|
1622
|
+
address: contractAddr,
|
|
1623
|
+
abi: batchSettlementABI,
|
|
1624
|
+
functionName: "receivers",
|
|
1625
|
+
args: [receiver, token]
|
|
1626
|
+
});
|
|
1627
|
+
if (totalClaimed <= totalSettled) {
|
|
1628
|
+
return {
|
|
1629
|
+
success: false,
|
|
1630
|
+
errorReason: ErrNothingToSettle,
|
|
1631
|
+
errorMessage: "nothing to settle for receiver and token",
|
|
1632
|
+
transaction: "",
|
|
1633
|
+
network
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
} catch (e) {
|
|
1637
|
+
return {
|
|
1638
|
+
success: false,
|
|
1639
|
+
errorReason: ErrRpcReadFailed,
|
|
1640
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1641
|
+
transaction: "",
|
|
1642
|
+
network
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
try {
|
|
1646
|
+
await signer.readContract({
|
|
1647
|
+
address: contractAddr,
|
|
1648
|
+
abi: batchSettlementABI,
|
|
1649
|
+
functionName: "settle",
|
|
1650
|
+
args: [receiver, token]
|
|
1651
|
+
});
|
|
1652
|
+
} catch (e) {
|
|
1653
|
+
return {
|
|
1654
|
+
success: false,
|
|
1655
|
+
errorReason: ErrSettleSimulationFailed,
|
|
1656
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1657
|
+
transaction: "",
|
|
1658
|
+
network
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
try {
|
|
1662
|
+
const tx = await signer.writeContract({
|
|
1663
|
+
address: contractAddr,
|
|
1664
|
+
abi: batchSettlementABI,
|
|
1665
|
+
functionName: "settle",
|
|
1666
|
+
args: [receiver, token]
|
|
1667
|
+
});
|
|
1668
|
+
const receipt = await signer.waitForTransactionReceipt({ hash: tx });
|
|
1669
|
+
if (receipt.status !== "success") {
|
|
1670
|
+
return {
|
|
1671
|
+
success: false,
|
|
1672
|
+
errorReason: ErrSettleTransactionFailed,
|
|
1673
|
+
errorMessage: `transaction reverted (receipt status ${receipt.status})`,
|
|
1674
|
+
transaction: tx,
|
|
1675
|
+
network
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
let amount = "";
|
|
1679
|
+
if (receipt.logs) {
|
|
1680
|
+
const logs = (0, import_viem13.parseEventLogs)({
|
|
1681
|
+
abi: batchSettlementABI,
|
|
1682
|
+
eventName: "Settled",
|
|
1683
|
+
logs: receipt.logs.filter((log) => (0, import_viem13.isAddressEqual)(log.address, contractAddr))
|
|
1684
|
+
});
|
|
1685
|
+
const settledLog = logs.find(
|
|
1686
|
+
(log) => (0, import_viem13.isAddressEqual)(log.args.receiver, receiver) && (0, import_viem13.isAddressEqual)(log.args.token, token)
|
|
1687
|
+
);
|
|
1688
|
+
amount = settledLog?.args.amount.toString() ?? "0";
|
|
1689
|
+
}
|
|
1690
|
+
return {
|
|
1691
|
+
success: true,
|
|
1692
|
+
transaction: tx,
|
|
1693
|
+
network,
|
|
1694
|
+
amount
|
|
1695
|
+
};
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
return {
|
|
1698
|
+
success: false,
|
|
1699
|
+
errorReason: ErrSettleTransactionFailed,
|
|
1700
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1701
|
+
transaction: "",
|
|
1702
|
+
network
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
// src/batch-settlement/facilitator/refund.ts
|
|
1708
|
+
var import_viem14 = require("viem");
|
|
1709
|
+
var REFUND_STATE_POLL_MS = 2e3;
|
|
1710
|
+
var REFUND_STATE_POLL_INTERVAL_MS = 150;
|
|
1711
|
+
function getRefundableAmount(payload, preState, channelId, network) {
|
|
1712
|
+
const postClaimTotalClaimed = payload.claims.reduce((max, claim) => {
|
|
1713
|
+
const claimChannelId = computeChannelId(claim.voucher.channel, network);
|
|
1714
|
+
if (claimChannelId.toLowerCase() !== channelId.toLowerCase()) {
|
|
1715
|
+
return max;
|
|
1716
|
+
}
|
|
1717
|
+
const totalClaimed = BigInt(claim.totalClaimed);
|
|
1718
|
+
return totalClaimed > max ? totalClaimed : max;
|
|
1719
|
+
}, preState.totalClaimed);
|
|
1720
|
+
if (postClaimTotalClaimed > preState.balance) {
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
const requestedAmount = BigInt(payload.amount);
|
|
1724
|
+
if (requestedAmount === 0n) {
|
|
1725
|
+
return null;
|
|
1726
|
+
}
|
|
1727
|
+
const available = preState.balance - postClaimTotalClaimed;
|
|
1728
|
+
return requestedAmount > available ? available : requestedAmount;
|
|
1729
|
+
}
|
|
1730
|
+
function buildRefundExtra(payload, channelId, preState) {
|
|
1731
|
+
const preTotalClaimed = preState?.totalClaimed ?? 0n;
|
|
1732
|
+
const preBalance = preState?.balance ?? 0n;
|
|
1733
|
+
const lastClaimTotal = payload.claims.length > 0 ? BigInt(payload.claims[payload.claims.length - 1].totalClaimed) : preTotalClaimed;
|
|
1734
|
+
const postClaimTotalClaimed = lastClaimTotal > preTotalClaimed ? lastClaimTotal : preTotalClaimed;
|
|
1735
|
+
const available = preBalance - postClaimTotalClaimed;
|
|
1736
|
+
const requestedAmount = BigInt(payload.amount);
|
|
1737
|
+
const actualRefund = requestedAmount > available ? available : requestedAmount;
|
|
1738
|
+
return {
|
|
1739
|
+
amount: actualRefund.toString(),
|
|
1740
|
+
extra: {
|
|
1741
|
+
channelState: {
|
|
1742
|
+
channelId,
|
|
1743
|
+
balance: (preBalance - actualRefund).toString(),
|
|
1744
|
+
totalClaimed: postClaimTotalClaimed.toString(),
|
|
1745
|
+
withdrawRequestedAt: 0,
|
|
1746
|
+
refundNonce: String((preState?.refundNonce ?? 0n) + 1n)
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
async function readPostRefundState(signer, channelId, submittedNonce) {
|
|
1752
|
+
const expectedNonce = BigInt(submittedNonce) + 1n;
|
|
1753
|
+
const deadline = Date.now() + REFUND_STATE_POLL_MS;
|
|
1754
|
+
do {
|
|
1755
|
+
let state;
|
|
1756
|
+
try {
|
|
1757
|
+
state = await readChannelState(signer, channelId);
|
|
1758
|
+
} catch {
|
|
1759
|
+
return null;
|
|
1760
|
+
}
|
|
1761
|
+
if (state.refundNonce >= expectedNonce) {
|
|
1762
|
+
return state;
|
|
1763
|
+
}
|
|
1764
|
+
await new Promise((resolve) => setTimeout(resolve, REFUND_STATE_POLL_INTERVAL_MS));
|
|
1765
|
+
} while (Date.now() < deadline);
|
|
1766
|
+
return null;
|
|
1767
|
+
}
|
|
1768
|
+
function buildRefundExtraFromPostState(channelId, preState, postState) {
|
|
1769
|
+
const actualRefund = preState.balance > postState.balance ? preState.balance - postState.balance : 0n;
|
|
1770
|
+
return {
|
|
1771
|
+
amount: actualRefund.toString(),
|
|
1772
|
+
extra: {
|
|
1773
|
+
channelState: {
|
|
1774
|
+
channelId,
|
|
1775
|
+
balance: postState.balance.toString(),
|
|
1776
|
+
totalClaimed: postState.totalClaimed.toString(),
|
|
1777
|
+
withdrawRequestedAt: postState.withdrawRequestedAt,
|
|
1778
|
+
refundNonce: postState.refundNonce.toString()
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
async function executeRefundWithSignature(signer, payload, requirements, authorizerSigner) {
|
|
1784
|
+
const network = requirements.network;
|
|
1785
|
+
try {
|
|
1786
|
+
const channelId = computeChannelId(payload.channelConfig, network);
|
|
1787
|
+
const preState = await readChannelState(signer, channelId);
|
|
1788
|
+
const contractAddr = (0, import_viem14.getAddress)(BATCH_SETTLEMENT_ADDRESS);
|
|
1789
|
+
const refundableAmount = getRefundableAmount(payload, preState, channelId, network);
|
|
1790
|
+
if (refundableAmount === 0n) {
|
|
1791
|
+
return {
|
|
1792
|
+
success: false,
|
|
1793
|
+
errorReason: ErrRefundNoBalance,
|
|
1794
|
+
errorMessage: "Nothing to refund",
|
|
1795
|
+
transaction: "",
|
|
1796
|
+
network
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
const hasClientSig = payload.refundAuthorizerSignature !== void 0;
|
|
1800
|
+
const authorizerMismatch = (0, import_viem14.getAddress)(payload.channelConfig.receiverAuthorizer) !== (0, import_viem14.getAddress)(authorizerSigner.address);
|
|
1801
|
+
if (!hasClientSig && authorizerMismatch) {
|
|
1802
|
+
return {
|
|
1803
|
+
success: false,
|
|
1804
|
+
errorReason: ErrAuthorizerAddressMismatch,
|
|
1805
|
+
transaction: "",
|
|
1806
|
+
network
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
const refundSig = payload.refundAuthorizerSignature ?? await signRefund(authorizerSigner, channelId, payload.amount, payload.refundNonce, network);
|
|
1810
|
+
const refundCalldata = (0, import_viem14.encodeFunctionData)({
|
|
1811
|
+
abi: batchSettlementABI,
|
|
1812
|
+
functionName: "refundWithSignature",
|
|
1813
|
+
args: [
|
|
1814
|
+
toContractChannelConfig(payload.channelConfig),
|
|
1815
|
+
BigInt(payload.amount),
|
|
1816
|
+
BigInt(payload.refundNonce),
|
|
1817
|
+
refundSig
|
|
1818
|
+
]
|
|
1819
|
+
});
|
|
1820
|
+
let tx;
|
|
1821
|
+
if (payload.claims.length > 0) {
|
|
1822
|
+
let claimSig = payload.claimAuthorizerSignature;
|
|
1823
|
+
if (!claimSig) {
|
|
1824
|
+
claimSig = await signClaimBatch(authorizerSigner, payload.claims, network);
|
|
1825
|
+
}
|
|
1826
|
+
const claimCalldata = (0, import_viem14.encodeFunctionData)({
|
|
1827
|
+
abi: batchSettlementABI,
|
|
1828
|
+
functionName: "claimWithSignature",
|
|
1829
|
+
args: [buildVoucherClaimArgs(payload.claims), claimSig]
|
|
1830
|
+
});
|
|
1831
|
+
try {
|
|
1832
|
+
await signer.readContract({
|
|
1833
|
+
address: contractAddr,
|
|
1834
|
+
abi: batchSettlementABI,
|
|
1835
|
+
functionName: "multicall",
|
|
1836
|
+
args: [[claimCalldata, refundCalldata]]
|
|
1837
|
+
});
|
|
1838
|
+
} catch (e) {
|
|
1839
|
+
return {
|
|
1840
|
+
success: false,
|
|
1841
|
+
errorReason: ErrRefundSimulationFailed,
|
|
1842
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1843
|
+
transaction: "",
|
|
1844
|
+
network
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
tx = await signer.writeContract({
|
|
1848
|
+
address: contractAddr,
|
|
1849
|
+
abi: batchSettlementABI,
|
|
1850
|
+
functionName: "multicall",
|
|
1851
|
+
args: [[claimCalldata, refundCalldata]]
|
|
1852
|
+
});
|
|
1853
|
+
} else {
|
|
1854
|
+
try {
|
|
1855
|
+
await signer.readContract({
|
|
1856
|
+
address: contractAddr,
|
|
1857
|
+
abi: batchSettlementABI,
|
|
1858
|
+
functionName: "refundWithSignature",
|
|
1859
|
+
args: [
|
|
1860
|
+
toContractChannelConfig(payload.channelConfig),
|
|
1861
|
+
BigInt(payload.amount),
|
|
1862
|
+
BigInt(payload.refundNonce),
|
|
1863
|
+
refundSig
|
|
1864
|
+
]
|
|
1865
|
+
});
|
|
1866
|
+
} catch (e) {
|
|
1867
|
+
return {
|
|
1868
|
+
success: false,
|
|
1869
|
+
errorReason: ErrRefundSimulationFailed,
|
|
1870
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1871
|
+
transaction: "",
|
|
1872
|
+
network
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
tx = await signer.writeContract({
|
|
1876
|
+
address: contractAddr,
|
|
1877
|
+
abi: batchSettlementABI,
|
|
1878
|
+
functionName: "refundWithSignature",
|
|
1879
|
+
args: [
|
|
1880
|
+
toContractChannelConfig(payload.channelConfig),
|
|
1881
|
+
BigInt(payload.amount),
|
|
1882
|
+
BigInt(payload.refundNonce),
|
|
1883
|
+
refundSig
|
|
1884
|
+
]
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
const receipt = await signer.waitForTransactionReceipt({ hash: tx });
|
|
1888
|
+
if (receipt.status !== "success") {
|
|
1889
|
+
return {
|
|
1890
|
+
success: false,
|
|
1891
|
+
errorReason: ErrRefundTransactionFailed,
|
|
1892
|
+
errorMessage: `transaction reverted (receipt status ${receipt.status})`,
|
|
1893
|
+
transaction: tx,
|
|
1894
|
+
network
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
const postState = preState && preState.withdrawRequestedAt !== 0 ? await readPostRefundState(signer, channelId, payload.refundNonce) : null;
|
|
1898
|
+
const refundDetails = preState && postState ? buildRefundExtraFromPostState(channelId, preState, postState) : buildRefundExtra(payload, channelId, preState);
|
|
1899
|
+
return {
|
|
1900
|
+
success: true,
|
|
1901
|
+
transaction: tx,
|
|
1902
|
+
network,
|
|
1903
|
+
payer: payload.channelConfig.payer,
|
|
1904
|
+
amount: refundDetails.amount,
|
|
1905
|
+
extra: refundDetails.extra
|
|
1906
|
+
};
|
|
1907
|
+
} catch (e) {
|
|
1908
|
+
return {
|
|
1909
|
+
success: false,
|
|
1910
|
+
errorReason: ErrRefundTransactionFailed,
|
|
1911
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
1912
|
+
transaction: "",
|
|
1913
|
+
network
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// src/batch-settlement/facilitator/scheme.ts
|
|
1919
|
+
var BatchSettlementEvmScheme = class {
|
|
1920
|
+
/**
|
|
1921
|
+
* Creates a facilitator scheme for verifying and settling batch-settlement payments.
|
|
1922
|
+
*
|
|
1923
|
+
* @param signer - Facilitator EVM signer(s) used for tx submission and onchain reads.
|
|
1924
|
+
* @param authorizerSigner - Dedicated key that provides EIP-712 signatures for
|
|
1925
|
+
* `claimWithSignature` / `refundWithSignature`. The facilitator will sign missing
|
|
1926
|
+
* authorizer signatures using this key when the server omits them.
|
|
1927
|
+
*/
|
|
1928
|
+
constructor(signer, authorizerSigner) {
|
|
1929
|
+
this.signer = signer;
|
|
1930
|
+
this.authorizerSigner = authorizerSigner;
|
|
1931
|
+
this.scheme = BATCH_SETTLEMENT_SCHEME;
|
|
1932
|
+
this.caipFamily = "eip155:*";
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Returns facilitator-specific extra fields to be merged into payment requirements.
|
|
1936
|
+
*
|
|
1937
|
+
* Exposes the configured `receiverAuthorizer` address so the server and client can
|
|
1938
|
+
* embed it in `ChannelConfig`.
|
|
1939
|
+
*
|
|
1940
|
+
* @param _ - Network identifier (unused).
|
|
1941
|
+
* @returns Extra fields containing `receiverAuthorizer`.
|
|
1942
|
+
*/
|
|
1943
|
+
getExtra(_) {
|
|
1944
|
+
return { receiverAuthorizer: this.authorizerSigner.address };
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Returns all facilitator signer addresses available for the given network.
|
|
1948
|
+
*
|
|
1949
|
+
* @param _ - Network identifier (unused).
|
|
1950
|
+
* @returns Array of hex addresses.
|
|
1951
|
+
*/
|
|
1952
|
+
getSigners(_) {
|
|
1953
|
+
return [...this.signer.getAddresses()];
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Verifies a payment payload (deposit or voucher) without executing settlement.
|
|
1957
|
+
*
|
|
1958
|
+
* @param payload - The x402 payment payload envelope.
|
|
1959
|
+
* @param requirements - Server payment requirements (scheme, network, asset, amount).
|
|
1960
|
+
* @param context - Optional facilitator extension context.
|
|
1961
|
+
* @returns A {@link VerifyResponse} indicating validity with payer and channel state in `extra`.
|
|
1962
|
+
*/
|
|
1963
|
+
async verify(payload, requirements, context) {
|
|
1964
|
+
const rawPayload = payload.payload;
|
|
1965
|
+
if (payload.accepted.scheme !== BATCH_SETTLEMENT_SCHEME || requirements.scheme !== BATCH_SETTLEMENT_SCHEME) {
|
|
1966
|
+
return { isValid: false, invalidReason: ErrInvalidScheme };
|
|
1967
|
+
}
|
|
1968
|
+
if (payload.accepted.network !== requirements.network) {
|
|
1969
|
+
return { isValid: false, invalidReason: ErrNetworkMismatch };
|
|
1970
|
+
}
|
|
1971
|
+
if (isBatchSettlementDepositPayload(rawPayload)) {
|
|
1972
|
+
return verifyDeposit(this.signer, payload, rawPayload, requirements, context);
|
|
1973
|
+
}
|
|
1974
|
+
if (isBatchSettlementVoucherPayload(rawPayload)) {
|
|
1975
|
+
return verifyVoucher(this.signer, rawPayload, requirements, rawPayload.channelConfig);
|
|
1976
|
+
}
|
|
1977
|
+
if (isBatchSettlementRefundPayload(rawPayload)) {
|
|
1978
|
+
return verifyVoucher(this.signer, rawPayload, requirements, rawPayload.channelConfig);
|
|
1979
|
+
}
|
|
1980
|
+
return { isValid: false, invalidReason: ErrInvalidPayloadType };
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Executes settlement for a payment payload.
|
|
1984
|
+
*
|
|
1985
|
+
* Dispatches to the correct handler based on payload settle action:
|
|
1986
|
+
* - `deposit` → onchain `deposit(config, amount, collector, collectorData)`
|
|
1987
|
+
* - `claim` → onchain `claimWithSignature(VoucherClaim[], bytes)`
|
|
1988
|
+
* - `settle` → onchain `settle(receiver, token)`
|
|
1989
|
+
* - `refund` → optional claim + onchain `refundWithSignature(config, amount, nonce, sig)`
|
|
1990
|
+
*
|
|
1991
|
+
* @param payload - The x402 payment payload envelope.
|
|
1992
|
+
* @param requirements - Server payment requirements.
|
|
1993
|
+
* @param context - Optional facilitator extension context.
|
|
1994
|
+
* @returns A {@link SettleResponse} with the transaction hash on success.
|
|
1995
|
+
*/
|
|
1996
|
+
async settle(payload, requirements, context) {
|
|
1997
|
+
const rawPayload = payload.payload;
|
|
1998
|
+
if (isBatchSettlementDepositPayload(rawPayload)) {
|
|
1999
|
+
return settleDeposit(this.signer, payload, rawPayload, requirements, context);
|
|
2000
|
+
}
|
|
2001
|
+
if (isBatchSettlementClaimPayload(rawPayload)) {
|
|
2002
|
+
return executeClaimWithSignature(
|
|
2003
|
+
this.signer,
|
|
2004
|
+
rawPayload,
|
|
2005
|
+
requirements,
|
|
2006
|
+
this.authorizerSigner
|
|
2007
|
+
);
|
|
2008
|
+
}
|
|
2009
|
+
if (isBatchSettlementEnrichedRefundPayload(rawPayload)) {
|
|
2010
|
+
return executeRefundWithSignature(
|
|
2011
|
+
this.signer,
|
|
2012
|
+
rawPayload,
|
|
2013
|
+
requirements,
|
|
2014
|
+
this.authorizerSigner
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
if (isBatchSettlementSettlePayload(rawPayload)) {
|
|
2018
|
+
return executeSettle(this.signer, rawPayload, requirements);
|
|
2019
|
+
}
|
|
2020
|
+
return {
|
|
2021
|
+
success: false,
|
|
2022
|
+
errorReason: ErrInvalidPayloadType,
|
|
2023
|
+
transaction: "",
|
|
2024
|
+
network: requirements.network
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2029
|
+
0 && (module.exports = {
|
|
2030
|
+
BatchSettlementEvmScheme
|
|
2031
|
+
});
|
|
2032
|
+
//# sourceMappingURL=index.js.map
|