@bankofai/x402-evm 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +172 -0
  2. package/dist/cjs/auth-capture/client/index.d.ts +44 -0
  3. package/dist/cjs/auth-capture/client/index.js +298 -0
  4. package/dist/cjs/auth-capture/client/index.js.map +1 -0
  5. package/dist/cjs/batch-settlement/client/file-storage.d.ts +47 -0
  6. package/dist/cjs/batch-settlement/client/file-storage.js +116 -0
  7. package/dist/cjs/batch-settlement/client/file-storage.js.map +1 -0
  8. package/dist/cjs/batch-settlement/client/index.d.ts +111 -0
  9. package/dist/cjs/batch-settlement/client/index.js +1565 -0
  10. package/dist/cjs/batch-settlement/client/index.js.map +1 -0
  11. package/dist/cjs/batch-settlement/facilitator/index.d.ts +72 -0
  12. package/dist/cjs/batch-settlement/facilitator/index.js +2102 -0
  13. package/dist/cjs/batch-settlement/facilitator/index.js.map +1 -0
  14. package/dist/cjs/batch-settlement/server/file-storage.d.ts +53 -0
  15. package/dist/cjs/batch-settlement/server/file-storage.js +181 -0
  16. package/dist/cjs/batch-settlement/server/file-storage.js.map +1 -0
  17. package/dist/cjs/batch-settlement/server/index.d.ts +491 -0
  18. package/dist/cjs/batch-settlement/server/index.js +1978 -0
  19. package/dist/cjs/batch-settlement/server/index.js.map +1 -0
  20. package/dist/cjs/batch-settlement/server/redis-storage.d.ts +87 -0
  21. package/dist/cjs/batch-settlement/server/redis-storage.js +181 -0
  22. package/dist/cjs/batch-settlement/server/redis-storage.js.map +1 -0
  23. package/dist/cjs/client/agent-wallet.d.ts +69 -0
  24. package/dist/cjs/client/agent-wallet.js +84 -0
  25. package/dist/cjs/client/agent-wallet.js.map +1 -0
  26. package/dist/cjs/exact/client/index.d.ts +63 -0
  27. package/dist/cjs/exact/client/index.js +739 -0
  28. package/dist/cjs/exact/client/index.js.map +1 -0
  29. package/dist/cjs/exact/facilitator/index.d.ts +141 -0
  30. package/dist/cjs/exact/facilitator/index.js +1989 -0
  31. package/dist/cjs/exact/facilitator/index.js.map +1 -0
  32. package/dist/cjs/exact/server/index.d.ts +118 -0
  33. package/dist/cjs/exact/server/index.js +326 -0
  34. package/dist/cjs/exact/server/index.js.map +1 -0
  35. package/dist/cjs/exact/v1/client/index.d.ts +38 -0
  36. package/dist/cjs/exact/v1/client/index.js +193 -0
  37. package/dist/cjs/exact/v1/client/index.js.map +1 -0
  38. package/dist/cjs/exact/v1/facilitator/index.d.ts +84 -0
  39. package/dist/cjs/exact/v1/facilitator/index.js +739 -0
  40. package/dist/cjs/exact/v1/facilitator/index.js.map +1 -0
  41. package/dist/cjs/facilitator/agent-wallet.d.ts +109 -0
  42. package/dist/cjs/facilitator/agent-wallet.js +105 -0
  43. package/dist/cjs/facilitator/agent-wallet.js.map +1 -0
  44. package/dist/cjs/index.d.ts +338 -0
  45. package/dist/cjs/index.js +2860 -0
  46. package/dist/cjs/index.js.map +1 -0
  47. package/dist/cjs/permit2-DK5A8alk.d.ts +729 -0
  48. package/dist/cjs/permit2-DhJRUcgY.d.ts +729 -0
  49. package/dist/cjs/rpc-DULZzRne.d.ts +13 -0
  50. package/dist/cjs/scheme-7ehldYoO.d.ts +307 -0
  51. package/dist/cjs/scheme-BjBJzHF7.d.ts +307 -0
  52. package/dist/cjs/scheme-DWgpkDgz.d.ts +47 -0
  53. package/dist/cjs/signer-BFelv8DL.d.ts +170 -0
  54. package/dist/cjs/storage-6W5MO46W.d.ts +50 -0
  55. package/dist/cjs/storage-CHNote8s.d.ts +81 -0
  56. package/dist/cjs/storage-DjCv5IPh.d.ts +81 -0
  57. package/dist/cjs/types-CKd3Xoi1.d.ts +180 -0
  58. package/dist/cjs/types-DIt9uAUy.d.ts +180 -0
  59. package/dist/cjs/upto/client/index.d.ts +34 -0
  60. package/dist/cjs/upto/client/index.js +509 -0
  61. package/dist/cjs/upto/client/index.js.map +1 -0
  62. package/dist/cjs/upto/facilitator/index.d.ts +54 -0
  63. package/dist/cjs/upto/facilitator/index.js +1313 -0
  64. package/dist/cjs/upto/facilitator/index.js.map +1 -0
  65. package/dist/cjs/upto/server/index.d.ts +69 -0
  66. package/dist/cjs/upto/server/index.js +296 -0
  67. package/dist/cjs/upto/server/index.js.map +1 -0
  68. package/dist/cjs/v1/index.d.ts +40 -0
  69. package/dist/cjs/v1/index.js +199 -0
  70. package/dist/cjs/v1/index.js.map +1 -0
  71. package/dist/esm/auth-capture/client/index.d.mts +44 -0
  72. package/dist/esm/auth-capture/client/index.mjs +8 -0
  73. package/dist/esm/auth-capture/client/index.mjs.map +1 -0
  74. package/dist/esm/batch-settlement/client/file-storage.d.mts +47 -0
  75. package/dist/esm/batch-settlement/client/file-storage.mjs +63 -0
  76. package/dist/esm/batch-settlement/client/file-storage.mjs.map +1 -0
  77. package/dist/esm/batch-settlement/client/index.d.mts +111 -0
  78. package/dist/esm/batch-settlement/client/index.mjs +58 -0
  79. package/dist/esm/batch-settlement/client/index.mjs.map +1 -0
  80. package/dist/esm/batch-settlement/facilitator/index.d.mts +72 -0
  81. package/dist/esm/batch-settlement/facilitator/index.mjs +1252 -0
  82. package/dist/esm/batch-settlement/facilitator/index.mjs.map +1 -0
  83. package/dist/esm/batch-settlement/server/file-storage.d.mts +53 -0
  84. package/dist/esm/batch-settlement/server/file-storage.mjs +128 -0
  85. package/dist/esm/batch-settlement/server/file-storage.mjs.map +1 -0
  86. package/dist/esm/batch-settlement/server/index.d.mts +491 -0
  87. package/dist/esm/batch-settlement/server/index.mjs +1640 -0
  88. package/dist/esm/batch-settlement/server/index.mjs.map +1 -0
  89. package/dist/esm/batch-settlement/server/redis-storage.d.mts +87 -0
  90. package/dist/esm/batch-settlement/server/redis-storage.mjs +156 -0
  91. package/dist/esm/batch-settlement/server/redis-storage.mjs.map +1 -0
  92. package/dist/esm/chunk-2EUQTNJO.mjs +38 -0
  93. package/dist/esm/chunk-2EUQTNJO.mjs.map +1 -0
  94. package/dist/esm/chunk-3WZF6722.mjs +36 -0
  95. package/dist/esm/chunk-3WZF6722.mjs.map +1 -0
  96. package/dist/esm/chunk-E4Z7PNXC.mjs +275 -0
  97. package/dist/esm/chunk-E4Z7PNXC.mjs.map +1 -0
  98. package/dist/esm/chunk-GQVMVP4N.mjs +911 -0
  99. package/dist/esm/chunk-GQVMVP4N.mjs.map +1 -0
  100. package/dist/esm/chunk-H2EYJIZL.mjs +489 -0
  101. package/dist/esm/chunk-H2EYJIZL.mjs.map +1 -0
  102. package/dist/esm/chunk-H3KPLYGI.mjs +152 -0
  103. package/dist/esm/chunk-H3KPLYGI.mjs.map +1 -0
  104. package/dist/esm/chunk-HYABYUBD.mjs +432 -0
  105. package/dist/esm/chunk-HYABYUBD.mjs.map +1 -0
  106. package/dist/esm/chunk-I2DVUHM5.mjs +123 -0
  107. package/dist/esm/chunk-I2DVUHM5.mjs.map +1 -0
  108. package/dist/esm/chunk-JK7SLLF7.mjs +34 -0
  109. package/dist/esm/chunk-JK7SLLF7.mjs.map +1 -0
  110. package/dist/esm/chunk-JNT7C46S.mjs +352 -0
  111. package/dist/esm/chunk-JNT7C46S.mjs.map +1 -0
  112. package/dist/esm/chunk-MACPBXCT.mjs +415 -0
  113. package/dist/esm/chunk-MACPBXCT.mjs.map +1 -0
  114. package/dist/esm/chunk-P3QOX3QZ.mjs +113 -0
  115. package/dist/esm/chunk-P3QOX3QZ.mjs.map +1 -0
  116. package/dist/esm/chunk-QVATVA3J.mjs +47 -0
  117. package/dist/esm/chunk-QVATVA3J.mjs.map +1 -0
  118. package/dist/esm/chunk-SHJFA25H.mjs +159 -0
  119. package/dist/esm/chunk-SHJFA25H.mjs.map +1 -0
  120. package/dist/esm/chunk-TW7Z65AO.mjs +34 -0
  121. package/dist/esm/chunk-TW7Z65AO.mjs.map +1 -0
  122. package/dist/esm/chunk-U4HCGTLU.mjs +35 -0
  123. package/dist/esm/chunk-U4HCGTLU.mjs.map +1 -0
  124. package/dist/esm/chunk-VS3RYAYE.mjs +80 -0
  125. package/dist/esm/chunk-VS3RYAYE.mjs.map +1 -0
  126. package/dist/esm/chunk-W6ON4LG2.mjs +39 -0
  127. package/dist/esm/chunk-W6ON4LG2.mjs.map +1 -0
  128. package/dist/esm/chunk-XG2JLZVJ.mjs +627 -0
  129. package/dist/esm/chunk-XG2JLZVJ.mjs.map +1 -0
  130. package/dist/esm/chunk-ZCJRY5LQ.mjs +162 -0
  131. package/dist/esm/chunk-ZCJRY5LQ.mjs.map +1 -0
  132. package/dist/esm/client/agent-wallet.d.mts +69 -0
  133. package/dist/esm/client/agent-wallet.mjs +36 -0
  134. package/dist/esm/client/agent-wallet.mjs.map +1 -0
  135. package/dist/esm/exact/client/index.d.mts +63 -0
  136. package/dist/esm/exact/client/index.mjs +25 -0
  137. package/dist/esm/exact/client/index.mjs.map +1 -0
  138. package/dist/esm/exact/facilitator/index.d.mts +141 -0
  139. package/dist/esm/exact/facilitator/index.mjs +694 -0
  140. package/dist/esm/exact/facilitator/index.mjs.map +1 -0
  141. package/dist/esm/exact/server/index.d.mts +118 -0
  142. package/dist/esm/exact/server/index.mjs +153 -0
  143. package/dist/esm/exact/server/index.mjs.map +1 -0
  144. package/dist/esm/exact/v1/client/index.d.mts +38 -0
  145. package/dist/esm/exact/v1/client/index.mjs +12 -0
  146. package/dist/esm/exact/v1/client/index.mjs.map +1 -0
  147. package/dist/esm/exact/v1/facilitator/index.d.mts +84 -0
  148. package/dist/esm/exact/v1/facilitator/index.mjs +12 -0
  149. package/dist/esm/exact/v1/facilitator/index.mjs.map +1 -0
  150. package/dist/esm/facilitator/agent-wallet.d.mts +109 -0
  151. package/dist/esm/facilitator/agent-wallet.mjs +74 -0
  152. package/dist/esm/facilitator/agent-wallet.mjs.map +1 -0
  153. package/dist/esm/index.d.mts +338 -0
  154. package/dist/esm/index.mjs +144 -0
  155. package/dist/esm/index.mjs.map +1 -0
  156. package/dist/esm/permit2-DhJRUcgY.d.mts +729 -0
  157. package/dist/esm/rpc-DULZzRne.d.mts +13 -0
  158. package/dist/esm/scheme-CkNhpXrG.d.mts +307 -0
  159. package/dist/esm/scheme-D8ZbykGV.d.mts +47 -0
  160. package/dist/esm/signer-BFelv8DL.d.mts +170 -0
  161. package/dist/esm/storage-6W5MO46W.d.mts +50 -0
  162. package/dist/esm/storage-BEzTEiUr.d.mts +81 -0
  163. package/dist/esm/types-DIt9uAUy.d.mts +180 -0
  164. package/dist/esm/upto/client/index.d.mts +34 -0
  165. package/dist/esm/upto/client/index.mjs +22 -0
  166. package/dist/esm/upto/client/index.mjs.map +1 -0
  167. package/dist/esm/upto/facilitator/index.d.mts +54 -0
  168. package/dist/esm/upto/facilitator/index.mjs +507 -0
  169. package/dist/esm/upto/facilitator/index.mjs.map +1 -0
  170. package/dist/esm/upto/server/index.d.mts +69 -0
  171. package/dist/esm/upto/server/index.mjs +124 -0
  172. package/dist/esm/upto/server/index.mjs.map +1 -0
  173. package/dist/esm/v1/index.d.mts +40 -0
  174. package/dist/esm/v1/index.mjs +18 -0
  175. package/dist/esm/v1/index.mjs.map +1 -0
  176. package/package.json +250 -0
@@ -0,0 +1,1252 @@
1
+ import {
2
+ erc3009AuthorizationTimeInvalidReason,
3
+ readChannelState,
4
+ signClaimBatch,
5
+ signRefund,
6
+ toContractChannelConfig,
7
+ validateChannelConfig,
8
+ verifyBatchSettlementVoucherTypedData
9
+ } from "../../chunk-SHJFA25H.mjs";
10
+ import {
11
+ buildEip2612PermitData,
12
+ buildErc3009CollectorData,
13
+ buildErc3009DepositNonce,
14
+ buildPermit2CollectorData
15
+ } from "../../chunk-W6ON4LG2.mjs";
16
+ import {
17
+ BATCH_SETTLEMENT_ADDRESS,
18
+ BATCH_SETTLEMENT_SCHEME,
19
+ ERC3009_DEPOSIT_COLLECTOR_ADDRESS,
20
+ ErrAuthorizerAddressMismatch,
21
+ ErrChannelIdMismatch,
22
+ ErrChannelNotFound,
23
+ ErrClaimSimulationFailed,
24
+ ErrClaimTransactionFailed,
25
+ ErrCumulativeAmountBelowClaimed,
26
+ ErrCumulativeExceedsBalance,
27
+ ErrDepositSimulationFailed,
28
+ ErrDepositTransactionFailed,
29
+ ErrEip2612AmountMismatch,
30
+ ErrErc20ApprovalUnavailable,
31
+ ErrErc3009AuthorizationRequired,
32
+ ErrInsufficientBalance,
33
+ ErrInvalidPayloadType,
34
+ ErrInvalidReceiveAuthorizationSignature,
35
+ ErrInvalidScheme,
36
+ ErrInvalidVoucherSignature,
37
+ ErrMissingEip712Domain,
38
+ ErrNetworkMismatch,
39
+ ErrNothingToSettle,
40
+ ErrPermit2AllowanceRequired,
41
+ ErrPermit2AmountMismatch,
42
+ ErrPermit2AuthorizationRequired,
43
+ ErrPermit2DeadlineExpired,
44
+ ErrPermit2InvalidSignature,
45
+ ErrPermit2InvalidSpender,
46
+ ErrRefundNoBalance,
47
+ ErrRefundSimulationFailed,
48
+ ErrRefundTransactionFailed,
49
+ ErrRpcReadFailed,
50
+ ErrSettleSimulationFailed,
51
+ ErrSettleTransactionFailed,
52
+ ErrTokenMismatch,
53
+ PERMIT2_DEPOSIT_COLLECTOR_ADDRESS,
54
+ batchPermit2WitnessTypes,
55
+ batchSettlementABI,
56
+ computeChannelId,
57
+ erc20BalanceOfABI,
58
+ receiveAuthorizationTypes
59
+ } from "../../chunk-HYABYUBD.mjs";
60
+ import {
61
+ isBatchSettlementClaimPayload,
62
+ isBatchSettlementDepositPayload,
63
+ isBatchSettlementEnrichedRefundPayload,
64
+ isBatchSettlementRefundPayload,
65
+ isBatchSettlementSettlePayload,
66
+ isBatchSettlementVoucherPayload
67
+ } from "../../chunk-U4HCGTLU.mjs";
68
+ import {
69
+ splitEip2612Signature,
70
+ validateEip2612PermitForPayment,
71
+ validateErc20ApprovalForPayment
72
+ } from "../../chunk-H2EYJIZL.mjs";
73
+ import "../../chunk-P3QOX3QZ.mjs";
74
+ import {
75
+ ERC20_APPROVAL_GAS_SPONSORING_KEY,
76
+ appendDataSuffix,
77
+ extractEip2612GasSponsoringInfo,
78
+ extractErc20ApprovalGasSponsoringInfo,
79
+ resolveDataSuffix,
80
+ resolveErc20ApprovalExtensionSigner
81
+ } from "../../chunk-JNT7C46S.mjs";
82
+ import {
83
+ PERMIT2_ADDRESS,
84
+ erc20AllowanceAbi
85
+ } from "../../chunk-MACPBXCT.mjs";
86
+ import {
87
+ multicall
88
+ } from "../../chunk-VS3RYAYE.mjs";
89
+ import {
90
+ getEvmChainId
91
+ } from "../../chunk-TW7Z65AO.mjs";
92
+
93
+ // src/batch-settlement/facilitator/deposit.ts
94
+ import { getAddress as getAddress3 } from "viem";
95
+
96
+ // src/batch-settlement/facilitator/deposit-eip3009.ts
97
+ import { getAddress, parseErc6492Signature } from "viem";
98
+ function getEip3009DepositCollectorAddress() {
99
+ return getAddress(ERC3009_DEPOSIT_COLLECTOR_ADDRESS);
100
+ }
101
+ function buildEip3009DepositCollectorData(payload) {
102
+ const auth = payload.deposit.authorization.erc3009Authorization;
103
+ if (!auth) {
104
+ throw new Error(ErrErc3009AuthorizationRequired);
105
+ }
106
+ const { signature } = parseErc6492Signature(auth.signature);
107
+ return buildErc3009CollectorData(auth.validAfter, auth.validBefore, auth.salt, signature);
108
+ }
109
+ async function verifyEip3009DepositAuthorization(signer, payload, requirements, chainId) {
110
+ const { deposit, voucher } = payload;
111
+ const payer = payload.channelConfig.payer;
112
+ const auth = deposit.authorization.erc3009Authorization;
113
+ if (!auth) {
114
+ return { isValid: false, invalidReason: ErrErc3009AuthorizationRequired, payer };
115
+ }
116
+ const extra = requirements.extra;
117
+ if (!extra?.name || !extra?.version) {
118
+ return { isValid: false, invalidReason: ErrMissingEip712Domain, payer };
119
+ }
120
+ const validAfter = BigInt(auth.validAfter);
121
+ const validBefore = BigInt(auth.validBefore);
122
+ const timeInvalid = erc3009AuthorizationTimeInvalidReason(validAfter, validBefore);
123
+ if (timeInvalid) {
124
+ return { isValid: false, invalidReason: timeInvalid, payer };
125
+ }
126
+ const erc3009Nonce = buildErc3009DepositNonce(voucher.channelId, auth.salt);
127
+ const receiveAuthOk = await verifyReceiveAuth(signer, {
128
+ payer,
129
+ asset: requirements.asset,
130
+ name: extra.name,
131
+ version: extra.version,
132
+ chainId,
133
+ amount: deposit.amount,
134
+ validAfter,
135
+ validBefore,
136
+ nonce: erc3009Nonce,
137
+ signature: auth.signature
138
+ });
139
+ if (!receiveAuthOk) {
140
+ return { isValid: false, invalidReason: ErrInvalidReceiveAuthorizationSignature, payer };
141
+ }
142
+ return null;
143
+ }
144
+ async function verifyReceiveAuth(signer, params) {
145
+ try {
146
+ return await signer.verifyTypedData({
147
+ address: getAddress(params.payer),
148
+ domain: {
149
+ name: params.name,
150
+ version: params.version,
151
+ chainId: params.chainId,
152
+ verifyingContract: getAddress(params.asset)
153
+ },
154
+ types: receiveAuthorizationTypes,
155
+ primaryType: "ReceiveWithAuthorization",
156
+ message: {
157
+ from: getAddress(params.payer),
158
+ to: getAddress(ERC3009_DEPOSIT_COLLECTOR_ADDRESS),
159
+ value: BigInt(params.amount),
160
+ validAfter: params.validAfter,
161
+ validBefore: params.validBefore,
162
+ nonce: params.nonce
163
+ },
164
+ signature: params.signature
165
+ });
166
+ } catch {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ // src/batch-settlement/facilitator/deposit-permit2.ts
172
+ import { encodeFunctionData, getAddress as getAddress2, parseErc6492Signature as parseErc6492Signature2 } from "viem";
173
+ function getPermit2DepositCollectorAddress() {
174
+ return getAddress2(PERMIT2_DEPOSIT_COLLECTOR_ADDRESS);
175
+ }
176
+ function buildPermit2DepositCollectorData(payload, eip2612PermitData = "0x") {
177
+ const auth = payload.deposit.authorization.permit2Authorization;
178
+ if (!auth) {
179
+ throw new Error(ErrPermit2AuthorizationRequired);
180
+ }
181
+ const { signature } = parseErc6492Signature2(auth.signature);
182
+ return buildPermit2CollectorData(auth.nonce, auth.deadline, signature, eip2612PermitData);
183
+ }
184
+ async function verifyPermit2DepositAuthorization(signer, payment, payload, requirements, chainId, context) {
185
+ const authResult = await verifyPermit2TypedData(signer, payload, requirements, chainId);
186
+ if (authResult) {
187
+ return authResult;
188
+ }
189
+ const branchResult = await resolvePermit2DepositBranch(
190
+ signer,
191
+ payment,
192
+ payload,
193
+ requirements,
194
+ context
195
+ );
196
+ if ("isValid" in branchResult) {
197
+ return branchResult;
198
+ }
199
+ if (branchResult.kind !== "erc20Approval" || !branchResult.extensionSigner.simulateTransactions) {
200
+ return null;
201
+ }
202
+ const ok = await branchResult.extensionSigner.simulateTransactions([
203
+ branchResult.signedTransaction,
204
+ buildDepositTransaction(payload, branchResult.collectorData)
205
+ ]);
206
+ if (!ok) {
207
+ return {
208
+ isValid: false,
209
+ invalidReason: ErrDepositSimulationFailed,
210
+ payer: payload.channelConfig.payer
211
+ };
212
+ }
213
+ return null;
214
+ }
215
+ async function resolvePermit2DepositBranch(signer, payment, payload, requirements, context) {
216
+ const payer = payload.channelConfig.payer;
217
+ const tokenAddress = getAddress2(requirements.asset);
218
+ const eip2612Info = extractEip2612GasSponsoringInfo(payment);
219
+ if (eip2612Info) {
220
+ const result = validateBatchEip2612Permit(
221
+ eip2612Info,
222
+ payer,
223
+ tokenAddress,
224
+ payload.deposit.amount
225
+ );
226
+ if (!result.isValid) {
227
+ return { isValid: false, invalidReason: result.invalidReason, payer };
228
+ }
229
+ const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
230
+ return {
231
+ kind: "eip2612",
232
+ collectorData: buildPermit2DepositCollectorData(
233
+ payload,
234
+ buildEip2612PermitData({
235
+ value: eip2612Info.amount,
236
+ deadline: eip2612Info.deadline,
237
+ v,
238
+ r,
239
+ s
240
+ })
241
+ )
242
+ };
243
+ }
244
+ const erc20Info = extractErc20ApprovalGasSponsoringInfo(payment);
245
+ if (erc20Info) {
246
+ const extensionSigner = resolveErc20ApprovalExtensionSigner(
247
+ context?.getExtension(
248
+ ERC20_APPROVAL_GAS_SPONSORING_KEY
249
+ ),
250
+ requirements.network
251
+ );
252
+ if (!extensionSigner) {
253
+ return { isValid: false, invalidReason: ErrErc20ApprovalUnavailable, payer };
254
+ }
255
+ const result = await validateErc20ApprovalForPayment(erc20Info, payer, tokenAddress);
256
+ if (!result.isValid) {
257
+ return {
258
+ isValid: false,
259
+ invalidReason: result.invalidReason,
260
+ invalidMessage: result.invalidMessage,
261
+ payer
262
+ };
263
+ }
264
+ return {
265
+ kind: "erc20Approval",
266
+ collectorData: buildPermit2DepositCollectorData(payload),
267
+ signedTransaction: erc20Info.signedTransaction,
268
+ extensionSigner
269
+ };
270
+ }
271
+ try {
272
+ const allowance = await signer.readContract({
273
+ address: tokenAddress,
274
+ abi: erc20AllowanceAbi,
275
+ functionName: "allowance",
276
+ args: [payer, PERMIT2_ADDRESS]
277
+ });
278
+ if (allowance < BigInt(payload.deposit.amount)) {
279
+ return { isValid: false, invalidReason: ErrPermit2AllowanceRequired, payer };
280
+ }
281
+ } catch {
282
+ return { isValid: false, invalidReason: ErrPermit2AllowanceRequired, payer };
283
+ }
284
+ return {
285
+ kind: "standard",
286
+ collectorData: buildPermit2DepositCollectorData(payload)
287
+ };
288
+ }
289
+ function buildDepositTransaction(payload, collectorData, dataSuffix) {
290
+ const data = encodeFunctionData({
291
+ abi: batchSettlementABI,
292
+ functionName: "deposit",
293
+ args: [
294
+ toContractChannelConfig(payload.channelConfig),
295
+ BigInt(payload.deposit.amount),
296
+ getPermit2DepositCollectorAddress(),
297
+ collectorData
298
+ ]
299
+ });
300
+ return {
301
+ to: getAddress2(BATCH_SETTLEMENT_ADDRESS),
302
+ data: appendDataSuffix(data, dataSuffix),
303
+ gas: 300000n
304
+ };
305
+ }
306
+ async function verifyPermit2TypedData(signer, payload, requirements, chainId) {
307
+ const auth = payload.deposit.authorization.permit2Authorization;
308
+ const payer = payload.channelConfig.payer;
309
+ if (!auth) {
310
+ return { isValid: false, invalidReason: ErrPermit2AuthorizationRequired, payer };
311
+ }
312
+ if (getAddress2(auth.from) !== getAddress2(payer)) {
313
+ return { isValid: false, invalidReason: ErrPermit2InvalidSignature, payer };
314
+ }
315
+ if (getAddress2(auth.spender) !== getPermit2DepositCollectorAddress()) {
316
+ return { isValid: false, invalidReason: ErrPermit2InvalidSpender, payer };
317
+ }
318
+ if (getAddress2(auth.permitted.token) !== getAddress2(requirements.asset)) {
319
+ return { isValid: false, invalidReason: ErrTokenMismatch, payer };
320
+ }
321
+ if (BigInt(auth.permitted.amount) !== BigInt(payload.deposit.amount)) {
322
+ return { isValid: false, invalidReason: ErrPermit2AmountMismatch, payer };
323
+ }
324
+ if (auth.witness.channelId !== payload.voucher.channelId) {
325
+ return { isValid: false, invalidReason: ErrChannelIdMismatch, payer };
326
+ }
327
+ const now = Math.floor(Date.now() / 1e3);
328
+ if (BigInt(auth.deadline) < BigInt(now + 6)) {
329
+ return { isValid: false, invalidReason: ErrPermit2DeadlineExpired, payer };
330
+ }
331
+ try {
332
+ const ok = await signer.verifyTypedData({
333
+ address: getAddress2(auth.from),
334
+ domain: { name: "Permit2", chainId, verifyingContract: PERMIT2_ADDRESS },
335
+ types: batchPermit2WitnessTypes,
336
+ primaryType: "PermitWitnessTransferFrom",
337
+ message: {
338
+ permitted: {
339
+ token: getAddress2(auth.permitted.token),
340
+ amount: BigInt(auth.permitted.amount)
341
+ },
342
+ spender: getAddress2(auth.spender),
343
+ nonce: BigInt(auth.nonce),
344
+ deadline: BigInt(auth.deadline),
345
+ witness: {
346
+ channelId: auth.witness.channelId
347
+ }
348
+ },
349
+ signature: auth.signature
350
+ });
351
+ if (!ok) {
352
+ return { isValid: false, invalidReason: ErrPermit2InvalidSignature, payer };
353
+ }
354
+ } catch {
355
+ return { isValid: false, invalidReason: ErrPermit2InvalidSignature, payer };
356
+ }
357
+ return null;
358
+ }
359
+ function validateBatchEip2612Permit(info, payer, tokenAddress, depositAmount) {
360
+ const baseline = validateEip2612PermitForPayment(info, payer, tokenAddress);
361
+ if (!baseline.isValid) {
362
+ return {
363
+ isValid: false,
364
+ invalidReason: baseline.invalidReason ?? ErrInvalidPayloadType
365
+ };
366
+ }
367
+ if (BigInt(info.amount) !== BigInt(depositAmount)) {
368
+ return { isValid: false, invalidReason: ErrEip2612AmountMismatch };
369
+ }
370
+ return { isValid: true };
371
+ }
372
+
373
+ // src/batch-settlement/facilitator/deposit.ts
374
+ async function verifyDeposit(signer, payment, payload, requirements, context) {
375
+ const payer = payload.channelConfig.payer;
376
+ const chainId = getEvmChainId(requirements.network);
377
+ const configErr = validateChannelConfig(
378
+ payload.channelConfig,
379
+ payload.voucher.channelId,
380
+ requirements
381
+ );
382
+ if (configErr) {
383
+ return { isValid: false, invalidReason: configErr, payer };
384
+ }
385
+ const transferMethod = resolveDepositTransferMethod(payload, requirements);
386
+ if (transferMethod === "permit2" && !payload.deposit.authorization.permit2Authorization) {
387
+ return { isValid: false, invalidReason: ErrInvalidPayloadType, payer };
388
+ }
389
+ const methodErr = transferMethod === "permit2" ? await verifyPermit2DepositAuthorization(
390
+ signer,
391
+ payment,
392
+ payload,
393
+ requirements,
394
+ chainId,
395
+ context
396
+ ) : await verifyEip3009DepositAuthorization(signer, payload, requirements, chainId);
397
+ if (methodErr) {
398
+ return methodErr;
399
+ }
400
+ const shared = await verifySharedDepositState(signer, payload, requirements);
401
+ if (!shared.ok) {
402
+ return shared.response;
403
+ }
404
+ const { depositAmount, chBalance, chTotalClaimed, wdInitiatedAt, refundNonceVal } = shared;
405
+ const execution = await resolveDepositExecution(signer, payment, payload, requirements, context);
406
+ if ("isValid" in execution) {
407
+ return execution;
408
+ }
409
+ if (!execution.skipDirectSimulation) {
410
+ try {
411
+ await signer.readContract({
412
+ address: getAddress3(BATCH_SETTLEMENT_ADDRESS),
413
+ abi: batchSettlementABI,
414
+ functionName: "deposit",
415
+ args: [
416
+ toContractChannelConfig(payload.channelConfig),
417
+ depositAmount,
418
+ execution.collector,
419
+ execution.collectorData
420
+ ]
421
+ });
422
+ } catch (e) {
423
+ return {
424
+ isValid: false,
425
+ invalidReason: ErrDepositSimulationFailed,
426
+ invalidMessage: e instanceof Error ? e.message : String(e),
427
+ payer
428
+ };
429
+ }
430
+ }
431
+ return {
432
+ isValid: true,
433
+ payer,
434
+ extra: {
435
+ channelId: payload.voucher.channelId,
436
+ balance: chBalance.toString(),
437
+ totalClaimed: chTotalClaimed.toString(),
438
+ withdrawRequestedAt: Number(wdInitiatedAt),
439
+ refundNonce: refundNonceVal.toString()
440
+ }
441
+ };
442
+ }
443
+ async function verifySharedDepositState(signer, payload, requirements) {
444
+ const { deposit, voucher } = payload;
445
+ const config = payload.channelConfig;
446
+ const payer = config.payer;
447
+ const chainId = getEvmChainId(requirements.network);
448
+ const configErr = validateChannelConfig(config, voucher.channelId, requirements);
449
+ if (configErr) {
450
+ return { ok: false, response: { isValid: false, invalidReason: configErr, payer } };
451
+ }
452
+ const voucherOk = await verifyBatchSettlementVoucherTypedData(
453
+ signer,
454
+ {
455
+ channelId: voucher.channelId,
456
+ maxClaimableAmount: voucher.maxClaimableAmount,
457
+ payerAuthorizer: config.payerAuthorizer,
458
+ payer: config.payer,
459
+ signature: voucher.signature
460
+ },
461
+ chainId
462
+ );
463
+ if (!voucherOk) {
464
+ return {
465
+ ok: false,
466
+ response: { isValid: false, invalidReason: ErrInvalidVoucherSignature, payer }
467
+ };
468
+ }
469
+ const mcResults = await multicall(signer.readContract.bind(signer), [
470
+ {
471
+ address: getAddress3(BATCH_SETTLEMENT_ADDRESS),
472
+ abi: batchSettlementABI,
473
+ functionName: "channels",
474
+ args: [voucher.channelId]
475
+ },
476
+ {
477
+ address: getAddress3(requirements.asset),
478
+ abi: erc20BalanceOfABI,
479
+ functionName: "balanceOf",
480
+ args: [getAddress3(payer)]
481
+ },
482
+ {
483
+ address: getAddress3(BATCH_SETTLEMENT_ADDRESS),
484
+ abi: batchSettlementABI,
485
+ functionName: "pendingWithdrawals",
486
+ args: [voucher.channelId]
487
+ },
488
+ {
489
+ address: getAddress3(BATCH_SETTLEMENT_ADDRESS),
490
+ abi: batchSettlementABI,
491
+ functionName: "refundNonce",
492
+ args: [voucher.channelId]
493
+ }
494
+ ]);
495
+ const [chRes, balRes, wdRes, rnRes] = mcResults;
496
+ if (chRes.status === "failure" || balRes.status === "failure" || wdRes.status === "failure" || rnRes.status === "failure") {
497
+ return {
498
+ ok: false,
499
+ response: { isValid: false, invalidReason: ErrRpcReadFailed, payer }
500
+ };
501
+ }
502
+ const [chBalance, chTotalClaimed] = chRes.result;
503
+ const payerBalance = balRes.result;
504
+ const [, wdInitiatedAt] = wdRes.result;
505
+ const refundNonceVal = rnRes.result;
506
+ const depositAmount = BigInt(deposit.amount);
507
+ if (payerBalance < depositAmount) {
508
+ return {
509
+ ok: false,
510
+ response: { isValid: false, invalidReason: ErrInsufficientBalance, payer }
511
+ };
512
+ }
513
+ const effectiveBalance = chBalance + depositAmount;
514
+ const maxClaimableAmount = BigInt(voucher.maxClaimableAmount);
515
+ if (maxClaimableAmount > effectiveBalance) {
516
+ return {
517
+ ok: false,
518
+ response: { isValid: false, invalidReason: ErrCumulativeExceedsBalance, payer }
519
+ };
520
+ }
521
+ if (maxClaimableAmount <= chTotalClaimed) {
522
+ return {
523
+ ok: false,
524
+ response: { isValid: false, invalidReason: ErrCumulativeAmountBelowClaimed, payer }
525
+ };
526
+ }
527
+ return {
528
+ ok: true,
529
+ chainId,
530
+ depositAmount,
531
+ payer,
532
+ chBalance,
533
+ chTotalClaimed,
534
+ wdInitiatedAt,
535
+ refundNonceVal
536
+ };
537
+ }
538
+ async function settleDeposit(signer, payment, payload, requirements, context, dataSuffix) {
539
+ const { deposit, voucher } = payload;
540
+ const config = payload.channelConfig;
541
+ const payer = config.payer;
542
+ const verified = await verifyDeposit(signer, payment, payload, requirements, context);
543
+ if (!verified.isValid) {
544
+ const reason = verified.invalidReason ?? ErrInvalidPayloadType;
545
+ return {
546
+ success: false,
547
+ errorReason: reason,
548
+ errorMessage: verified.invalidMessage ?? reason,
549
+ transaction: "",
550
+ network: requirements.network,
551
+ payer: verified.payer
552
+ };
553
+ }
554
+ try {
555
+ const execution = await resolveDepositExecution(
556
+ signer,
557
+ payment,
558
+ payload,
559
+ requirements,
560
+ context
561
+ );
562
+ if ("isValid" in execution) {
563
+ const reason = execution.invalidReason ?? ErrInvalidPayloadType;
564
+ return {
565
+ success: false,
566
+ errorReason: reason,
567
+ errorMessage: execution.invalidMessage ?? reason,
568
+ transaction: "",
569
+ network: requirements.network,
570
+ payer: execution.payer
571
+ };
572
+ }
573
+ const depositTx = buildDepositTransaction(payload, execution.collectorData, dataSuffix);
574
+ const tx = execution.kind === "erc20Approval" ? (await execution.extensionSigner.sendTransactions([
575
+ execution.signedTransaction,
576
+ depositTx
577
+ ]))[1] : await signer.writeContract({
578
+ address: getAddress3(BATCH_SETTLEMENT_ADDRESS),
579
+ abi: batchSettlementABI,
580
+ functionName: "deposit",
581
+ args: [
582
+ toContractChannelConfig(config),
583
+ BigInt(deposit.amount),
584
+ execution.collector,
585
+ execution.collectorData
586
+ ],
587
+ dataSuffix
588
+ });
589
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
590
+ if (receipt.status !== "success") {
591
+ return {
592
+ success: false,
593
+ errorReason: ErrDepositTransactionFailed,
594
+ errorMessage: `transaction reverted (receipt status ${receipt.status})`,
595
+ transaction: tx,
596
+ network: requirements.network,
597
+ payer
598
+ };
599
+ }
600
+ const optimisticExtra = {
601
+ channelState: {
602
+ channelId: voucher.channelId,
603
+ balance: (BigInt(String(verified.extra?.balance ?? "0")) + BigInt(deposit.amount)).toString(),
604
+ totalClaimed: String(verified.extra?.totalClaimed ?? "0"),
605
+ withdrawRequestedAt: Number(verified.extra?.withdrawRequestedAt ?? 0),
606
+ refundNonce: String(verified.extra?.refundNonce ?? "0")
607
+ }
608
+ };
609
+ const expectedMinBalance = BigInt(optimisticExtra.channelState.balance);
610
+ const rpcDeadline = Date.now() + 2e3;
611
+ let postState = await readChannelState(signer, voucher.channelId);
612
+ while (postState.balance < expectedMinBalance && Date.now() < rpcDeadline) {
613
+ await new Promise((resolve) => setTimeout(resolve, 150));
614
+ postState = await readChannelState(signer, voucher.channelId);
615
+ }
616
+ const rpcCaughtUp = postState.balance >= expectedMinBalance;
617
+ return {
618
+ success: true,
619
+ transaction: tx,
620
+ network: requirements.network,
621
+ payer,
622
+ amount: deposit.amount,
623
+ extra: rpcCaughtUp ? {
624
+ ...optimisticExtra,
625
+ channelState: {
626
+ channelId: voucher.channelId,
627
+ balance: postState.balance.toString(),
628
+ totalClaimed: postState.totalClaimed.toString(),
629
+ withdrawRequestedAt: postState.withdrawRequestedAt,
630
+ refundNonce: postState.refundNonce.toString()
631
+ }
632
+ } : optimisticExtra
633
+ };
634
+ } catch (e) {
635
+ return {
636
+ success: false,
637
+ errorReason: ErrDepositTransactionFailed,
638
+ errorMessage: e instanceof Error ? e.message : String(e),
639
+ transaction: "",
640
+ network: requirements.network,
641
+ payer
642
+ };
643
+ }
644
+ }
645
+ async function resolveDepositExecution(signer, payment, payload, requirements, context) {
646
+ const transferMethod = resolveDepositTransferMethod(payload, requirements);
647
+ if (transferMethod === "eip3009") {
648
+ return {
649
+ kind: "direct",
650
+ collector: getEip3009DepositCollectorAddress(),
651
+ collectorData: buildEip3009DepositCollectorData(payload)
652
+ };
653
+ }
654
+ const branch = await resolvePermit2DepositBranch(signer, payment, payload, requirements, context);
655
+ if ("isValid" in branch) {
656
+ return branch;
657
+ }
658
+ if (branch.kind === "erc20Approval") {
659
+ return {
660
+ kind: "erc20Approval",
661
+ collector: getPermit2DepositCollectorAddress(),
662
+ collectorData: branch.collectorData,
663
+ signedTransaction: branch.signedTransaction,
664
+ extensionSigner: branch.extensionSigner,
665
+ skipDirectSimulation: true
666
+ };
667
+ }
668
+ return {
669
+ kind: "direct",
670
+ collector: getPermit2DepositCollectorAddress(),
671
+ collectorData: branch.collectorData
672
+ };
673
+ }
674
+ function resolveDepositTransferMethod(payload, requirements) {
675
+ const hinted = requirements.extra?.assetTransferMethod;
676
+ if (hinted) {
677
+ return hinted;
678
+ }
679
+ return payload.deposit.authorization.permit2Authorization ? "permit2" : "eip3009";
680
+ }
681
+
682
+ // src/batch-settlement/facilitator/voucher.ts
683
+ async function verifyVoucher(signer, payload, requirements, channelConfig) {
684
+ const { voucher } = payload;
685
+ const channelId = voucher.channelId;
686
+ const chainId = getEvmChainId(requirements.network);
687
+ const configErr = validateChannelConfig(channelConfig, channelId, requirements);
688
+ if (configErr) {
689
+ return { isValid: false, invalidReason: configErr, payer: channelConfig.payer };
690
+ }
691
+ const voucherOk = await verifyBatchSettlementVoucherTypedData(
692
+ signer,
693
+ {
694
+ channelId,
695
+ maxClaimableAmount: voucher.maxClaimableAmount,
696
+ payerAuthorizer: channelConfig.payerAuthorizer,
697
+ payer: channelConfig.payer,
698
+ signature: voucher.signature
699
+ },
700
+ chainId
701
+ );
702
+ if (!voucherOk) {
703
+ return {
704
+ isValid: false,
705
+ invalidReason: ErrInvalidVoucherSignature,
706
+ payer: channelConfig.payer
707
+ };
708
+ }
709
+ const state = await readChannelState(signer, channelId);
710
+ if (state.balance === 0n) {
711
+ return { isValid: false, invalidReason: ErrChannelNotFound, payer: channelConfig.payer };
712
+ }
713
+ const maxClaimableAmount = BigInt(voucher.maxClaimableAmount);
714
+ if (maxClaimableAmount > state.balance) {
715
+ return {
716
+ isValid: false,
717
+ invalidReason: ErrCumulativeExceedsBalance,
718
+ payer: channelConfig.payer
719
+ };
720
+ }
721
+ const belowClaimed = payload.type === "refund" ? maxClaimableAmount < state.totalClaimed : maxClaimableAmount <= state.totalClaimed;
722
+ if (belowClaimed) {
723
+ return {
724
+ isValid: false,
725
+ invalidReason: ErrCumulativeAmountBelowClaimed,
726
+ payer: channelConfig.payer
727
+ };
728
+ }
729
+ return {
730
+ isValid: true,
731
+ payer: channelConfig.payer,
732
+ extra: {
733
+ channelId,
734
+ balance: state.balance.toString(),
735
+ totalClaimed: state.totalClaimed.toString(),
736
+ withdrawRequestedAt: state.withdrawRequestedAt,
737
+ refundNonce: state.refundNonce.toString()
738
+ }
739
+ };
740
+ }
741
+
742
+ // src/batch-settlement/facilitator/claim.ts
743
+ import { getAddress as getAddress4 } from "viem";
744
+ function buildVoucherClaimArgs(claims) {
745
+ return claims.map((c) => ({
746
+ voucher: {
747
+ channel: toContractChannelConfig(c.voucher.channel),
748
+ maxClaimableAmount: BigInt(c.voucher.maxClaimableAmount)
749
+ },
750
+ signature: c.signature,
751
+ totalClaimed: BigInt(c.totalClaimed)
752
+ }));
753
+ }
754
+ async function executeClaimWithSignature(signer, payload, requirements, authorizerSigner, dataSuffix) {
755
+ const network = requirements.network;
756
+ const claimArgs = buildVoucherClaimArgs(payload.claims);
757
+ let sig = payload.claimAuthorizerSignature;
758
+ if (!sig) {
759
+ for (const claim of payload.claims) {
760
+ if (getAddress4(claim.voucher.channel.receiverAuthorizer) !== getAddress4(authorizerSigner.address)) {
761
+ return {
762
+ success: false,
763
+ errorReason: ErrAuthorizerAddressMismatch,
764
+ transaction: "",
765
+ network
766
+ };
767
+ }
768
+ }
769
+ sig = await signClaimBatch(authorizerSigner, payload.claims, network);
770
+ }
771
+ try {
772
+ await signer.readContract({
773
+ address: getAddress4(BATCH_SETTLEMENT_ADDRESS),
774
+ abi: batchSettlementABI,
775
+ functionName: "claimWithSignature",
776
+ args: [claimArgs, sig]
777
+ });
778
+ } catch (e) {
779
+ return {
780
+ success: false,
781
+ errorReason: ErrClaimSimulationFailed,
782
+ errorMessage: e instanceof Error ? e.message : String(e),
783
+ transaction: "",
784
+ network
785
+ };
786
+ }
787
+ try {
788
+ const tx = await signer.writeContract({
789
+ address: getAddress4(BATCH_SETTLEMENT_ADDRESS),
790
+ abi: batchSettlementABI,
791
+ functionName: "claimWithSignature",
792
+ args: [claimArgs, sig],
793
+ dataSuffix
794
+ });
795
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
796
+ if (receipt.status !== "success") {
797
+ return {
798
+ success: false,
799
+ errorReason: ErrClaimTransactionFailed,
800
+ errorMessage: `transaction reverted (receipt status ${receipt.status})`,
801
+ transaction: tx,
802
+ network
803
+ };
804
+ }
805
+ return {
806
+ success: true,
807
+ transaction: tx,
808
+ network,
809
+ amount: ""
810
+ };
811
+ } catch (e) {
812
+ return {
813
+ success: false,
814
+ errorReason: ErrClaimTransactionFailed,
815
+ errorMessage: e instanceof Error ? e.message : String(e),
816
+ transaction: "",
817
+ network
818
+ };
819
+ }
820
+ }
821
+
822
+ // src/batch-settlement/facilitator/settle.ts
823
+ import { getAddress as getAddress5, isAddressEqual, parseEventLogs } from "viem";
824
+ var SETTLE_GAS_LIMIT = 120000n;
825
+ async function executeSettle(signer, payload, requirements, dataSuffix) {
826
+ const network = requirements.network;
827
+ const contractAddr = getAddress5(BATCH_SETTLEMENT_ADDRESS);
828
+ const receiver = getAddress5(payload.receiver);
829
+ const token = getAddress5(payload.token);
830
+ try {
831
+ const [totalClaimed, totalSettled] = await signer.readContract({
832
+ address: contractAddr,
833
+ abi: batchSettlementABI,
834
+ functionName: "receivers",
835
+ args: [receiver, token]
836
+ });
837
+ if (totalClaimed <= totalSettled) {
838
+ return {
839
+ success: false,
840
+ errorReason: ErrNothingToSettle,
841
+ errorMessage: "nothing to settle for receiver and token",
842
+ transaction: "",
843
+ network
844
+ };
845
+ }
846
+ } catch (e) {
847
+ return {
848
+ success: false,
849
+ errorReason: ErrRpcReadFailed,
850
+ errorMessage: e instanceof Error ? e.message : String(e),
851
+ transaction: "",
852
+ network
853
+ };
854
+ }
855
+ try {
856
+ await signer.readContract({
857
+ address: contractAddr,
858
+ abi: batchSettlementABI,
859
+ functionName: "settle",
860
+ args: [receiver, token]
861
+ });
862
+ } catch (e) {
863
+ return {
864
+ success: false,
865
+ errorReason: ErrSettleSimulationFailed,
866
+ errorMessage: e instanceof Error ? e.message : String(e),
867
+ transaction: "",
868
+ network
869
+ };
870
+ }
871
+ try {
872
+ const tx = await signer.writeContract({
873
+ address: contractAddr,
874
+ abi: batchSettlementABI,
875
+ functionName: "settle",
876
+ args: [receiver, token],
877
+ gas: SETTLE_GAS_LIMIT,
878
+ dataSuffix
879
+ });
880
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
881
+ if (receipt.status !== "success") {
882
+ return {
883
+ success: false,
884
+ errorReason: ErrSettleTransactionFailed,
885
+ errorMessage: `transaction reverted (receipt status ${receipt.status})`,
886
+ transaction: tx,
887
+ network
888
+ };
889
+ }
890
+ let amount = "";
891
+ if (receipt.logs) {
892
+ const logs = parseEventLogs({
893
+ abi: batchSettlementABI,
894
+ eventName: "Settled",
895
+ logs: receipt.logs.filter((log) => isAddressEqual(log.address, contractAddr))
896
+ });
897
+ const settledLog = logs.find(
898
+ (log) => isAddressEqual(log.args.receiver, receiver) && isAddressEqual(log.args.token, token)
899
+ );
900
+ amount = settledLog?.args.amount.toString() ?? "0";
901
+ }
902
+ return {
903
+ success: true,
904
+ transaction: tx,
905
+ network,
906
+ amount
907
+ };
908
+ } catch (e) {
909
+ return {
910
+ success: false,
911
+ errorReason: ErrSettleTransactionFailed,
912
+ errorMessage: e instanceof Error ? e.message : String(e),
913
+ transaction: "",
914
+ network
915
+ };
916
+ }
917
+ }
918
+
919
+ // src/batch-settlement/facilitator/refund.ts
920
+ import { encodeFunctionData as encodeFunctionData2, getAddress as getAddress6 } from "viem";
921
+ var REFUND_STATE_POLL_MS = 2e3;
922
+ var REFUND_STATE_POLL_INTERVAL_MS = 150;
923
+ function getRefundableAmount(payload, preState, channelId, network) {
924
+ const postClaimTotalClaimed = payload.claims.reduce((max, claim) => {
925
+ const claimChannelId = computeChannelId(claim.voucher.channel, network);
926
+ if (claimChannelId.toLowerCase() !== channelId.toLowerCase()) {
927
+ return max;
928
+ }
929
+ const totalClaimed = BigInt(claim.totalClaimed);
930
+ return totalClaimed > max ? totalClaimed : max;
931
+ }, preState.totalClaimed);
932
+ if (postClaimTotalClaimed > preState.balance) {
933
+ return null;
934
+ }
935
+ const requestedAmount = BigInt(payload.amount);
936
+ if (requestedAmount === 0n) {
937
+ return null;
938
+ }
939
+ const available = preState.balance - postClaimTotalClaimed;
940
+ return requestedAmount > available ? available : requestedAmount;
941
+ }
942
+ function buildRefundExtra(payload, channelId, preState) {
943
+ const preTotalClaimed = preState?.totalClaimed ?? 0n;
944
+ const preBalance = preState?.balance ?? 0n;
945
+ const lastClaimTotal = payload.claims.length > 0 ? BigInt(payload.claims[payload.claims.length - 1].totalClaimed) : preTotalClaimed;
946
+ const postClaimTotalClaimed = lastClaimTotal > preTotalClaimed ? lastClaimTotal : preTotalClaimed;
947
+ const available = preBalance - postClaimTotalClaimed;
948
+ const requestedAmount = BigInt(payload.amount);
949
+ const actualRefund = requestedAmount > available ? available : requestedAmount;
950
+ return {
951
+ amount: actualRefund.toString(),
952
+ extra: {
953
+ channelState: {
954
+ channelId,
955
+ balance: (preBalance - actualRefund).toString(),
956
+ totalClaimed: postClaimTotalClaimed.toString(),
957
+ withdrawRequestedAt: 0,
958
+ refundNonce: String((preState?.refundNonce ?? 0n) + 1n)
959
+ }
960
+ }
961
+ };
962
+ }
963
+ async function readPostRefundState(signer, channelId, submittedNonce) {
964
+ const expectedNonce = BigInt(submittedNonce) + 1n;
965
+ const deadline = Date.now() + REFUND_STATE_POLL_MS;
966
+ do {
967
+ let state;
968
+ try {
969
+ state = await readChannelState(signer, channelId);
970
+ } catch {
971
+ return null;
972
+ }
973
+ if (state.refundNonce >= expectedNonce) {
974
+ return state;
975
+ }
976
+ await new Promise((resolve) => setTimeout(resolve, REFUND_STATE_POLL_INTERVAL_MS));
977
+ } while (Date.now() < deadline);
978
+ return null;
979
+ }
980
+ function buildRefundExtraFromPostState(channelId, preState, postState) {
981
+ const actualRefund = preState.balance > postState.balance ? preState.balance - postState.balance : 0n;
982
+ return {
983
+ amount: actualRefund.toString(),
984
+ extra: {
985
+ channelState: {
986
+ channelId,
987
+ balance: postState.balance.toString(),
988
+ totalClaimed: postState.totalClaimed.toString(),
989
+ withdrawRequestedAt: postState.withdrawRequestedAt,
990
+ refundNonce: postState.refundNonce.toString()
991
+ }
992
+ }
993
+ };
994
+ }
995
+ async function executeRefundWithSignature(signer, payload, requirements, authorizerSigner, dataSuffix) {
996
+ const network = requirements.network;
997
+ try {
998
+ const channelId = computeChannelId(payload.channelConfig, network);
999
+ const preState = await readChannelState(signer, channelId);
1000
+ const contractAddr = getAddress6(BATCH_SETTLEMENT_ADDRESS);
1001
+ const refundableAmount = getRefundableAmount(payload, preState, channelId, network);
1002
+ if (refundableAmount === 0n) {
1003
+ return {
1004
+ success: false,
1005
+ errorReason: ErrRefundNoBalance,
1006
+ errorMessage: "Nothing to refund",
1007
+ transaction: "",
1008
+ network
1009
+ };
1010
+ }
1011
+ const hasClientSig = payload.refundAuthorizerSignature !== void 0;
1012
+ const authorizerMismatch = getAddress6(payload.channelConfig.receiverAuthorizer) !== getAddress6(authorizerSigner.address);
1013
+ if (!hasClientSig && authorizerMismatch) {
1014
+ return {
1015
+ success: false,
1016
+ errorReason: ErrAuthorizerAddressMismatch,
1017
+ transaction: "",
1018
+ network
1019
+ };
1020
+ }
1021
+ const refundSig = payload.refundAuthorizerSignature ?? await signRefund(authorizerSigner, channelId, payload.amount, payload.refundNonce, network);
1022
+ const refundCalldata = encodeFunctionData2({
1023
+ abi: batchSettlementABI,
1024
+ functionName: "refundWithSignature",
1025
+ args: [
1026
+ toContractChannelConfig(payload.channelConfig),
1027
+ BigInt(payload.amount),
1028
+ BigInt(payload.refundNonce),
1029
+ refundSig
1030
+ ]
1031
+ });
1032
+ let tx;
1033
+ if (payload.claims.length > 0) {
1034
+ let claimSig = payload.claimAuthorizerSignature;
1035
+ if (!claimSig) {
1036
+ claimSig = await signClaimBatch(authorizerSigner, payload.claims, network);
1037
+ }
1038
+ const claimCalldata = encodeFunctionData2({
1039
+ abi: batchSettlementABI,
1040
+ functionName: "claimWithSignature",
1041
+ args: [buildVoucherClaimArgs(payload.claims), claimSig]
1042
+ });
1043
+ try {
1044
+ await signer.readContract({
1045
+ address: contractAddr,
1046
+ abi: batchSettlementABI,
1047
+ functionName: "multicall",
1048
+ args: [[claimCalldata, refundCalldata]]
1049
+ });
1050
+ } catch (e) {
1051
+ return {
1052
+ success: false,
1053
+ errorReason: ErrRefundSimulationFailed,
1054
+ errorMessage: e instanceof Error ? e.message : String(e),
1055
+ transaction: "",
1056
+ network
1057
+ };
1058
+ }
1059
+ tx = await signer.writeContract({
1060
+ address: contractAddr,
1061
+ abi: batchSettlementABI,
1062
+ functionName: "multicall",
1063
+ args: [[claimCalldata, refundCalldata]],
1064
+ dataSuffix
1065
+ });
1066
+ } else {
1067
+ try {
1068
+ await signer.readContract({
1069
+ address: contractAddr,
1070
+ abi: batchSettlementABI,
1071
+ functionName: "refundWithSignature",
1072
+ args: [
1073
+ toContractChannelConfig(payload.channelConfig),
1074
+ BigInt(payload.amount),
1075
+ BigInt(payload.refundNonce),
1076
+ refundSig
1077
+ ]
1078
+ });
1079
+ } catch (e) {
1080
+ return {
1081
+ success: false,
1082
+ errorReason: ErrRefundSimulationFailed,
1083
+ errorMessage: e instanceof Error ? e.message : String(e),
1084
+ transaction: "",
1085
+ network
1086
+ };
1087
+ }
1088
+ tx = await signer.writeContract({
1089
+ address: contractAddr,
1090
+ abi: batchSettlementABI,
1091
+ functionName: "refundWithSignature",
1092
+ args: [
1093
+ toContractChannelConfig(payload.channelConfig),
1094
+ BigInt(payload.amount),
1095
+ BigInt(payload.refundNonce),
1096
+ refundSig
1097
+ ],
1098
+ dataSuffix
1099
+ });
1100
+ }
1101
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
1102
+ if (receipt.status !== "success") {
1103
+ return {
1104
+ success: false,
1105
+ errorReason: ErrRefundTransactionFailed,
1106
+ errorMessage: `transaction reverted (receipt status ${receipt.status})`,
1107
+ transaction: tx,
1108
+ network
1109
+ };
1110
+ }
1111
+ const postState = preState && preState.withdrawRequestedAt !== 0 ? await readPostRefundState(signer, channelId, payload.refundNonce) : null;
1112
+ const refundDetails = preState && postState ? buildRefundExtraFromPostState(channelId, preState, postState) : buildRefundExtra(payload, channelId, preState);
1113
+ return {
1114
+ success: true,
1115
+ transaction: tx,
1116
+ network,
1117
+ payer: payload.channelConfig.payer,
1118
+ amount: refundDetails.amount,
1119
+ extra: refundDetails.extra
1120
+ };
1121
+ } catch (e) {
1122
+ return {
1123
+ success: false,
1124
+ errorReason: ErrRefundTransactionFailed,
1125
+ errorMessage: e instanceof Error ? e.message : String(e),
1126
+ transaction: "",
1127
+ network
1128
+ };
1129
+ }
1130
+ }
1131
+
1132
+ // src/batch-settlement/facilitator/scheme.ts
1133
+ var BatchSettlementEvmScheme = class {
1134
+ /**
1135
+ * Creates a facilitator scheme for verifying and settling batch-settlement payments.
1136
+ *
1137
+ * @param signer - Facilitator EVM signer(s) used for tx submission and onchain reads.
1138
+ * @param authorizerSigner - Dedicated key that provides EIP-712 signatures for
1139
+ * `claimWithSignature` / `refundWithSignature`. The facilitator will sign missing
1140
+ * authorizer signatures using this key when the server omits them.
1141
+ */
1142
+ constructor(signer, authorizerSigner) {
1143
+ this.signer = signer;
1144
+ this.authorizerSigner = authorizerSigner;
1145
+ this.scheme = BATCH_SETTLEMENT_SCHEME;
1146
+ this.caipFamily = "eip155:*";
1147
+ }
1148
+ /**
1149
+ * Returns facilitator-specific extra fields to be merged into payment requirements.
1150
+ *
1151
+ * Exposes the configured `receiverAuthorizer` address so the server and client can
1152
+ * embed it in `ChannelConfig`.
1153
+ *
1154
+ * @param _ - Network identifier (unused).
1155
+ * @returns Extra fields containing `receiverAuthorizer`.
1156
+ */
1157
+ getExtra(_) {
1158
+ return { receiverAuthorizer: this.authorizerSigner.address };
1159
+ }
1160
+ /**
1161
+ * Returns all facilitator signer addresses available for the given network.
1162
+ *
1163
+ * @param _ - Network identifier (unused).
1164
+ * @returns Array of hex addresses.
1165
+ */
1166
+ getSigners(_) {
1167
+ return [...this.signer.getAddresses()];
1168
+ }
1169
+ /**
1170
+ * Verifies a payment payload (deposit or voucher) without executing settlement.
1171
+ *
1172
+ * @param payload - The x402 payment payload envelope.
1173
+ * @param requirements - Server payment requirements (scheme, network, asset, amount).
1174
+ * @param context - Optional facilitator extension context.
1175
+ * @param _ - Payment required extensions (unused; reserved for interface parity)
1176
+ * @returns A {@link VerifyResponse} indicating validity with payer and channel state in `extra`.
1177
+ */
1178
+ async verify(payload, requirements, context, _) {
1179
+ const rawPayload = payload.payload;
1180
+ if (payload.accepted.scheme !== BATCH_SETTLEMENT_SCHEME || requirements.scheme !== BATCH_SETTLEMENT_SCHEME) {
1181
+ return { isValid: false, invalidReason: ErrInvalidScheme };
1182
+ }
1183
+ if (payload.accepted.network !== requirements.network) {
1184
+ return { isValid: false, invalidReason: ErrNetworkMismatch };
1185
+ }
1186
+ if (isBatchSettlementDepositPayload(rawPayload)) {
1187
+ return verifyDeposit(this.signer, payload, rawPayload, requirements, context);
1188
+ }
1189
+ if (isBatchSettlementVoucherPayload(rawPayload)) {
1190
+ return verifyVoucher(this.signer, rawPayload, requirements, rawPayload.channelConfig);
1191
+ }
1192
+ if (isBatchSettlementRefundPayload(rawPayload)) {
1193
+ return verifyVoucher(this.signer, rawPayload, requirements, rawPayload.channelConfig);
1194
+ }
1195
+ return { isValid: false, invalidReason: ErrInvalidPayloadType };
1196
+ }
1197
+ /**
1198
+ * Executes settlement for a payment payload.
1199
+ *
1200
+ * Dispatches to the correct handler based on payload settle action:
1201
+ * - `deposit` → onchain `deposit(config, amount, collector, collectorData)`
1202
+ * - `claim` → onchain `claimWithSignature(VoucherClaim[], bytes)`
1203
+ * - `settle` → onchain `settle(receiver, token)`
1204
+ * - `refund` → optional claim + onchain `refundWithSignature(config, amount, nonce, sig)`
1205
+ *
1206
+ * @param payload - The x402 payment payload envelope.
1207
+ * @param requirements - Server payment requirements.
1208
+ * @param context - Optional facilitator extension context.
1209
+ * @returns A {@link SettleResponse} with the transaction hash on success.
1210
+ */
1211
+ async settle(payload, requirements, context) {
1212
+ const rawPayload = payload.payload;
1213
+ const dataSuffix = await resolveDataSuffix(context, {
1214
+ paymentPayload: payload,
1215
+ paymentRequirements: requirements
1216
+ });
1217
+ if (isBatchSettlementDepositPayload(rawPayload)) {
1218
+ return settleDeposit(this.signer, payload, rawPayload, requirements, context, dataSuffix);
1219
+ }
1220
+ if (isBatchSettlementClaimPayload(rawPayload)) {
1221
+ return executeClaimWithSignature(
1222
+ this.signer,
1223
+ rawPayload,
1224
+ requirements,
1225
+ this.authorizerSigner,
1226
+ dataSuffix
1227
+ );
1228
+ }
1229
+ if (isBatchSettlementEnrichedRefundPayload(rawPayload)) {
1230
+ return executeRefundWithSignature(
1231
+ this.signer,
1232
+ rawPayload,
1233
+ requirements,
1234
+ this.authorizerSigner,
1235
+ dataSuffix
1236
+ );
1237
+ }
1238
+ if (isBatchSettlementSettlePayload(rawPayload)) {
1239
+ return executeSettle(this.signer, rawPayload, requirements, dataSuffix);
1240
+ }
1241
+ return {
1242
+ success: false,
1243
+ errorReason: ErrInvalidPayloadType,
1244
+ transaction: "",
1245
+ network: requirements.network
1246
+ };
1247
+ }
1248
+ };
1249
+ export {
1250
+ BatchSettlementEvmScheme
1251
+ };
1252
+ //# sourceMappingURL=index.mjs.map