@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 ADDED
@@ -0,0 +1,428 @@
1
+ # @net-protocol/relay
2
+
3
+ **Status: In Development** - Do not use yet. Will have breaking changes and may be incomplete.
4
+
5
+ Net Relay SDK for submitting transactions via x402 payment relay service.
6
+
7
+ This package provides client-side functionality for interacting with the Net relay service, which allows users to submit on-chain transactions without holding ETH by paying with USDC via x402.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @net-protocol/relay
13
+ # or
14
+ yarn add @net-protocol/relay
15
+ ```
16
+
17
+ ## Features
18
+
19
+ - **Session Management**: Create session tokens for authenticated batch requests
20
+ - **Balance Checking**: Check backend wallet balance before submitting transactions
21
+ - **Wallet Funding**: Fund backend wallets via x402 payments (USDC)
22
+ - **Transaction Submission**: Submit batches of transactions via relay service
23
+ - **Retry Logic**: Automatic retry with exponential backoff for failed transactions
24
+ - **Transaction Batching**: Automatically batch transactions to respect API limits
25
+ - **Confirmation Waiting**: Wait for transaction confirmations on-chain
26
+
27
+ ## Quick Start
28
+
29
+ ### Basic Usage
30
+
31
+ ```typescript
32
+ import {
33
+ createRelaySession,
34
+ submitTransactionsViaRelay,
35
+ } from "@net-protocol/relay";
36
+ import { privateKeyToAccount } from "viem/accounts";
37
+
38
+ // Create a session token
39
+ const account = privateKeyToAccount("0x...");
40
+ const { sessionToken } = await createRelaySession({
41
+ apiUrl: "https://api.example.com",
42
+ chainId: 84532,
43
+ operatorAddress: "0x...",
44
+ secretKey: "your-secret-key",
45
+ account,
46
+ });
47
+
48
+ // Submit transactions
49
+ const result = await submitTransactionsViaRelay({
50
+ apiUrl: "https://api.example.com",
51
+ chainId: 84532,
52
+ operatorAddress: "0x...",
53
+ secretKey: "your-secret-key",
54
+ transactions: [
55
+ {
56
+ to: "0x...",
57
+ data: "0x...",
58
+ },
59
+ ],
60
+ sessionToken,
61
+ });
62
+ ```
63
+
64
+ ### Using the RelayClient Class
65
+
66
+ For a more convenient API, use the `RelayClient` class:
67
+
68
+ ```typescript
69
+ import { RelayClient } from "@net-protocol/relay";
70
+ import { privateKeyToAccount } from "viem/accounts";
71
+
72
+ const client = new RelayClient({
73
+ apiUrl: "https://api.example.com",
74
+ chainId: 84532,
75
+ });
76
+
77
+ const account = privateKeyToAccount("0x...");
78
+
79
+ // Create session
80
+ const { sessionToken } = await client.createSession({
81
+ operatorAddress: "0x...",
82
+ secretKey: "your-secret-key",
83
+ account,
84
+ });
85
+
86
+ // Check balance
87
+ const balance = await client.checkBalance({
88
+ operatorAddress: "0x...",
89
+ secretKey: "your-secret-key",
90
+ });
91
+
92
+ // Fund backend wallet (if needed)
93
+ if (!balance.sufficientBalance) {
94
+ const x402Client = client.createX402Client(account);
95
+ await client.fundBackendWallet({
96
+ operatorAddress: "0x...",
97
+ secretKey: "your-secret-key",
98
+ ...x402Client,
99
+ });
100
+ }
101
+
102
+ // Submit transactions
103
+ const result = await client.submitTransactions({
104
+ operatorAddress: "0x...",
105
+ secretKey: "your-secret-key",
106
+ transactions: [...],
107
+ sessionToken,
108
+ });
109
+ ```
110
+
111
+ ## API Reference
112
+
113
+ ### Functions
114
+
115
+ #### `createRelaySession`
116
+
117
+ Create a relay session token for authenticated batch requests.
118
+
119
+ ```typescript
120
+ function createRelaySession(params: {
121
+ apiUrl: string;
122
+ chainId: number;
123
+ operatorAddress: Address;
124
+ secretKey: string;
125
+ account: LocalAccount;
126
+ expiresIn?: number; // Optional, default: 3600 seconds
127
+ }): Promise<{ sessionToken: string; expiresAt: number }>;
128
+ ```
129
+
130
+ #### `checkBackendWalletBalance`
131
+
132
+ Check the balance of the backend wallet for a given operator.
133
+
134
+ ```typescript
135
+ function checkBackendWalletBalance(params: {
136
+ apiUrl: string;
137
+ chainId: number;
138
+ operatorAddress: Address;
139
+ secretKey: string;
140
+ }): Promise<CheckBalanceResult>;
141
+ ```
142
+
143
+ #### `fundBackendWallet`
144
+
145
+ Fund a backend wallet via x402 payment (USDC).
146
+
147
+ ```typescript
148
+ function fundBackendWallet(params: {
149
+ apiUrl: string;
150
+ chainId: number;
151
+ operatorAddress: Address;
152
+ secretKey: string;
153
+ fetchWithPayment: typeof fetch;
154
+ httpClient: {
155
+ getPaymentSettleResponse: (
156
+ getHeader: (name: string) => string | null
157
+ ) => { transaction?: string; txHash?: string } | null;
158
+ };
159
+ }): Promise<RelayFundResult>;
160
+ ```
161
+
162
+ #### `submitTransactionsViaRelay`
163
+
164
+ Submit a batch of transactions via the relay service.
165
+
166
+ ```typescript
167
+ function submitTransactionsViaRelay(params: {
168
+ apiUrl: string;
169
+ chainId: number;
170
+ operatorAddress: Address;
171
+ secretKey: string;
172
+ transactions: WriteTransactionConfig[];
173
+ sessionToken: string;
174
+ }): Promise<RelaySubmitResult>;
175
+ ```
176
+
177
+ #### `retryFailedTransactions`
178
+
179
+ Retry failed transactions with exponential backoff.
180
+
181
+ ```typescript
182
+ function retryFailedTransactions(params: {
183
+ apiUrl: string;
184
+ chainId: number;
185
+ operatorAddress: Address;
186
+ secretKey: string;
187
+ failedIndexes: number[];
188
+ originalTransactions: WriteTransactionConfig[];
189
+ backendWalletAddress: Address;
190
+ sessionToken: string;
191
+ config?: RetryConfig;
192
+ recheckFunction?: (
193
+ failedIndexes: number[],
194
+ transactions: WriteTransactionConfig[],
195
+ backendWalletAddress: Address
196
+ ) => Promise<number[]>;
197
+ }): Promise<RelaySubmitResult>;
198
+ ```
199
+
200
+ #### `waitForConfirmations`
201
+
202
+ Wait for transaction confirmations on-chain.
203
+
204
+ ```typescript
205
+ function waitForConfirmations(params: {
206
+ publicClient: PublicClient;
207
+ transactionHashes: Hash[];
208
+ confirmations?: number; // Default: 1
209
+ timeout?: number; // Default: 60000ms
210
+ onProgress?: (confirmed: number, total: number) => void;
211
+ }): Promise<Array<{ hash: Hash; receipt: any }>>;
212
+ ```
213
+
214
+ #### `batchTransactions`
215
+
216
+ Batch transactions to respect API limits.
217
+
218
+ ```typescript
219
+ function batchTransactions(
220
+ transactions: WriteTransactionConfig[]
221
+ ): WriteTransactionConfig[][];
222
+ ```
223
+
224
+ #### `createRelayX402Client`
225
+
226
+ Create an x402 client for relay payments.
227
+
228
+ ```typescript
229
+ function createRelayX402Client(
230
+ account: LocalAccount,
231
+ chainId?: number
232
+ ): {
233
+ fetchWithPayment: typeof fetch;
234
+ httpClient: {
235
+ getPaymentSettleResponse: (
236
+ getHeader: (name: string) => string | null
237
+ ) => { transaction?: string; txHash?: string } | null;
238
+ };
239
+ };
240
+ ```
241
+
242
+ ### Classes
243
+
244
+ #### `RelayClient`
245
+
246
+ High-level class-based API for the relay service.
247
+
248
+ ```typescript
249
+ class RelayClient {
250
+ constructor(options: { apiUrl: string; chainId: number });
251
+
252
+ createSession(params: {...}): Promise<{ sessionToken: string; expiresAt: number }>;
253
+ checkBalance(params: {...}): Promise<CheckBalanceResult>;
254
+ fundBackendWallet(params: {...}): Promise<RelayFundResult>;
255
+ submitTransactions(params: {...}): Promise<RelaySubmitResult>;
256
+ retryFailedTransactions(params: {...}): Promise<RelaySubmitResult>;
257
+ createX402Client(account: LocalAccount): {...};
258
+ }
259
+ ```
260
+
261
+ ## Types
262
+
263
+ ### `CheckBalanceResult`
264
+
265
+ ```typescript
266
+ interface CheckBalanceResult {
267
+ backendWalletAddress: Address;
268
+ balanceWei: string;
269
+ balanceEth: string;
270
+ sufficientBalance: boolean;
271
+ minRequiredWei: string;
272
+ minRequiredEth: string;
273
+ }
274
+ ```
275
+
276
+ ### `RelayFundResult`
277
+
278
+ ```typescript
279
+ interface RelayFundResult {
280
+ paymentTxHash: Hash;
281
+ backendWalletAddress: Address;
282
+ }
283
+ ```
284
+
285
+ ### `RelaySubmitResult`
286
+
287
+ ```typescript
288
+ interface RelaySubmitResult {
289
+ transactionHashes: Hash[];
290
+ successfulIndexes: number[];
291
+ failedIndexes: number[];
292
+ errors: { index: number; error: string }[];
293
+ backendWalletAddress: Address;
294
+ appFeeTransactionHash: Hash; // Always included
295
+ }
296
+ ```
297
+
298
+ ### `RetryConfig`
299
+
300
+ ```typescript
301
+ interface RetryConfig {
302
+ maxRetries?: number; // Default: 3
303
+ initialDelay?: number; // Default: 1000ms
304
+ maxDelay?: number; // Default: 30000ms
305
+ backoffMultiplier?: number; // Default: 2
306
+ }
307
+ ```
308
+
309
+ ## Constants
310
+
311
+ - `MAX_TRANSACTIONS_PER_BATCH`: Maximum transactions per batch (100)
312
+ - `MAX_BATCH_SIZE_BYTES`: Maximum batch size in bytes (900KB)
313
+ - `MAX_TRANSACTION_SIZE_BYTES`: Maximum transaction size in bytes (100KB)
314
+
315
+ ## Usage Examples
316
+
317
+ ### Complete Upload Flow
318
+
319
+ ```typescript
320
+ import {
321
+ RelayClient,
322
+ batchTransactions,
323
+ waitForConfirmations,
324
+ } from "@net-protocol/relay";
325
+ import { privateKeyToAccount } from "viem/accounts";
326
+ import { createPublicClient, http } from "viem";
327
+ import { base } from "viem/chains";
328
+
329
+ const client = new RelayClient({
330
+ apiUrl: "https://api.example.com",
331
+ chainId: 84532,
332
+ });
333
+
334
+ const account = privateKeyToAccount("0x...");
335
+ const publicClient = createPublicClient({
336
+ chain: base,
337
+ transport: http(),
338
+ });
339
+
340
+ // 1. Create session
341
+ const { sessionToken } = await client.createSession({
342
+ operatorAddress: account.address,
343
+ secretKey: "your-secret-key",
344
+ account,
345
+ });
346
+
347
+ // 2. Check balance
348
+ const balance = await client.checkBalance({
349
+ operatorAddress: account.address,
350
+ secretKey: "your-secret-key",
351
+ });
352
+
353
+ // 3. Fund if needed
354
+ if (!balance.sufficientBalance) {
355
+ const x402Client = client.createX402Client(account);
356
+ await client.fundBackendWallet({
357
+ operatorAddress: account.address,
358
+ secretKey: "your-secret-key",
359
+ ...x402Client,
360
+ });
361
+ }
362
+
363
+ // 4. Prepare transactions
364
+ const transactions = [
365
+ // ... your transactions
366
+ ];
367
+
368
+ // 5. Batch transactions
369
+ const batches = batchTransactions(transactions);
370
+
371
+ // 6. Submit each batch
372
+ const allHashes: Hash[] = [];
373
+ for (const batch of batches) {
374
+ const result = await client.submitTransactions({
375
+ operatorAddress: account.address,
376
+ secretKey: "your-secret-key",
377
+ transactions: batch,
378
+ sessionToken,
379
+ });
380
+
381
+ allHashes.push(...result.transactionHashes);
382
+
383
+ // Retry failed transactions if any
384
+ if (result.failedIndexes.length > 0) {
385
+ const retryResult = await client.retryFailedTransactions({
386
+ operatorAddress: account.address,
387
+ secretKey: "your-secret-key",
388
+ failedIndexes: result.failedIndexes,
389
+ originalTransactions: batch,
390
+ backendWalletAddress: result.backendWalletAddress,
391
+ sessionToken,
392
+ });
393
+
394
+ allHashes.push(...retryResult.transactionHashes);
395
+ }
396
+ }
397
+
398
+ // 7. Wait for confirmations
399
+ const receipts = await waitForConfirmations({
400
+ publicClient,
401
+ transactionHashes: allHashes,
402
+ confirmations: 1,
403
+ onProgress: (confirmed, total) => {
404
+ console.log(`Confirmed ${confirmed}/${total}`);
405
+ },
406
+ });
407
+ ```
408
+
409
+ ## Chain Support
410
+
411
+ The package supports multiple chains via the `chainId` parameter:
412
+
413
+ - **Base Sepolia** (84532): Uses x402.org facilitator
414
+ - **Base Mainnet** (8453): Uses Coinbase CDP facilitator (automatic)
415
+
416
+ ## Error Handling
417
+
418
+ All functions throw errors with descriptive messages. Common error scenarios:
419
+
420
+ - **Session expired**: Session token has expired, create a new one
421
+ - **Insufficient balance**: Backend wallet needs funding
422
+ - **Payment failed**: x402 payment could not be processed
423
+ - **Transaction failed**: Transaction was rejected by the network
424
+ - **Rate limit**: Too many requests, retry after delay
425
+
426
+ ## License
427
+
428
+ MIT