@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/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
|