@net-protocol/relay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +428 -0
- package/dist/index.d.mts +458 -0
- package/dist/index.d.ts +458 -0
- package/dist/index.js +564 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +543 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import { keccak256, toBytes } from 'viem';
|
|
2
|
+
import { waitForTransactionReceipt } from 'viem/actions';
|
|
3
|
+
import { x402Client, wrapFetchWithPayment, x402HTTPClient } from '@x402/fetch';
|
|
4
|
+
import { registerExactEvmScheme } from '@x402/evm/exact/client';
|
|
5
|
+
|
|
6
|
+
// src/session.ts
|
|
7
|
+
|
|
8
|
+
// src/constants.ts
|
|
9
|
+
var RELAY_DOMAIN_NAME = "Net Relay Service";
|
|
10
|
+
var RELAY_DOMAIN_VERSION = "1";
|
|
11
|
+
var RELAY_SESSION_TYPES = {
|
|
12
|
+
RelaySession: [
|
|
13
|
+
{ name: "operatorAddress", type: "address" },
|
|
14
|
+
{ name: "secretKeyHash", type: "bytes32" },
|
|
15
|
+
{ name: "expiresAt", type: "uint256" }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
19
|
+
maxRetries: 3,
|
|
20
|
+
initialDelay: 1e3,
|
|
21
|
+
maxDelay: 3e4,
|
|
22
|
+
backoffMultiplier: 2
|
|
23
|
+
};
|
|
24
|
+
var DEFAULT_CONFIRMATIONS = 1;
|
|
25
|
+
var DEFAULT_TIMEOUT = 6e4;
|
|
26
|
+
var MAX_TRANSACTIONS_PER_BATCH = 100;
|
|
27
|
+
var MAX_BATCH_SIZE_BYTES = 900 * 1024;
|
|
28
|
+
var MAX_TRANSACTION_SIZE_BYTES = 100 * 1024;
|
|
29
|
+
|
|
30
|
+
// src/session.ts
|
|
31
|
+
async function createRelaySession(params) {
|
|
32
|
+
const { apiUrl, chainId, operatorAddress, secretKey, account, expiresIn } = params;
|
|
33
|
+
const expiresInSeconds = expiresIn || 3600;
|
|
34
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + expiresInSeconds;
|
|
35
|
+
const secretKeyHash = keccak256(toBytes(secretKey));
|
|
36
|
+
const domain = {
|
|
37
|
+
name: RELAY_DOMAIN_NAME,
|
|
38
|
+
version: RELAY_DOMAIN_VERSION,
|
|
39
|
+
chainId
|
|
40
|
+
};
|
|
41
|
+
const message = {
|
|
42
|
+
operatorAddress,
|
|
43
|
+
secretKeyHash,
|
|
44
|
+
expiresAt: BigInt(expiresAt)
|
|
45
|
+
};
|
|
46
|
+
const signature = await account.signTypedData({
|
|
47
|
+
domain,
|
|
48
|
+
types: RELAY_SESSION_TYPES,
|
|
49
|
+
primaryType: "RelaySession",
|
|
50
|
+
message
|
|
51
|
+
});
|
|
52
|
+
const response = await fetch(`${apiUrl}/api/relay/session`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json"
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
chainId,
|
|
59
|
+
operatorAddress,
|
|
60
|
+
secretKey,
|
|
61
|
+
signature,
|
|
62
|
+
expiresAt
|
|
63
|
+
// Send the exact expiresAt that was signed
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorData = await response.json();
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Session creation failed: ${response.status} ${errorData.error || response.statusText}`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const result = await response.json();
|
|
73
|
+
if (!result.success || "error" in result) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Session creation failed: ${("error" in result ? result.error : null) || "Unknown error"}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (!result.sessionToken || !result.expiresAt) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"Session creation failed: Missing sessionToken or expiresAt in response"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
sessionToken: result.sessionToken,
|
|
85
|
+
expiresAt: result.expiresAt
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/balance.ts
|
|
90
|
+
async function checkBackendWalletBalance(params) {
|
|
91
|
+
const { apiUrl, chainId, operatorAddress, secretKey } = params;
|
|
92
|
+
const response = await fetch(`${apiUrl}/api/relay/balance`, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json"
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
chainId,
|
|
99
|
+
operatorAddress,
|
|
100
|
+
secretKey
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const errorData = await response.json();
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Balance check endpoint failed: ${response.status} ${JSON.stringify(
|
|
107
|
+
errorData
|
|
108
|
+
)}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const result = await response.json();
|
|
112
|
+
if (!result.success || "error" in result) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Balance check failed: ${("error" in result ? result.error : null) || "Unknown error"}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
backendWalletAddress: result.backendWalletAddress,
|
|
119
|
+
balanceWei: result.balanceWei,
|
|
120
|
+
balanceEth: result.balanceEth,
|
|
121
|
+
sufficientBalance: result.sufficientBalance,
|
|
122
|
+
minRequiredWei: result.minRequiredWei,
|
|
123
|
+
minRequiredEth: result.minRequiredEth
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/fund.ts
|
|
128
|
+
function extractPaymentTxHash(response, httpClient) {
|
|
129
|
+
try {
|
|
130
|
+
const paymentResponse = httpClient.getPaymentSettleResponse(
|
|
131
|
+
(name) => response.headers.get(name)
|
|
132
|
+
);
|
|
133
|
+
return paymentResponse?.transaction || paymentResponse?.txHash || null;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error("Failed to extract payment transaction hash:", error);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function fundBackendWallet(params) {
|
|
140
|
+
const {
|
|
141
|
+
apiUrl,
|
|
142
|
+
operatorAddress,
|
|
143
|
+
secretKey,
|
|
144
|
+
fetchWithPayment,
|
|
145
|
+
httpClient,
|
|
146
|
+
chainId
|
|
147
|
+
} = params;
|
|
148
|
+
const fundUrl = `${apiUrl}/api/relay/${chainId}/fund`;
|
|
149
|
+
console.log("\u{1F4B0} Funding backend wallet", {
|
|
150
|
+
url: fundUrl,
|
|
151
|
+
chainId,
|
|
152
|
+
operatorAddress,
|
|
153
|
+
facilitator: chainId === 8453 ? "Coinbase CDP (Base Mainnet)" : chainId === 84532 ? "x402.org (Base Sepolia)" : "unknown"
|
|
154
|
+
});
|
|
155
|
+
let fundResponse;
|
|
156
|
+
try {
|
|
157
|
+
fundResponse = await fetchWithPayment(fundUrl, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: {
|
|
160
|
+
"Content-Type": "application/json"
|
|
161
|
+
},
|
|
162
|
+
body: JSON.stringify({
|
|
163
|
+
chainId,
|
|
164
|
+
operatorAddress,
|
|
165
|
+
secretKey
|
|
166
|
+
})
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("\u274C Fund request failed", {
|
|
170
|
+
error: error instanceof Error ? error.message : String(error),
|
|
171
|
+
chainId,
|
|
172
|
+
url: fundUrl
|
|
173
|
+
});
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
const fundData = await fundResponse.json();
|
|
177
|
+
if (fundResponse.status === 402) {
|
|
178
|
+
if ("payer" in fundData && fundData.payer) ; else if ("success" in fundData && fundData.success) ; else {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Fund endpoint returned 402 Payment Required: ${JSON.stringify(
|
|
181
|
+
fundData
|
|
182
|
+
)}`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
} else if (!fundResponse.ok) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Fund endpoint failed: ${fundResponse.status} ${JSON.stringify(fundData)}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const paymentTxHash = extractPaymentTxHash(fundResponse, httpClient);
|
|
191
|
+
if (!paymentTxHash) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
"Failed to extract payment transaction hash from payment response headers"
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
197
|
+
const verifyFundResponse = await fetch(`${apiUrl}/api/relay/fund/verify`, {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: {
|
|
200
|
+
"Content-Type": "application/json"
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
chainId,
|
|
204
|
+
paymentTxHash,
|
|
205
|
+
operatorAddress,
|
|
206
|
+
secretKey
|
|
207
|
+
})
|
|
208
|
+
});
|
|
209
|
+
if (!verifyFundResponse.ok) {
|
|
210
|
+
const errorData = await verifyFundResponse.json();
|
|
211
|
+
throw new Error(
|
|
212
|
+
`Fund verify endpoint failed: ${verifyFundResponse.status} ${JSON.stringify(errorData)}`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
const verifyData = await verifyFundResponse.json();
|
|
216
|
+
console.log("\u2713 Payment verified and backend wallet funded", {
|
|
217
|
+
backendWalletAddress: verifyData.backendWalletAddress
|
|
218
|
+
});
|
|
219
|
+
if (!verifyData.success) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Fund verify failed: ${verifyData.error || "Unknown error"}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (!verifyData.backendWalletAddress) {
|
|
225
|
+
throw new Error("Backend wallet address not found in verify response");
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
paymentTxHash,
|
|
229
|
+
backendWalletAddress: verifyData.backendWalletAddress
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/submit.ts
|
|
234
|
+
async function submitTransactionsViaRelay(params) {
|
|
235
|
+
const {
|
|
236
|
+
apiUrl,
|
|
237
|
+
chainId,
|
|
238
|
+
operatorAddress,
|
|
239
|
+
secretKey,
|
|
240
|
+
transactions,
|
|
241
|
+
sessionToken
|
|
242
|
+
} = params;
|
|
243
|
+
const response = await fetch(`${apiUrl}/api/relay/submit`, {
|
|
244
|
+
method: "POST",
|
|
245
|
+
headers: {
|
|
246
|
+
"Content-Type": "application/json"
|
|
247
|
+
},
|
|
248
|
+
body: JSON.stringify({
|
|
249
|
+
chainId,
|
|
250
|
+
operatorAddress,
|
|
251
|
+
secretKey,
|
|
252
|
+
transactions: transactions.map((tx) => ({
|
|
253
|
+
...tx,
|
|
254
|
+
// Only include value if it exists and is > 0
|
|
255
|
+
...tx.value !== void 0 && tx.value > BigInt(0) ? { value: tx.value.toString() } : {}
|
|
256
|
+
})),
|
|
257
|
+
sessionToken
|
|
258
|
+
})
|
|
259
|
+
});
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
const errorData = await response.json();
|
|
262
|
+
throw new Error(
|
|
263
|
+
`Relay submit endpoint failed: ${response.status} ${JSON.stringify(
|
|
264
|
+
errorData
|
|
265
|
+
)}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
const result = await response.json();
|
|
269
|
+
if (!result.success || "error" in result) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`Relay submit failed: ${("error" in result ? result.error : null) || "Unknown error"}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
transactionHashes: result.transactionHashes.map((h) => h),
|
|
276
|
+
successfulIndexes: result.successfulIndexes || [],
|
|
277
|
+
failedIndexes: result.failedIndexes || [],
|
|
278
|
+
errors: result.errors || [],
|
|
279
|
+
backendWalletAddress: result.backendWalletAddress,
|
|
280
|
+
appFeeTransactionHash: result.appFeeTransactionHash
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/retry.ts
|
|
285
|
+
function sleep(ms) {
|
|
286
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
287
|
+
}
|
|
288
|
+
function calculateDelay(attempt, config) {
|
|
289
|
+
const delay = config.initialDelay * Math.pow(config.backoffMultiplier, attempt);
|
|
290
|
+
return Math.min(delay, config.maxDelay);
|
|
291
|
+
}
|
|
292
|
+
async function retryFailedTransactions(params) {
|
|
293
|
+
const {
|
|
294
|
+
apiUrl,
|
|
295
|
+
chainId,
|
|
296
|
+
operatorAddress,
|
|
297
|
+
secretKey,
|
|
298
|
+
failedIndexes: initialFailedIndexes,
|
|
299
|
+
originalTransactions,
|
|
300
|
+
backendWalletAddress,
|
|
301
|
+
config = {},
|
|
302
|
+
sessionToken,
|
|
303
|
+
recheckFunction
|
|
304
|
+
} = params;
|
|
305
|
+
const retryConfig = {
|
|
306
|
+
...DEFAULT_RETRY_CONFIG,
|
|
307
|
+
...config
|
|
308
|
+
};
|
|
309
|
+
let failedIndexes = initialFailedIndexes;
|
|
310
|
+
let attempt = 0;
|
|
311
|
+
let lastResult = null;
|
|
312
|
+
while (failedIndexes.length > 0 && attempt < retryConfig.maxRetries) {
|
|
313
|
+
attempt++;
|
|
314
|
+
if (recheckFunction) {
|
|
315
|
+
failedIndexes = await recheckFunction(
|
|
316
|
+
failedIndexes,
|
|
317
|
+
originalTransactions,
|
|
318
|
+
backendWalletAddress
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if (failedIndexes.length === 0) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
const delay = calculateDelay(attempt - 1, retryConfig);
|
|
325
|
+
if (attempt > 1) {
|
|
326
|
+
await sleep(delay);
|
|
327
|
+
}
|
|
328
|
+
const failedTransactions = failedIndexes.map(
|
|
329
|
+
(idx) => originalTransactions[idx]
|
|
330
|
+
);
|
|
331
|
+
try {
|
|
332
|
+
const retryResult = await submitTransactionsViaRelay({
|
|
333
|
+
apiUrl,
|
|
334
|
+
chainId,
|
|
335
|
+
operatorAddress,
|
|
336
|
+
secretKey,
|
|
337
|
+
transactions: failedTransactions,
|
|
338
|
+
sessionToken
|
|
339
|
+
});
|
|
340
|
+
if (lastResult) {
|
|
341
|
+
lastResult.transactionHashes.push(...retryResult.transactionHashes);
|
|
342
|
+
lastResult.successfulIndexes.push(
|
|
343
|
+
...retryResult.successfulIndexes.map((idx) => failedIndexes[idx])
|
|
344
|
+
);
|
|
345
|
+
lastResult.failedIndexes = retryResult.failedIndexes.map(
|
|
346
|
+
(idx) => failedIndexes[idx]
|
|
347
|
+
);
|
|
348
|
+
lastResult.errors.push(
|
|
349
|
+
...retryResult.errors.map((err) => ({
|
|
350
|
+
index: failedIndexes[err.index],
|
|
351
|
+
error: err.error
|
|
352
|
+
}))
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
lastResult = {
|
|
356
|
+
...retryResult,
|
|
357
|
+
successfulIndexes: retryResult.successfulIndexes.map(
|
|
358
|
+
(idx) => failedIndexes[idx]
|
|
359
|
+
),
|
|
360
|
+
failedIndexes: retryResult.failedIndexes.map(
|
|
361
|
+
(idx) => failedIndexes[idx]
|
|
362
|
+
),
|
|
363
|
+
errors: retryResult.errors.map((err) => ({
|
|
364
|
+
index: failedIndexes[err.index],
|
|
365
|
+
error: err.error
|
|
366
|
+
}))
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
failedIndexes = retryResult.failedIndexes.map(
|
|
370
|
+
(idx) => failedIndexes[idx]
|
|
371
|
+
);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
console.error(`Retry attempt ${attempt} failed:`, error);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (!lastResult) {
|
|
377
|
+
throw new Error("Retry failed: No result after retries");
|
|
378
|
+
}
|
|
379
|
+
return lastResult;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/batch.ts
|
|
383
|
+
function estimateTransactionSize(tx) {
|
|
384
|
+
const argsSize = tx.args ? JSON.stringify(tx.args).length : 0;
|
|
385
|
+
return Math.min(
|
|
386
|
+
argsSize + 1024,
|
|
387
|
+
// args + overhead
|
|
388
|
+
MAX_TRANSACTION_SIZE_BYTES
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
function estimateRequestSize(transactions) {
|
|
392
|
+
const baseOverhead = 300;
|
|
393
|
+
const transactionsSize = transactions.reduce(
|
|
394
|
+
(sum, tx) => sum + estimateTransactionSize(tx),
|
|
395
|
+
0
|
|
396
|
+
);
|
|
397
|
+
return baseOverhead + transactionsSize;
|
|
398
|
+
}
|
|
399
|
+
function batchTransactions(transactions) {
|
|
400
|
+
const batches = [];
|
|
401
|
+
let currentBatch = [];
|
|
402
|
+
for (const tx of transactions) {
|
|
403
|
+
const batchWithTx = [...currentBatch, tx];
|
|
404
|
+
const estimatedSize = estimateRequestSize(batchWithTx);
|
|
405
|
+
if (currentBatch.length >= MAX_TRANSACTIONS_PER_BATCH || estimatedSize > MAX_BATCH_SIZE_BYTES) {
|
|
406
|
+
if (currentBatch.length > 0) {
|
|
407
|
+
batches.push(currentBatch);
|
|
408
|
+
currentBatch = [];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
currentBatch.push(tx);
|
|
412
|
+
}
|
|
413
|
+
if (currentBatch.length > 0) {
|
|
414
|
+
batches.push(currentBatch);
|
|
415
|
+
}
|
|
416
|
+
return batches;
|
|
417
|
+
}
|
|
418
|
+
async function waitForConfirmations(params) {
|
|
419
|
+
const {
|
|
420
|
+
publicClient,
|
|
421
|
+
transactionHashes,
|
|
422
|
+
confirmations = DEFAULT_CONFIRMATIONS,
|
|
423
|
+
timeout = DEFAULT_TIMEOUT,
|
|
424
|
+
onProgress
|
|
425
|
+
} = params;
|
|
426
|
+
if (transactionHashes.length === 0) {
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
let confirmed = 0;
|
|
430
|
+
const promises = transactionHashes.map(async (hash) => {
|
|
431
|
+
try {
|
|
432
|
+
const receipt = await waitForTransactionReceipt(publicClient, {
|
|
433
|
+
hash,
|
|
434
|
+
confirmations,
|
|
435
|
+
timeout
|
|
436
|
+
});
|
|
437
|
+
confirmed++;
|
|
438
|
+
if (onProgress) {
|
|
439
|
+
onProgress(confirmed, transactionHashes.length);
|
|
440
|
+
}
|
|
441
|
+
return { hash, receipt };
|
|
442
|
+
} catch (error) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`Transaction ${hash} failed or timed out: ${error instanceof Error ? error.message : String(error)}`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
const receipts = await Promise.all(promises);
|
|
449
|
+
return receipts;
|
|
450
|
+
}
|
|
451
|
+
function createRelayX402Client(account, chainId) {
|
|
452
|
+
const client = new x402Client();
|
|
453
|
+
registerExactEvmScheme(client, { signer: account });
|
|
454
|
+
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
|
|
455
|
+
const httpClient = new x402HTTPClient(client);
|
|
456
|
+
return { fetchWithPayment, httpClient };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/client/RelayClient.ts
|
|
460
|
+
var RelayClient = class {
|
|
461
|
+
constructor(options) {
|
|
462
|
+
this.apiUrl = options.apiUrl;
|
|
463
|
+
this.chainId = options.chainId;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Create a relay session token
|
|
467
|
+
*
|
|
468
|
+
* @param params - Session creation parameters (apiUrl and chainId are already set)
|
|
469
|
+
* @returns Session token and expiration timestamp
|
|
470
|
+
*/
|
|
471
|
+
async createSession(params) {
|
|
472
|
+
return createRelaySession({
|
|
473
|
+
apiUrl: this.apiUrl,
|
|
474
|
+
chainId: this.chainId,
|
|
475
|
+
...params
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Check backend wallet balance
|
|
480
|
+
*
|
|
481
|
+
* @param params - Balance check parameters (apiUrl and chainId are already set)
|
|
482
|
+
* @returns Result with balance information
|
|
483
|
+
*/
|
|
484
|
+
async checkBalance(params) {
|
|
485
|
+
return checkBackendWalletBalance({
|
|
486
|
+
apiUrl: this.apiUrl,
|
|
487
|
+
chainId: this.chainId,
|
|
488
|
+
...params
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Fund backend wallet via x402 payment
|
|
493
|
+
*
|
|
494
|
+
* @param params - Funding parameters (apiUrl and chainId are already set)
|
|
495
|
+
* @returns Result with paymentTxHash and backendWalletAddress
|
|
496
|
+
*/
|
|
497
|
+
async fundBackendWallet(params) {
|
|
498
|
+
return fundBackendWallet({
|
|
499
|
+
apiUrl: this.apiUrl,
|
|
500
|
+
chainId: this.chainId,
|
|
501
|
+
...params
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Submit transactions via relay
|
|
506
|
+
*
|
|
507
|
+
* @param params - Submission parameters (apiUrl and chainId are already set)
|
|
508
|
+
* @returns Result with transaction hashes and success/failure tracking
|
|
509
|
+
*/
|
|
510
|
+
async submitTransactions(params) {
|
|
511
|
+
return submitTransactionsViaRelay({
|
|
512
|
+
apiUrl: this.apiUrl,
|
|
513
|
+
chainId: this.chainId,
|
|
514
|
+
...params
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Retry failed transactions with exponential backoff
|
|
519
|
+
*
|
|
520
|
+
* @param params - Retry parameters (apiUrl and chainId are already set)
|
|
521
|
+
* @returns Final success/failure status after retries
|
|
522
|
+
*/
|
|
523
|
+
async retryFailedTransactions(params) {
|
|
524
|
+
return retryFailedTransactions({
|
|
525
|
+
apiUrl: this.apiUrl,
|
|
526
|
+
chainId: this.chainId,
|
|
527
|
+
...params
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Create x402 client for relay payments
|
|
532
|
+
*
|
|
533
|
+
* @param account - User's local account
|
|
534
|
+
* @returns Object with fetchWithPayment and httpClient
|
|
535
|
+
*/
|
|
536
|
+
createX402Client(account) {
|
|
537
|
+
return createRelayX402Client(account, this.chainId);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
export { DEFAULT_CONFIRMATIONS, DEFAULT_RETRY_CONFIG, DEFAULT_TIMEOUT, MAX_BATCH_SIZE_BYTES, MAX_TRANSACTIONS_PER_BATCH, MAX_TRANSACTION_SIZE_BYTES, RELAY_DOMAIN_NAME, RELAY_DOMAIN_VERSION, RELAY_SESSION_TYPES, RelayClient, batchTransactions, checkBackendWalletBalance, createRelaySession, createRelayX402Client, estimateRequestSize, estimateTransactionSize, fundBackendWallet, retryFailedTransactions, submitTransactionsViaRelay, waitForConfirmations };
|
|
542
|
+
//# sourceMappingURL=index.mjs.map
|
|
543
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/session.ts","../src/balance.ts","../src/fund.ts","../src/submit.ts","../src/retry.ts","../src/batch.ts","../src/confirmations.ts","../src/client/x402Client.ts","../src/client/RelayClient.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { RetryConfig } from \"./types\";\n\n/**\n * EIP-712 domain name for relay session signing\n */\nexport const RELAY_DOMAIN_NAME = \"Net Relay Service\";\n\n/**\n * EIP-712 domain version for relay session signing\n */\nexport const RELAY_DOMAIN_VERSION = \"1\";\n\n/**\n * EIP-712 types for relay session signing\n */\nexport const RELAY_SESSION_TYPES = {\n RelaySession: [\n { name: \"operatorAddress\", type: \"address\" },\n { name: \"secretKeyHash\", type: \"bytes32\" },\n { name: \"expiresAt\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * Default retry configuration\n */\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n};\n\n/**\n * Default number of confirmations to wait for\n */\nexport const DEFAULT_CONFIRMATIONS = 1;\n\n/**\n * Default timeout for waiting for transaction confirmations (milliseconds)\n */\nexport const DEFAULT_TIMEOUT = 60000; // 60 seconds\n\n/**\n * Maximum transactions per batch (matches server limit)\n */\nexport const MAX_TRANSACTIONS_PER_BATCH = 100;\n\n/**\n * Maximum request size in bytes (slightly under 1MB for safety margin)\n */\nexport const MAX_BATCH_SIZE_BYTES = 900 * 1024; // 900KB\n\n/**\n * Maximum size per transaction in bytes\n * Storage transactions can include large data chunks, so we assume up to 100KB per transaction\n */\nexport const MAX_TRANSACTION_SIZE_BYTES = 100 * 1024; // 100KB\n\n","import { keccak256, toBytes } from \"viem\";\nimport type { LocalAccount, Address } from \"viem/accounts\";\nimport {\n RELAY_DOMAIN_NAME,\n RELAY_DOMAIN_VERSION,\n RELAY_SESSION_TYPES,\n} from \"./constants\";\nimport type { CreateSessionResponse, ErrorResponse } from \"./types\";\n\n/**\n * Create a relay session token\n *\n * Signs an EIP-712 message proving ownership of operatorAddress\n * and receives a sessionToken that can be reused for multiple batch requests.\n *\n * @param params - Session creation parameters\n * @returns Session token and expiration timestamp\n * @throws Error if session creation fails\n */\nexport async function createRelaySession(params: {\n apiUrl: string;\n chainId: number;\n operatorAddress: Address;\n secretKey: string;\n account: LocalAccount;\n expiresIn?: number; // seconds, default 3600\n}): Promise<{ sessionToken: string; expiresAt: number }> {\n const { apiUrl, chainId, operatorAddress, secretKey, account, expiresIn } =\n params;\n\n const expiresInSeconds = expiresIn || 3600; // Default 1 hour\n const expiresAt = Math.floor(Date.now() / 1000) + expiresInSeconds;\n const secretKeyHash = keccak256(toBytes(secretKey));\n\n const domain = {\n name: RELAY_DOMAIN_NAME,\n version: RELAY_DOMAIN_VERSION,\n chainId,\n };\n\n const message = {\n operatorAddress,\n secretKeyHash,\n expiresAt: BigInt(expiresAt),\n };\n\n // Sign the typed data using account's signTypedData method\n const signature = await account.signTypedData({\n domain,\n types: RELAY_SESSION_TYPES,\n primaryType: \"RelaySession\",\n message,\n });\n\n // Call session endpoint (send expiresAt - the exact value that was signed)\n const response = await fetch(`${apiUrl}/api/relay/session`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n chainId,\n operatorAddress,\n secretKey,\n signature,\n expiresAt, // Send the exact expiresAt that was signed\n }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json()) as ErrorResponse;\n throw new Error(\n `Session creation failed: ${response.status} ${\n errorData.error || response.statusText\n }`\n );\n }\n\n const result = (await response.json()) as\n | CreateSessionResponse\n | ErrorResponse;\n if (!result.success || \"error\" in result) {\n throw new Error(\n `Session creation failed: ${\n (\"error\" in result ? result.error : null) || \"Unknown error\"\n }`\n );\n }\n\n if (!result.sessionToken || !result.expiresAt) {\n throw new Error(\n \"Session creation failed: Missing sessionToken or expiresAt in response\"\n );\n }\n\n return {\n sessionToken: result.sessionToken,\n expiresAt: result.expiresAt,\n };\n}\n\n","import type {\n CheckBackendWalletBalanceParams,\n CheckBalanceResult,\n BalanceResponse,\n ErrorResponse,\n} from \"./types\";\n\n/**\n * Check backend wallet balance via relay service\n *\n * This function:\n * 1. Calls /api/relay/balance with operatorAddress and secretKey\n * 2. Returns balance information including sufficientBalance flag\n *\n * @param params - Balance check parameters\n * @returns Result with balance information\n * @throws Error if balance check fails\n */\nexport async function checkBackendWalletBalance(\n params: CheckBackendWalletBalanceParams\n): Promise<CheckBalanceResult> {\n const { apiUrl, chainId, operatorAddress, secretKey } = params;\n\n const response = await fetch(`${apiUrl}/api/relay/balance`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n chainId,\n operatorAddress,\n secretKey,\n }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json()) as ErrorResponse;\n throw new Error(\n `Balance check endpoint failed: ${response.status} ${JSON.stringify(\n errorData\n )}`\n );\n }\n\n const result = (await response.json()) as BalanceResponse | ErrorResponse;\n if (!result.success || \"error\" in result) {\n throw new Error(\n `Balance check failed: ${(\"error\" in result ? result.error : null) || \"Unknown error\"}`\n );\n }\n\n return {\n backendWalletAddress: result.backendWalletAddress,\n balanceWei: result.balanceWei,\n balanceEth: result.balanceEth,\n sufficientBalance: result.sufficientBalance,\n minRequiredWei: result.minRequiredWei,\n minRequiredEth: result.minRequiredEth,\n };\n}\n\n","import type { Hash } from \"viem\";\nimport type {\n FundBackendWalletParams,\n RelayFundResult,\n FundResponse,\n VerifyFundResponse,\n ErrorResponse,\n} from \"./types\";\n\n/**\n * Extract payment transaction hash from response headers\n */\nfunction extractPaymentTxHash(\n response: Response,\n httpClient: FundBackendWalletParams[\"httpClient\"]\n): string | null {\n try {\n const paymentResponse = httpClient.getPaymentSettleResponse(\n (name: string) => response.headers.get(name)\n );\n return paymentResponse?.transaction || paymentResponse?.txHash || null;\n } catch (error) {\n console.error(\"Failed to extract payment transaction hash:\", error);\n return null;\n }\n}\n\n/**\n * Fund backend wallet via x402 relay service\n *\n * This function:\n * 1. Calls /api/relay/[chainId]/fund with x402 payment (using fetchWithPayment)\n * 2. Extracts paymentTxHash from X-PAYMENT-RESPONSE header\n * 3. Waits for payment confirmation (2 seconds)\n * 4. Calls /api/relay/fund/verify to get backendWalletAddress\n * 5. Returns { paymentTxHash, backendWalletAddress }\n *\n * @param params - Funding parameters\n * @returns Result with paymentTxHash and backendWalletAddress\n * @throws Error if funding fails\n */\nexport async function fundBackendWallet(\n params: FundBackendWalletParams\n): Promise<RelayFundResult> {\n const {\n apiUrl,\n operatorAddress,\n secretKey,\n fetchWithPayment,\n httpClient,\n chainId,\n } = params;\n\n const fundUrl = `${apiUrl}/api/relay/${chainId}/fund`;\n\n console.log(\"💰 Funding backend wallet\", {\n url: fundUrl,\n chainId,\n operatorAddress,\n facilitator:\n chainId === 8453\n ? \"Coinbase CDP (Base Mainnet)\"\n : chainId === 84532\n ? \"x402.org (Base Sepolia)\"\n : \"unknown\",\n });\n\n // Step 1: Call /api/relay/[chainId]/fund (Payment)\n let fundResponse: Response;\n try {\n fundResponse = await fetchWithPayment(fundUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n chainId,\n operatorAddress,\n secretKey,\n }),\n });\n } catch (error) {\n console.error(\"❌ Fund request failed\", {\n error: error instanceof Error ? error.message : String(error),\n chainId,\n url: fundUrl,\n });\n throw error;\n }\n\n const fundData = (await fundResponse.json()) as FundResponse | ErrorResponse;\n\n // Handle 402 Payment Required\n if (fundResponse.status === 402) {\n // Check if payment was actually processed despite 402\n if (\"payer\" in fundData && fundData.payer) {\n // Payment appears to have been processed\n } else if (\"success\" in fundData && fundData.success) {\n // Payment appears to have been processed\n } else {\n throw new Error(\n `Fund endpoint returned 402 Payment Required: ${JSON.stringify(\n fundData\n )}`\n );\n }\n } else if (!fundResponse.ok) {\n throw new Error(\n `Fund endpoint failed: ${fundResponse.status} ${JSON.stringify(fundData)}`\n );\n }\n\n // Extract payment transaction hash from response headers\n const paymentTxHash = extractPaymentTxHash(fundResponse, httpClient);\n\n if (!paymentTxHash) {\n throw new Error(\n \"Failed to extract payment transaction hash from payment response headers\"\n );\n }\n\n // Step 2: Wait for payment confirmation\n await new Promise((resolve) => setTimeout(resolve, 2000));\n\n // Step 3: Call /api/relay/fund/verify (Verification & Funding)\n const verifyFundResponse = await fetch(`${apiUrl}/api/relay/fund/verify`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n chainId,\n paymentTxHash: paymentTxHash as Hash,\n operatorAddress,\n secretKey,\n }),\n });\n\n if (!verifyFundResponse.ok) {\n const errorData = (await verifyFundResponse.json()) as ErrorResponse;\n throw new Error(\n `Fund verify endpoint failed: ${\n verifyFundResponse.status\n } ${JSON.stringify(errorData)}`\n );\n }\n\n const verifyData = (await verifyFundResponse.json()) as VerifyFundResponse;\n console.log(\"✓ Payment verified and backend wallet funded\", {\n backendWalletAddress: verifyData.backendWalletAddress,\n });\n\n if (!verifyData.success) {\n throw new Error(\n `Fund verify failed: ${verifyData.error || \"Unknown error\"}`\n );\n }\n\n if (!verifyData.backendWalletAddress) {\n throw new Error(\"Backend wallet address not found in verify response\");\n }\n\n return {\n paymentTxHash: paymentTxHash as Hash,\n backendWalletAddress: verifyData.backendWalletAddress,\n };\n}\n\n","import type { Hash, Address } from \"viem\";\nimport type {\n SubmitTransactionsViaRelayParams,\n RelaySubmitResult,\n SubmitResponse,\n ErrorResponse,\n} from \"./types\";\n\n/**\n * Submit transactions via relay service\n *\n * Calls /api/relay/submit with transactions and returns result\n * with success/failure tracking by index.\n *\n * @param params - Submission parameters\n * @returns Result with transaction hashes and success/failure tracking\n * @throws Error if submission fails\n */\nexport async function submitTransactionsViaRelay(\n params: SubmitTransactionsViaRelayParams\n): Promise<RelaySubmitResult> {\n const {\n apiUrl,\n chainId,\n operatorAddress,\n secretKey,\n transactions,\n sessionToken,\n } = params;\n\n const response = await fetch(`${apiUrl}/api/relay/submit`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n chainId,\n operatorAddress,\n secretKey,\n transactions: transactions.map((tx) => ({\n ...tx,\n // Only include value if it exists and is > 0\n ...(tx.value !== undefined && tx.value > BigInt(0)\n ? { value: tx.value.toString() }\n : {}),\n })),\n sessionToken,\n }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json()) as ErrorResponse;\n throw new Error(\n `Relay submit endpoint failed: ${response.status} ${JSON.stringify(\n errorData\n )}`\n );\n }\n\n const result = (await response.json()) as SubmitResponse | ErrorResponse;\n if (!result.success || \"error\" in result) {\n throw new Error(\n `Relay submit failed: ${\n (\"error\" in result ? result.error : null) || \"Unknown error\"\n }`\n );\n }\n\n return {\n transactionHashes: result.transactionHashes.map((h: string) => h as Hash),\n successfulIndexes: result.successfulIndexes || [],\n failedIndexes: result.failedIndexes || [],\n errors: result.errors || [],\n backendWalletAddress: result.backendWalletAddress as Address,\n appFeeTransactionHash: result.appFeeTransactionHash as Hash,\n };\n}\n\n","import type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport type { Address } from \"viem\";\nimport type {\n RetryFailedTransactionsParams,\n RetryConfig,\n RelaySubmitResult,\n} from \"./types\";\nimport { DEFAULT_RETRY_CONFIG } from \"./constants\";\nimport { submitTransactionsViaRelay } from \"./submit\";\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate delay for exponential backoff\n */\nfunction calculateDelay(\n attempt: number,\n config: Required<RetryConfig>\n): number {\n const delay =\n config.initialDelay * Math.pow(config.backoffMultiplier, attempt);\n return Math.min(delay, config.maxDelay);\n}\n\n/**\n * Retry failed transactions with exponential backoff\n *\n * This function:\n * 1. Extracts failed transactions by index from RelaySubmitResult\n * 2. Optionally re-checks on-chain before retry (via recheckFunction)\n * 3. Retries with exponential backoff\n * 4. Handles nested retries\n *\n * @param params - Retry parameters\n * @returns Final success/failure status after retries\n */\nexport async function retryFailedTransactions(\n params: RetryFailedTransactionsParams\n): Promise<RelaySubmitResult> {\n const {\n apiUrl,\n chainId,\n operatorAddress,\n secretKey,\n failedIndexes: initialFailedIndexes,\n originalTransactions,\n backendWalletAddress,\n config = {},\n sessionToken,\n recheckFunction,\n } = params;\n\n const retryConfig: Required<RetryConfig> = {\n ...DEFAULT_RETRY_CONFIG,\n ...config,\n };\n\n let failedIndexes = initialFailedIndexes;\n let attempt = 0;\n let lastResult: RelaySubmitResult | null = null;\n\n while (failedIndexes.length > 0 && attempt < retryConfig.maxRetries) {\n attempt++;\n\n // Re-check on-chain before retry (if recheckFunction provided)\n if (recheckFunction) {\n failedIndexes = await recheckFunction(\n failedIndexes,\n originalTransactions,\n backendWalletAddress\n );\n }\n\n if (failedIndexes.length === 0) {\n // All transactions have succeeded\n break;\n }\n\n // Calculate delay for exponential backoff\n const delay = calculateDelay(attempt - 1, retryConfig);\n if (attempt > 1) {\n // Don't delay on first attempt\n await sleep(delay);\n }\n\n // Extract failed transactions\n const failedTransactions = failedIndexes.map(\n (idx) => originalTransactions[idx]\n );\n\n // Retry failed transactions\n try {\n const retryResult = await submitTransactionsViaRelay({\n apiUrl,\n chainId,\n operatorAddress,\n secretKey,\n transactions: failedTransactions,\n sessionToken,\n });\n\n // Merge results\n if (lastResult) {\n // Combine transaction hashes\n lastResult.transactionHashes.push(...retryResult.transactionHashes);\n // Combine successful indexes (adjust for original index)\n lastResult.successfulIndexes.push(\n ...retryResult.successfulIndexes.map((idx) => failedIndexes[idx])\n );\n // Update failed indexes\n lastResult.failedIndexes = retryResult.failedIndexes.map(\n (idx) => failedIndexes[idx]\n );\n // Combine errors\n lastResult.errors.push(\n ...retryResult.errors.map((err) => ({\n index: failedIndexes[err.index],\n error: err.error,\n }))\n );\n } else {\n lastResult = {\n ...retryResult,\n successfulIndexes: retryResult.successfulIndexes.map(\n (idx) => failedIndexes[idx]\n ),\n failedIndexes: retryResult.failedIndexes.map(\n (idx) => failedIndexes[idx]\n ),\n errors: retryResult.errors.map((err) => ({\n index: failedIndexes[err.index],\n error: err.error,\n })),\n };\n }\n\n // Update failed indexes for next iteration\n failedIndexes = retryResult.failedIndexes.map(\n (idx) => failedIndexes[idx]\n );\n } catch (error) {\n // Retry failed, keep current failed indexes\n console.error(`Retry attempt ${attempt} failed:`, error);\n }\n }\n\n if (!lastResult) {\n throw new Error(\"Retry failed: No result after retries\");\n }\n\n return lastResult;\n}\n\n","import type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport {\n MAX_TRANSACTIONS_PER_BATCH,\n MAX_BATCH_SIZE_BYTES,\n MAX_TRANSACTION_SIZE_BYTES,\n} from \"./constants\";\n\n/**\n * Estimate the size of a serialized transaction config in bytes\n *\n * Storage transactions can be large due to:\n * - Large args arrays (chunk data)\n * - Hex-encoded data in args\n * - ABI structure overhead\n *\n * We use a conservative estimate of 100KB per transaction to ensure we stay under the 1MB limit.\n * With 100KB per transaction, we can fit ~9 transactions per batch (900KB / 100KB).\n */\nexport function estimateTransactionSize(tx: WriteTransactionConfig): number {\n // Estimate based on args size\n // Args can contain large hex-encoded data (chunks), so we assume up to 100KB\n // Add overhead for JSON structure, ABI, function name, etc. (~1KB)\n const argsSize = tx.args ? JSON.stringify(tx.args).length : 0;\n\n // Use the larger of: actual args size or conservative estimate\n // Cap at MAX_TRANSACTION_SIZE_BYTES to prevent single transaction from exceeding batch limit\n return Math.min(\n argsSize + 1024, // args + overhead\n MAX_TRANSACTION_SIZE_BYTES\n );\n}\n\n/**\n * Estimate the total request size for a batch of transactions\n */\nexport function estimateRequestSize(\n transactions: WriteTransactionConfig[]\n): number {\n // Base overhead: operatorAddress (~42 bytes),\n // secretKey (~50-200 bytes), JSON structure (~100 bytes)\n const baseOverhead = 300;\n const transactionsSize = transactions.reduce(\n (sum, tx) => sum + estimateTransactionSize(tx),\n 0\n );\n return baseOverhead + transactionsSize;\n}\n\n/**\n * Batch transactions into groups that respect count and size limits\n *\n * With 100KB per transaction estimate:\n * - Size limit: ~9 transactions per batch (900KB / 100KB)\n * - Count limit: 100 transactions per batch\n * - Size limit is the constraining factor for large transactions\n */\nexport function batchTransactions(\n transactions: WriteTransactionConfig[]\n): WriteTransactionConfig[][] {\n const batches: WriteTransactionConfig[][] = [];\n let currentBatch: WriteTransactionConfig[] = [];\n\n for (const tx of transactions) {\n // Calculate what the batch size would be with this transaction added\n const batchWithTx = [...currentBatch, tx];\n const estimatedSize = estimateRequestSize(batchWithTx);\n\n // Check if adding this transaction would exceed limits\n // Size limit is typically the constraining factor (9 transactions @ 100KB each)\n if (\n currentBatch.length >= MAX_TRANSACTIONS_PER_BATCH ||\n estimatedSize > MAX_BATCH_SIZE_BYTES\n ) {\n // Start a new batch\n if (currentBatch.length > 0) {\n batches.push(currentBatch);\n currentBatch = [];\n }\n }\n\n // If a single transaction exceeds the batch size limit, we still add it\n // (it will be its own batch, and the server will reject it with a clear error)\n currentBatch.push(tx);\n }\n\n // Add the last batch if it has transactions\n if (currentBatch.length > 0) {\n batches.push(currentBatch);\n }\n\n return batches;\n}\n\n","import { waitForTransactionReceipt } from \"viem/actions\";\nimport type { Hash } from \"viem\";\nimport type {\n WaitForConfirmationsParams,\n ConfirmationResult,\n} from \"./types\";\nimport { DEFAULT_CONFIRMATIONS, DEFAULT_TIMEOUT } from \"./constants\";\n\n/**\n * Wait for transaction confirmations\n *\n * Uses waitForTransactionReceipt() from viem/actions to wait for\n * multiple transactions in parallel. Handles timeouts and tracks progress.\n *\n * @param params - Confirmation parameters\n * @returns Array of transaction receipts\n * @throws Error if timeout occurs or transaction fails\n */\nexport async function waitForConfirmations(\n params: WaitForConfirmationsParams\n): Promise<ConfirmationResult[]> {\n const {\n publicClient,\n transactionHashes,\n confirmations = DEFAULT_CONFIRMATIONS,\n timeout = DEFAULT_TIMEOUT,\n onProgress,\n } = params;\n\n if (transactionHashes.length === 0) {\n return [];\n }\n\n const results: ConfirmationResult[] = [];\n let confirmed = 0;\n\n // Wait for all transactions in parallel\n const promises = transactionHashes.map(async (hash) => {\n try {\n const receipt = await waitForTransactionReceipt(publicClient, {\n hash,\n confirmations,\n timeout,\n });\n\n confirmed++;\n if (onProgress) {\n onProgress(confirmed, transactionHashes.length);\n }\n\n return { hash, receipt };\n } catch (error) {\n // Transaction failed or timed out\n throw new Error(\n `Transaction ${hash} failed or timed out: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n });\n\n // Wait for all promises\n const receipts = await Promise.all(promises);\n\n return receipts;\n}\n\n","import { x402Client, wrapFetchWithPayment, x402HTTPClient } from \"@x402/fetch\";\nimport { registerExactEvmScheme } from \"@x402/evm/exact/client\";\nimport type { LocalAccount } from \"viem/accounts\";\nimport type { X402ClientResult } from \"../types\";\n\n/**\n * Create x402 client for relay payments\n *\n * Sets up x402Client with user's account, registers EVM scheme,\n * and returns wrapped fetch function and HTTP client for header extraction.\n *\n * @param account - User's local account (from privateKeyToAccount)\n * @param chainId - Chain ID (optional, for logging purposes)\n * @returns Object with fetchWithPayment and httpClient\n */\nexport function createRelayX402Client(\n account: LocalAccount,\n chainId?: number\n): X402ClientResult {\n const client = new x402Client();\n registerExactEvmScheme(client, { signer: account });\n const fetchWithPayment = wrapFetchWithPayment(fetch, client);\n const httpClient = new x402HTTPClient(client);\n\n return { fetchWithPayment, httpClient };\n}\n\n","import type { LocalAccount, Address } from \"viem/accounts\";\nimport type { PublicClient, Hash } from \"viem\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport { createRelaySession } from \"../session\";\nimport { checkBackendWalletBalance } from \"../balance\";\nimport { fundBackendWallet } from \"../fund\";\nimport { submitTransactionsViaRelay } from \"../submit\";\nimport { retryFailedTransactions } from \"../retry\";\nimport { createRelayX402Client } from \"./x402Client\";\nimport type {\n CheckBalanceResult,\n RelayFundResult,\n RelaySubmitResult,\n RetryFailedTransactionsParams,\n RetryConfig,\n FundBackendWalletParams,\n SubmitTransactionsViaRelayParams,\n} from \"../types\";\n\n/**\n * High-level client for Net Relay Service\n *\n * Provides a convenient class-based API that stores apiUrl and chainId,\n * reducing boilerplate when making multiple relay calls.\n */\nexport class RelayClient {\n private apiUrl: string;\n private chainId: number;\n\n constructor(options: { apiUrl: string; chainId: number }) {\n this.apiUrl = options.apiUrl;\n this.chainId = options.chainId;\n }\n\n /**\n * Create a relay session token\n *\n * @param params - Session creation parameters (apiUrl and chainId are already set)\n * @returns Session token and expiration timestamp\n */\n async createSession(params: {\n operatorAddress: Address;\n secretKey: string;\n account: LocalAccount;\n expiresIn?: number;\n }): Promise<{ sessionToken: string; expiresAt: number }> {\n return createRelaySession({\n apiUrl: this.apiUrl,\n chainId: this.chainId,\n ...params,\n });\n }\n\n /**\n * Check backend wallet balance\n *\n * @param params - Balance check parameters (apiUrl and chainId are already set)\n * @returns Result with balance information\n */\n async checkBalance(params: {\n operatorAddress: Address;\n secretKey: string;\n }): Promise<CheckBalanceResult> {\n return checkBackendWalletBalance({\n apiUrl: this.apiUrl,\n chainId: this.chainId,\n ...params,\n });\n }\n\n /**\n * Fund backend wallet via x402 payment\n *\n * @param params - Funding parameters (apiUrl and chainId are already set)\n * @returns Result with paymentTxHash and backendWalletAddress\n */\n async fundBackendWallet(params: {\n operatorAddress: Address;\n secretKey: string;\n fetchWithPayment: typeof fetch;\n httpClient: FundBackendWalletParams[\"httpClient\"];\n }): Promise<RelayFundResult> {\n return fundBackendWallet({\n apiUrl: this.apiUrl,\n chainId: this.chainId,\n ...params,\n });\n }\n\n /**\n * Submit transactions via relay\n *\n * @param params - Submission parameters (apiUrl and chainId are already set)\n * @returns Result with transaction hashes and success/failure tracking\n */\n async submitTransactions(params: {\n operatorAddress: Address;\n secretKey: string;\n transactions: WriteTransactionConfig[];\n sessionToken: string;\n }): Promise<RelaySubmitResult> {\n return submitTransactionsViaRelay({\n apiUrl: this.apiUrl,\n chainId: this.chainId,\n ...params,\n });\n }\n\n /**\n * Retry failed transactions with exponential backoff\n *\n * @param params - Retry parameters (apiUrl and chainId are already set)\n * @returns Final success/failure status after retries\n */\n async retryFailedTransactions(params: {\n operatorAddress: Address;\n secretKey: string;\n failedIndexes: number[];\n originalTransactions: WriteTransactionConfig[];\n backendWalletAddress: Address;\n config?: RetryConfig;\n sessionToken: string;\n recheckFunction?: RetryFailedTransactionsParams[\"recheckFunction\"];\n }): Promise<RelaySubmitResult> {\n return retryFailedTransactions({\n apiUrl: this.apiUrl,\n chainId: this.chainId,\n ...params,\n });\n }\n\n /**\n * Create x402 client for relay payments\n *\n * @param account - User's local account\n * @returns Object with fetchWithPayment and httpClient\n */\n createX402Client(account: LocalAccount) {\n return createRelayX402Client(account, this.chainId);\n }\n}\n\n"]}
|