@tapforce/pod-bridge-sdk 1.0.1 → 1.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 +218 -106
- package/dist/clients/action/{client.d.ts → bridged-to-pod-client.d.ts} +8 -35
- package/dist/clients/action/{client.js → bridged-to-pod-client.js} +13 -46
- package/dist/clients/action/pod-to-bridged-client.d.ts +54 -0
- package/dist/clients/action/pod-to-bridged-client.js +81 -0
- package/dist/clients/tracker/client.d.ts +37 -5
- package/dist/clients/tracker/client.js +175 -27
- package/dist/index.d.ts +3 -2
- package/dist/index.js +7 -5
- package/dist/libs/types/pod-bridge.types.d.ts +64 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,8 @@ pnpm add @tapforce/pod-bridge-sdk
|
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import {
|
|
37
|
-
|
|
37
|
+
BridgedToPodActionClient,
|
|
38
|
+
PodToBridgedActionClient,
|
|
38
39
|
PodBridgeTrackerClient,
|
|
39
40
|
PodBridgeConfig
|
|
40
41
|
} from '@tapforce/pod-bridge-sdk';
|
|
@@ -46,11 +47,11 @@ Create a configuration object with source and destination chain details:
|
|
|
46
47
|
|
|
47
48
|
```typescript
|
|
48
49
|
const config: PodBridgeConfig = {
|
|
49
|
-
|
|
50
|
+
bridged: {
|
|
50
51
|
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
51
52
|
contractAddress: '0x...' // Source chain bridge contract
|
|
52
53
|
},
|
|
53
|
-
|
|
54
|
+
pod: {
|
|
54
55
|
rpcUrl: 'https://pod-rpc.example.com',
|
|
55
56
|
contractAddress: '0x...' // Destination chain bridge contract
|
|
56
57
|
}
|
|
@@ -59,94 +60,109 @@ const config: PodBridgeConfig = {
|
|
|
59
60
|
|
|
60
61
|
## Usage
|
|
61
62
|
|
|
62
|
-
### 1. Action
|
|
63
|
+
### 1. Action Clients - Creating Transactions
|
|
63
64
|
|
|
64
|
-
The
|
|
65
|
+
The SDK provides two action clients for different bridge directions:
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
- **`BridgedToPodActionClient`**: For Bridged → POD transfers (e.g., Sepolia → POD)
|
|
68
|
+
- **`PodToBridgedActionClient`**: For POD → Bridged transfers (e.g., POD → Sepolia)
|
|
69
|
+
|
|
70
|
+
#### Configuration for Action Clients
|
|
67
71
|
|
|
68
72
|
```typescript
|
|
69
|
-
const
|
|
73
|
+
const actionConfig = {
|
|
74
|
+
bridged: {
|
|
75
|
+
contractAddress: '0x...' // Bridged chain bridge contract (e.g., Sepolia)
|
|
76
|
+
},
|
|
77
|
+
pod: {
|
|
78
|
+
contractAddress: '0x...' // POD chain bridge contract
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const bridgedToPodClient = new BridgedToPodActionClient(actionConfig);
|
|
83
|
+
const podToBridgedClient = new PodToBridgedActionClient(actionConfig);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Deposit from Bridged Chain (e.g., Sepolia → POD)
|
|
70
87
|
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
```typescript
|
|
89
|
+
// Create unsigned transaction for ERC20 deposit on Bridged chain
|
|
90
|
+
const unsignedTx = bridgedToPodClient.depositToken({
|
|
73
91
|
token: '0x...', // ERC20 token address
|
|
74
92
|
amount: '1000000000000000000', // Amount in wei (1 token with 18 decimals)
|
|
75
|
-
|
|
76
|
-
contractAddress: config.source.contractAddress,
|
|
93
|
+
destinationWalletAddress: '0x...', // Recipient address on POD
|
|
77
94
|
from: '0x...' // Optional: sender address
|
|
78
95
|
});
|
|
79
96
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
#### Deposit Native Tokens (ETH)
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
// Create unsigned transaction for native token deposit
|
|
88
|
-
const unsignedTx = actionClient.depositNative({
|
|
97
|
+
// Deposit native tokens
|
|
98
|
+
const unsignedNativeTx = bridgedToPodClient.depositNative({
|
|
89
99
|
amount: '1000000000000000000', // Amount in wei (1 ETH)
|
|
90
|
-
|
|
91
|
-
contractAddress: config.source.contractAddress,
|
|
100
|
+
destinationWalletAddress: '0x...', // Recipient address on POD
|
|
92
101
|
from: '0x...' // Optional: sender address
|
|
93
102
|
});
|
|
94
103
|
|
|
95
|
-
// Sign and send transaction
|
|
104
|
+
// Sign and send transaction using your wallet/provider
|
|
96
105
|
// const tx = await signer.sendTransaction(unsignedTx);
|
|
97
106
|
```
|
|
98
107
|
|
|
99
|
-
#### Claim
|
|
108
|
+
#### Claim on POD from Bridged Chain Deposits
|
|
100
109
|
|
|
101
|
-
For claiming on
|
|
110
|
+
For claiming on POD from Bridged chain deposits, use block number-based claims:
|
|
102
111
|
|
|
103
112
|
```typescript
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
data: '0x...'
|
|
110
|
-
},
|
|
111
|
-
sigs: [
|
|
112
|
-
{ r: '0x...', s: '0x...', v: 27 }
|
|
113
|
-
]
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Create unsigned claim transaction for ERC20
|
|
117
|
-
const unsignedTx = actionClient.claimWithCertificate({
|
|
118
|
-
certifiedLog,
|
|
119
|
-
contractAddress: config.destination.contractAddress,
|
|
113
|
+
// Create unsigned claim transaction for ERC20 on POD
|
|
114
|
+
const unsignedTx = bridgedToPodClient.claimWithBlockNumber({
|
|
115
|
+
id: '123', // Request ID from deposit event
|
|
116
|
+
token: '0x...', // Token address on source chain
|
|
117
|
+
blockNumber: 12345678, // Block number of deposit on source chain
|
|
120
118
|
from: '0x...' // Optional: claimer address
|
|
121
119
|
});
|
|
122
120
|
|
|
123
121
|
// For native tokens
|
|
124
|
-
const unsignedNativeTx =
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
const unsignedNativeTx = bridgedToPodClient.claimNativeWithBlockNumber({
|
|
123
|
+
id: '123',
|
|
124
|
+
blockNumber: 12345678,
|
|
127
125
|
from: '0x...'
|
|
128
126
|
});
|
|
129
127
|
```
|
|
130
128
|
|
|
131
|
-
####
|
|
129
|
+
#### Deposit from POD (POD → Bridged Chain)
|
|
132
130
|
|
|
133
|
-
|
|
131
|
+
```typescript
|
|
132
|
+
// Create unsigned transaction for ERC20 deposit on POD
|
|
133
|
+
const unsignedTx = podToBridgedClient.depositToken({
|
|
134
|
+
token: '0x...', // ERC20 token address
|
|
135
|
+
amount: '1000000000000000000', // Amount in wei
|
|
136
|
+
destinationWalletAddress: '0x...', // Recipient address on Bridged chain
|
|
137
|
+
from: '0x...' // Optional: sender address
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Deposit native tokens
|
|
141
|
+
const unsignedNativeTx = podToBridgedClient.depositNative({
|
|
142
|
+
amount: '1000000000000000000',
|
|
143
|
+
destinationWalletAddress: '0x...',
|
|
144
|
+
from: '0x...'
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Claim on Bridged Chain from POD Deposits
|
|
149
|
+
|
|
150
|
+
For claiming on Bridged chain from POD deposits, use certificate-based claims. First, get the certified log from the POD deposit transaction:
|
|
134
151
|
|
|
135
152
|
```typescript
|
|
136
|
-
//
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
// Get certified log from POD deposit transaction
|
|
154
|
+
const depositTxHash = '0x...'; // Transaction hash of deposit on POD
|
|
155
|
+
const certifiedLog = await trackerClient.getDepositCertifiedLog(depositTxHash);
|
|
156
|
+
|
|
157
|
+
// Create unsigned claim transaction for ERC20 on Bridged chain
|
|
158
|
+
const unsignedTx = podToBridgedClient.claimWithCertificate({
|
|
159
|
+
certifiedLog,
|
|
142
160
|
from: '0x...' // Optional: claimer address
|
|
143
161
|
});
|
|
144
162
|
|
|
145
163
|
// For native tokens
|
|
146
|
-
const unsignedNativeTx =
|
|
147
|
-
|
|
148
|
-
blockNumber: 12345678,
|
|
149
|
-
contractAddress: config.destination.contractAddress,
|
|
164
|
+
const unsignedNativeTx = podToBridgedClient.claimNativeWithCertificate({
|
|
165
|
+
certifiedLog,
|
|
150
166
|
from: '0x...'
|
|
151
167
|
});
|
|
152
168
|
```
|
|
@@ -232,44 +248,69 @@ processedStatuses.forEach((isProcessed, index) => {
|
|
|
232
248
|
|
|
233
249
|
## API Reference
|
|
234
250
|
|
|
235
|
-
###
|
|
251
|
+
### BridgedToPodActionClient
|
|
252
|
+
|
|
253
|
+
Handles transactions for Bridged → POD direction.
|
|
254
|
+
|
|
255
|
+
#### Constructor
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
new BridgedToPodActionClient(config: PodBridgeActionsClientConfig)
|
|
259
|
+
```
|
|
236
260
|
|
|
237
261
|
#### Methods
|
|
238
262
|
|
|
239
|
-
- **`depositToken(args)`**: Create unsigned transaction for ERC20 deposit
|
|
263
|
+
- **`depositToken(args)`**: Create unsigned transaction for ERC20 deposit on Bridged chain
|
|
240
264
|
- `token`: Token address
|
|
241
265
|
- `amount`: Amount in wei (string | bigint)
|
|
242
|
-
- `
|
|
243
|
-
- `contractAddress`: Bridge contract address
|
|
266
|
+
- `destinationWalletAddress`: Recipient address on POD
|
|
244
267
|
- `from?`: Optional sender address
|
|
245
268
|
|
|
246
|
-
- **`depositNative(args)`**: Create unsigned transaction for native token deposit
|
|
269
|
+
- **`depositNative(args)`**: Create unsigned transaction for native token deposit on Bridged chain
|
|
247
270
|
- `amount`: Amount in wei (string | bigint)
|
|
248
|
-
- `
|
|
249
|
-
- `contractAddress`: Bridge contract address
|
|
271
|
+
- `destinationWalletAddress`: Recipient address on POD
|
|
250
272
|
- `from?`: Optional sender address
|
|
251
273
|
|
|
252
|
-
- **`
|
|
253
|
-
- `
|
|
254
|
-
- `
|
|
274
|
+
- **`claimWithBlockNumber(args)`**: Create unsigned transaction for claiming on POD with block number
|
|
275
|
+
- `id`: Request ID
|
|
276
|
+
- `token`: Token address
|
|
277
|
+
- `blockNumber`: Block number of deposit on Bridged chain
|
|
255
278
|
- `from?`: Optional claimer address
|
|
256
279
|
|
|
257
|
-
- **`
|
|
258
|
-
- `
|
|
259
|
-
- `
|
|
280
|
+
- **`claimNativeWithBlockNumber(args)`**: Create unsigned transaction for claiming native tokens on POD with block number
|
|
281
|
+
- `id`: Request ID
|
|
282
|
+
- `blockNumber`: Block number of deposit on Bridged chain
|
|
260
283
|
- `from?`: Optional claimer address
|
|
261
284
|
|
|
262
|
-
|
|
263
|
-
|
|
285
|
+
### PodToBridgedActionClient
|
|
286
|
+
|
|
287
|
+
Handles transactions for POD → Bridged direction.
|
|
288
|
+
|
|
289
|
+
#### Constructor
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
new PodToBridgedActionClient(config: PodBridgeActionsClientConfig)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### Methods
|
|
296
|
+
|
|
297
|
+
- **`depositToken(args)`**: Create unsigned transaction for ERC20 deposit on POD
|
|
264
298
|
- `token`: Token address
|
|
265
|
-
- `
|
|
266
|
-
- `
|
|
299
|
+
- `amount`: Amount in wei (string | bigint)
|
|
300
|
+
- `destinationWalletAddress`: Recipient address on Bridged chain
|
|
301
|
+
- `from?`: Optional sender address
|
|
302
|
+
|
|
303
|
+
- **`depositNative(args)`**: Create unsigned transaction for native token deposit on POD
|
|
304
|
+
- `amount`: Amount in wei (string | bigint)
|
|
305
|
+
- `destinationWalletAddress`: Recipient address on Bridged chain
|
|
306
|
+
- `from?`: Optional sender address
|
|
307
|
+
|
|
308
|
+
- **`claimWithCertificate(args)`**: Create unsigned transaction for claiming on Bridged chain with certificate
|
|
309
|
+
- `certifiedLog`: Certified log from POD certificate system
|
|
267
310
|
- `from?`: Optional claimer address
|
|
268
311
|
|
|
269
|
-
- **`
|
|
270
|
-
- `
|
|
271
|
-
- `blockNumber`: Block number of deposit
|
|
272
|
-
- `contractAddress`: Bridge contract address
|
|
312
|
+
- **`claimNativeWithCertificate(args)`**: Create unsigned transaction for claiming native tokens on Bridged chain with certificate
|
|
313
|
+
- `certifiedLog`: Certified log from POD certificate system
|
|
273
314
|
- `from?`: Optional claimer address
|
|
274
315
|
|
|
275
316
|
### PodBridgeTrackerClient
|
|
@@ -291,6 +332,10 @@ processedStatuses.forEach((isProcessed, index) => {
|
|
|
291
332
|
- **`areRequestsProcessed(deposits)`**: Batch check if requests are processed
|
|
292
333
|
- Returns: `Promise<boolean[]>`
|
|
293
334
|
|
|
335
|
+
- **`getDepositCertifiedLog(txHash)`**: Get certified log for claiming on Sepolia from POD deposit
|
|
336
|
+
- `txHash`: Transaction hash of deposit on POD
|
|
337
|
+
- Returns: `Promise<CertifiedLog>` with attestations and merkle proof
|
|
338
|
+
|
|
294
339
|
## Types
|
|
295
340
|
|
|
296
341
|
### BridgeRequest
|
|
@@ -334,11 +379,18 @@ interface CertifiedLog {
|
|
|
334
379
|
topics: string[];
|
|
335
380
|
data: string;
|
|
336
381
|
};
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
382
|
+
logIndex: bigint | string;
|
|
383
|
+
certificate: {
|
|
384
|
+
leaf: string;
|
|
385
|
+
certifiedReceipt: {
|
|
386
|
+
receiptRoot: string;
|
|
387
|
+
aggregateSignature: string;
|
|
388
|
+
sortedAttestationTimestamps: bigint[] | string[];
|
|
389
|
+
};
|
|
390
|
+
proof: {
|
|
391
|
+
path: string[];
|
|
392
|
+
};
|
|
393
|
+
};
|
|
342
394
|
}
|
|
343
395
|
```
|
|
344
396
|
|
|
@@ -358,65 +410,125 @@ The SDK supports two bridge implementations:
|
|
|
358
410
|
|
|
359
411
|
## Examples
|
|
360
412
|
|
|
361
|
-
### Complete Deposit and Claim Flow
|
|
413
|
+
### Complete Deposit and Claim Flow (Sepolia → POD)
|
|
362
414
|
|
|
363
415
|
```typescript
|
|
364
416
|
import { ethers } from 'ethers';
|
|
365
|
-
import {
|
|
417
|
+
import { BridgedToPodActionClient, PodBridgeTrackerClient } from '@tapforce/pod-bridge-sdk';
|
|
366
418
|
|
|
367
419
|
// Setup
|
|
368
|
-
const
|
|
369
|
-
|
|
420
|
+
const trackerConfig = {
|
|
421
|
+
bridged: {
|
|
370
422
|
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
371
|
-
contractAddress: '
|
|
423
|
+
contractAddress: '0xSepoliaBridgeContract'
|
|
372
424
|
},
|
|
373
|
-
|
|
374
|
-
rpcUrl: 'https://
|
|
375
|
-
contractAddress: '
|
|
425
|
+
pod: {
|
|
426
|
+
rpcUrl: 'https://rpc.v1.dev.pod.network',
|
|
427
|
+
contractAddress: '0xPodBridgeContract'
|
|
376
428
|
}
|
|
377
429
|
};
|
|
378
430
|
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
431
|
+
const actionConfig = {
|
|
432
|
+
bridged: { contractAddress: '0xSepoliaBridgeContract' },
|
|
433
|
+
pod: { contractAddress: '0xPodBridgeContract' }
|
|
434
|
+
};
|
|
383
435
|
|
|
384
|
-
|
|
436
|
+
const actionClient = new BridgedToPodActionClient(actionConfig);
|
|
437
|
+
const trackerClient = new PodBridgeTrackerClient(trackerConfig);
|
|
438
|
+
const sepoliaProvider = new ethers.JsonRpcProvider(trackerConfig.bridged.rpcUrl);
|
|
439
|
+
const sepoliaSigner = new ethers.Wallet('PRIVATE_KEY', sepoliaProvider);
|
|
440
|
+
|
|
441
|
+
// Step 1: Deposit tokens on Sepolia
|
|
385
442
|
const depositTx = actionClient.depositToken({
|
|
386
443
|
token: '0xTokenAddress',
|
|
387
444
|
amount: ethers.parseEther('10').toString(),
|
|
388
|
-
|
|
389
|
-
contractAddress: config.source.contractAddress
|
|
445
|
+
destinationWalletAddress: await sepoliaSigner.getAddress()
|
|
390
446
|
});
|
|
391
447
|
|
|
392
|
-
const tx = await
|
|
448
|
+
const tx = await sepoliaSigner.sendTransaction(depositTx);
|
|
393
449
|
await tx.wait();
|
|
394
450
|
console.log(`Deposit tx: ${tx.hash}`);
|
|
395
451
|
|
|
396
452
|
// Step 2: Track deposit
|
|
397
|
-
const deposits = await trackerClient.getDepositsSentBy(await
|
|
453
|
+
const deposits = await trackerClient.getDepositsSentBy(await sepoliaSigner.getAddress());
|
|
398
454
|
const latestDeposit = deposits[0];
|
|
399
455
|
|
|
400
456
|
console.log(`Request ID: ${latestDeposit.requestId}`);
|
|
401
457
|
console.log(`Amount: ${ethers.formatEther(latestDeposit.amount)}`);
|
|
402
458
|
|
|
403
|
-
// Step 3: Wait for finality and claim
|
|
459
|
+
// Step 3: Wait for finality and claim on POD
|
|
404
460
|
const canClaim = await trackerClient.canBeClaimed(latestDeposit);
|
|
405
461
|
if (canClaim) {
|
|
406
462
|
const claimTx = actionClient.claimWithBlockNumber({
|
|
407
463
|
id: latestDeposit.requestId,
|
|
408
464
|
token: latestDeposit.token,
|
|
409
|
-
blockNumber: latestDeposit.blockNumber
|
|
410
|
-
contractAddress: config.destination.contractAddress
|
|
465
|
+
blockNumber: latestDeposit.blockNumber
|
|
411
466
|
});
|
|
412
467
|
|
|
413
|
-
// Submit claim on
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
468
|
+
// Submit claim on POD
|
|
469
|
+
const podProvider = new ethers.JsonRpcProvider(trackerConfig.pod.rpcUrl);
|
|
470
|
+
const podSigner = new ethers.Wallet('PRIVATE_KEY', podProvider);
|
|
471
|
+
const claim = await podSigner.sendTransaction(claimTx);
|
|
472
|
+
await claim.wait();
|
|
473
|
+
console.log(`Claimed on POD: ${claim.hash}`);
|
|
417
474
|
}
|
|
418
475
|
```
|
|
419
476
|
|
|
477
|
+
### Complete Deposit and Claim Flow (POD → Sepolia)
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
import { ethers } from 'ethers';
|
|
481
|
+
import { PodToBridgedActionClient, PodBridgeTrackerClient } from '@tapforce/pod-bridge-sdk';
|
|
482
|
+
|
|
483
|
+
// Setup
|
|
484
|
+
const trackerConfig = {
|
|
485
|
+
bridged: {
|
|
486
|
+
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
487
|
+
contractAddress: '0xSepoliaBridgeContract'
|
|
488
|
+
},
|
|
489
|
+
pod: {
|
|
490
|
+
rpcUrl: 'https://rpc.v1.dev.pod.network',
|
|
491
|
+
contractAddress: '0xPodBridgeContract'
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const actionConfig = {
|
|
496
|
+
bridged: { contractAddress: '0xSepoliaBridgeContract' },
|
|
497
|
+
pod: { contractAddress: '0xPodBridgeContract' }
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
const actionClient = new PodToBridgedActionClient(actionConfig);
|
|
501
|
+
const trackerClient = new PodBridgeTrackerClient(trackerConfig);
|
|
502
|
+
const podProvider = new ethers.JsonRpcProvider(trackerConfig.pod.rpcUrl);
|
|
503
|
+
const podSigner = new ethers.Wallet('PRIVATE_KEY', podProvider);
|
|
504
|
+
|
|
505
|
+
// Step 1: Deposit tokens on POD
|
|
506
|
+
const depositTx = actionClient.depositToken({
|
|
507
|
+
token: '0xTokenAddress',
|
|
508
|
+
amount: ethers.parseEther('10').toString(),
|
|
509
|
+
destinationWalletAddress: await podSigner.getAddress()
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
const tx = await podSigner.sendTransaction(depositTx);
|
|
513
|
+
const receipt = await tx.wait();
|
|
514
|
+
console.log(`Deposit tx on POD: ${tx.hash}`);
|
|
515
|
+
|
|
516
|
+
// Step 2: Get certified log from POD transaction
|
|
517
|
+
const certifiedLog = await trackerClient.getDepositCertifiedLog(tx.hash);
|
|
518
|
+
console.log('Got certified log with attestations');
|
|
519
|
+
|
|
520
|
+
// Step 3: Claim on Sepolia using certified log
|
|
521
|
+
const claimTx = actionClient.claimWithCertificate({
|
|
522
|
+
certifiedLog
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
const sepoliaProvider = new ethers.JsonRpcProvider(trackerConfig.bridged.rpcUrl);
|
|
526
|
+
const sepoliaSigner = new ethers.Wallet('PRIVATE_KEY', sepoliaProvider);
|
|
527
|
+
const claim = await sepoliaSigner.sendTransaction(claimTx);
|
|
528
|
+
await claim.wait();
|
|
529
|
+
console.log(`Claimed on Sepolia: ${claim.hash}`);
|
|
530
|
+
```
|
|
531
|
+
|
|
420
532
|
## Development
|
|
421
533
|
|
|
422
534
|
### Build
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { UnsignedTransaction,
|
|
2
|
-
export declare class
|
|
1
|
+
import { UnsignedTransaction, PodBridgeActionsClientConfig } from "../../libs/types/pod-bridge.types";
|
|
2
|
+
export declare class BridgedToPodActionClient {
|
|
3
|
+
private readonly config;
|
|
3
4
|
private readonly iface;
|
|
4
|
-
constructor();
|
|
5
|
+
constructor(config: PodBridgeActionsClientConfig);
|
|
5
6
|
/**
|
|
6
7
|
* Create unsigned transaction for depositing ERC20 tokens
|
|
7
8
|
* @param args.token Token address to deposit
|
|
8
9
|
* @param args.amount Amount to deposit (in wei)
|
|
9
|
-
* @param args.
|
|
10
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
10
11
|
* @param args.contractAddress Bridge contract address (source chain)
|
|
11
12
|
* @param args.from Optional sender address
|
|
12
13
|
* @returns Unsigned transaction template with encoded deposit call
|
|
@@ -14,48 +15,20 @@ export declare class PodBridgeActionClient {
|
|
|
14
15
|
depositToken(args: {
|
|
15
16
|
token: string;
|
|
16
17
|
amount: string | bigint;
|
|
17
|
-
|
|
18
|
-
contractAddress: string;
|
|
18
|
+
destinationWalletAddress: string;
|
|
19
19
|
from?: string;
|
|
20
20
|
}): UnsignedTransaction;
|
|
21
21
|
/**
|
|
22
22
|
* Create unsigned transaction for depositing native tokens (ETH)
|
|
23
23
|
* @param args.amount Amount to deposit (in wei) - also set as transaction value
|
|
24
|
-
* @param args.
|
|
24
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
25
25
|
* @param args.contractAddress Bridge contract address (source chain)
|
|
26
26
|
* @param args.from Optional sender address
|
|
27
27
|
* @returns Unsigned transaction template with encoded depositNative call
|
|
28
28
|
*/
|
|
29
29
|
depositNative(args: {
|
|
30
30
|
amount: string | bigint;
|
|
31
|
-
|
|
32
|
-
contractAddress: string;
|
|
33
|
-
from?: string;
|
|
34
|
-
}): UnsignedTransaction;
|
|
35
|
-
/**
|
|
36
|
-
* Create unsigned transaction for claiming tokens (BridgeDepositWithdraw - with certificates)
|
|
37
|
-
* Used for claiming on Sepolia from POD deposits
|
|
38
|
-
* @param args.certifiedLog Certified log from POD certificate system
|
|
39
|
-
* @param args.contractAddress Bridge contract address (destination chain)
|
|
40
|
-
* @param args.from Optional sender address
|
|
41
|
-
* @returns Unsigned transaction template with encoded claim call
|
|
42
|
-
*/
|
|
43
|
-
claimWithCertificate(args: {
|
|
44
|
-
certifiedLog: CertifiedLog;
|
|
45
|
-
contractAddress: string;
|
|
46
|
-
from?: string;
|
|
47
|
-
}): UnsignedTransaction;
|
|
48
|
-
/**
|
|
49
|
-
* Create unsigned transaction for claiming native tokens (BridgeDepositWithdraw - with certificates)
|
|
50
|
-
* Used for claiming native tokens on Sepolia from POD deposits
|
|
51
|
-
* @param args.certifiedLog Certified log from POD certificate system
|
|
52
|
-
* @param args.contractAddress Bridge contract address (destination chain)
|
|
53
|
-
* @param args.from Optional sender address
|
|
54
|
-
* @returns Unsigned transaction template with encoded claimNative call
|
|
55
|
-
*/
|
|
56
|
-
claimNativeWithCertificate(args: {
|
|
57
|
-
certifiedLog: CertifiedLog;
|
|
58
|
-
contractAddress: string;
|
|
31
|
+
destinationWalletAddress: string;
|
|
59
32
|
from?: string;
|
|
60
33
|
}): UnsignedTransaction;
|
|
61
34
|
/**
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.BridgedToPodActionClient = void 0;
|
|
4
4
|
const ethers_1 = require("ethers");
|
|
5
5
|
const bridge_abi_1 = require("../../libs/abi/bridge.abi");
|
|
6
|
-
class
|
|
7
|
-
constructor() {
|
|
6
|
+
class BridgedToPodActionClient {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
8
9
|
this.iface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Create unsigned transaction for depositing ERC20 tokens
|
|
12
13
|
* @param args.token Token address to deposit
|
|
13
14
|
* @param args.amount Amount to deposit (in wei)
|
|
14
|
-
* @param args.
|
|
15
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
15
16
|
* @param args.contractAddress Bridge contract address (source chain)
|
|
16
17
|
* @param args.from Optional sender address
|
|
17
18
|
* @returns Unsigned transaction template with encoded deposit call
|
|
@@ -20,10 +21,10 @@ class PodBridgeActionClient {
|
|
|
20
21
|
const data = this.iface.encodeFunctionData('deposit', [
|
|
21
22
|
args.token,
|
|
22
23
|
args.amount,
|
|
23
|
-
args.
|
|
24
|
+
args.destinationWalletAddress
|
|
24
25
|
]);
|
|
25
26
|
return {
|
|
26
|
-
to:
|
|
27
|
+
to: this.config.bridged.contractAddress,
|
|
27
28
|
data,
|
|
28
29
|
value: '0',
|
|
29
30
|
from: args === null || args === void 0 ? void 0 : args.from
|
|
@@ -32,54 +33,20 @@ class PodBridgeActionClient {
|
|
|
32
33
|
/**
|
|
33
34
|
* Create unsigned transaction for depositing native tokens (ETH)
|
|
34
35
|
* @param args.amount Amount to deposit (in wei) - also set as transaction value
|
|
35
|
-
* @param args.
|
|
36
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
36
37
|
* @param args.contractAddress Bridge contract address (source chain)
|
|
37
38
|
* @param args.from Optional sender address
|
|
38
39
|
* @returns Unsigned transaction template with encoded depositNative call
|
|
39
40
|
*/
|
|
40
41
|
depositNative(args) {
|
|
41
|
-
const data = this.iface.encodeFunctionData('depositNative', [args.
|
|
42
|
+
const data = this.iface.encodeFunctionData('depositNative', [args.destinationWalletAddress]);
|
|
42
43
|
return {
|
|
43
|
-
to:
|
|
44
|
+
to: this.config.bridged.contractAddress,
|
|
44
45
|
data,
|
|
45
46
|
value: args.amount.toString(),
|
|
46
47
|
from: args === null || args === void 0 ? void 0 : args.from
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Create unsigned transaction for claiming tokens (BridgeDepositWithdraw - with certificates)
|
|
51
|
-
* Used for claiming on Sepolia from POD deposits
|
|
52
|
-
* @param args.certifiedLog Certified log from POD certificate system
|
|
53
|
-
* @param args.contractAddress Bridge contract address (destination chain)
|
|
54
|
-
* @param args.from Optional sender address
|
|
55
|
-
* @returns Unsigned transaction template with encoded claim call
|
|
56
|
-
*/
|
|
57
|
-
claimWithCertificate(args) {
|
|
58
|
-
const data = this.iface.encodeFunctionData('claim', [args.certifiedLog]);
|
|
59
|
-
return {
|
|
60
|
-
to: args.contractAddress,
|
|
61
|
-
data,
|
|
62
|
-
value: '0',
|
|
63
|
-
from: args === null || args === void 0 ? void 0 : args.from
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Create unsigned transaction for claiming native tokens (BridgeDepositWithdraw - with certificates)
|
|
68
|
-
* Used for claiming native tokens on Sepolia from POD deposits
|
|
69
|
-
* @param args.certifiedLog Certified log from POD certificate system
|
|
70
|
-
* @param args.contractAddress Bridge contract address (destination chain)
|
|
71
|
-
* @param args.from Optional sender address
|
|
72
|
-
* @returns Unsigned transaction template with encoded claimNative call
|
|
73
|
-
*/
|
|
74
|
-
claimNativeWithCertificate(args) {
|
|
75
|
-
const data = this.iface.encodeFunctionData('claimNative', [args.certifiedLog]);
|
|
76
|
-
return {
|
|
77
|
-
to: args.contractAddress,
|
|
78
|
-
data,
|
|
79
|
-
value: '0',
|
|
80
|
-
from: args === null || args === void 0 ? void 0 : args.from
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
50
|
/**
|
|
84
51
|
* Create unsigned transaction for claiming tokens (BridgeMintBurn - using precompiles)
|
|
85
52
|
* Used for claiming on POD from Sepolia deposits
|
|
@@ -97,7 +64,7 @@ class PodBridgeActionClient {
|
|
|
97
64
|
args.blockNumber
|
|
98
65
|
]);
|
|
99
66
|
return {
|
|
100
|
-
to:
|
|
67
|
+
to: this.config.pod.contractAddress,
|
|
101
68
|
data,
|
|
102
69
|
value: '0',
|
|
103
70
|
from: args === null || args === void 0 ? void 0 : args.from
|
|
@@ -115,11 +82,11 @@ class PodBridgeActionClient {
|
|
|
115
82
|
claimNativeWithBlockNumber(args) {
|
|
116
83
|
const data = this.iface.encodeFunctionData('claimNative(uint256,uint256)', [args.id, args.blockNumber]);
|
|
117
84
|
return {
|
|
118
|
-
to:
|
|
85
|
+
to: this.config.pod.contractAddress,
|
|
119
86
|
data,
|
|
120
87
|
value: '0',
|
|
121
88
|
from: args === null || args === void 0 ? void 0 : args.from
|
|
122
89
|
};
|
|
123
90
|
}
|
|
124
91
|
}
|
|
125
|
-
exports.
|
|
92
|
+
exports.BridgedToPodActionClient = BridgedToPodActionClient;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { UnsignedTransaction, CertifiedLog, PodBridgeActionsClientConfig } from "../../libs/types/pod-bridge.types";
|
|
2
|
+
export declare class PodToBridgedActionClient {
|
|
3
|
+
private readonly config;
|
|
4
|
+
private readonly iface;
|
|
5
|
+
constructor(config: PodBridgeActionsClientConfig);
|
|
6
|
+
/**
|
|
7
|
+
* Create unsigned transaction for depositing ERC20 tokens from POD to Bridged chain
|
|
8
|
+
* @param args.token Token address to deposit
|
|
9
|
+
* @param args.amount Amount to deposit (in wei)
|
|
10
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
11
|
+
* @param args.from Optional sender address
|
|
12
|
+
* @returns Unsigned transaction template with encoded deposit call
|
|
13
|
+
*/
|
|
14
|
+
depositToken(args: {
|
|
15
|
+
token: string;
|
|
16
|
+
amount: string | bigint;
|
|
17
|
+
destinationWalletAddress: string;
|
|
18
|
+
from?: string;
|
|
19
|
+
}): UnsignedTransaction;
|
|
20
|
+
/**
|
|
21
|
+
* Create unsigned transaction for depositing native tokens from POD to Bridged chain
|
|
22
|
+
* @param args.amount Amount to deposit (in wei) - also set as transaction value
|
|
23
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
24
|
+
* @param args.from Optional sender address
|
|
25
|
+
* @returns Unsigned transaction template with encoded depositNative call
|
|
26
|
+
*/
|
|
27
|
+
depositNative(args: {
|
|
28
|
+
amount: string | bigint;
|
|
29
|
+
destinationWalletAddress: string;
|
|
30
|
+
from?: string;
|
|
31
|
+
}): UnsignedTransaction;
|
|
32
|
+
/**
|
|
33
|
+
* Create unsigned transaction for claiming tokens on Bridged chain (BridgeDepositWithdraw - with certificates)
|
|
34
|
+
* Used for claiming on Bridged chain from POD deposits
|
|
35
|
+
* @param args.certifiedLog Certified log from POD certificate system
|
|
36
|
+
* @param args.from Optional sender address
|
|
37
|
+
* @returns Unsigned transaction template with encoded claim call
|
|
38
|
+
*/
|
|
39
|
+
claimWithCertificate(args: {
|
|
40
|
+
certifiedLog: CertifiedLog;
|
|
41
|
+
from?: string;
|
|
42
|
+
}): UnsignedTransaction;
|
|
43
|
+
/**
|
|
44
|
+
* Create unsigned transaction for claiming native tokens on Bridged chain (BridgeDepositWithdraw - with certificates)
|
|
45
|
+
* Used for claiming native tokens on Bridged chain from POD deposits
|
|
46
|
+
* @param args.certifiedLog Certified log from POD certificate system
|
|
47
|
+
* @param args.from Optional sender address
|
|
48
|
+
* @returns Unsigned transaction template with encoded claimNative call
|
|
49
|
+
*/
|
|
50
|
+
claimNativeWithCertificate(args: {
|
|
51
|
+
certifiedLog: CertifiedLog;
|
|
52
|
+
from?: string;
|
|
53
|
+
}): UnsignedTransaction;
|
|
54
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PodToBridgedActionClient = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const bridge_abi_1 = require("../../libs/abi/bridge.abi");
|
|
6
|
+
class PodToBridgedActionClient {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.iface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create unsigned transaction for depositing ERC20 tokens from POD to Bridged chain
|
|
13
|
+
* @param args.token Token address to deposit
|
|
14
|
+
* @param args.amount Amount to deposit (in wei)
|
|
15
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
16
|
+
* @param args.from Optional sender address
|
|
17
|
+
* @returns Unsigned transaction template with encoded deposit call
|
|
18
|
+
*/
|
|
19
|
+
depositToken(args) {
|
|
20
|
+
const data = this.iface.encodeFunctionData('deposit', [
|
|
21
|
+
args.token,
|
|
22
|
+
args.amount,
|
|
23
|
+
args.destinationWalletAddress
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
to: this.config.pod.contractAddress,
|
|
27
|
+
data,
|
|
28
|
+
value: '0',
|
|
29
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create unsigned transaction for depositing native tokens from POD to Bridged chain
|
|
34
|
+
* @param args.amount Amount to deposit (in wei) - also set as transaction value
|
|
35
|
+
* @param args.destinationWalletAddress Recipient address on destination chain
|
|
36
|
+
* @param args.from Optional sender address
|
|
37
|
+
* @returns Unsigned transaction template with encoded depositNative call
|
|
38
|
+
*/
|
|
39
|
+
depositNative(args) {
|
|
40
|
+
const data = this.iface.encodeFunctionData('depositNative', [args.destinationWalletAddress]);
|
|
41
|
+
return {
|
|
42
|
+
to: this.config.pod.contractAddress,
|
|
43
|
+
data,
|
|
44
|
+
value: args.amount.toString(),
|
|
45
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create unsigned transaction for claiming tokens on Bridged chain (BridgeDepositWithdraw - with certificates)
|
|
50
|
+
* Used for claiming on Bridged chain from POD deposits
|
|
51
|
+
* @param args.certifiedLog Certified log from POD certificate system
|
|
52
|
+
* @param args.from Optional sender address
|
|
53
|
+
* @returns Unsigned transaction template with encoded claim call
|
|
54
|
+
*/
|
|
55
|
+
claimWithCertificate(args) {
|
|
56
|
+
const data = this.iface.encodeFunctionData('claim', [args.certifiedLog]);
|
|
57
|
+
return {
|
|
58
|
+
to: this.config.bridged.contractAddress,
|
|
59
|
+
data,
|
|
60
|
+
value: '0',
|
|
61
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create unsigned transaction for claiming native tokens on Bridged chain (BridgeDepositWithdraw - with certificates)
|
|
66
|
+
* Used for claiming native tokens on Bridged chain from POD deposits
|
|
67
|
+
* @param args.certifiedLog Certified log from POD certificate system
|
|
68
|
+
* @param args.from Optional sender address
|
|
69
|
+
* @returns Unsigned transaction template with encoded claimNative call
|
|
70
|
+
*/
|
|
71
|
+
claimNativeWithCertificate(args) {
|
|
72
|
+
const data = this.iface.encodeFunctionData('claimNative', [args.certifiedLog]);
|
|
73
|
+
return {
|
|
74
|
+
to: this.config.bridged.contractAddress,
|
|
75
|
+
data,
|
|
76
|
+
value: '0',
|
|
77
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.PodToBridgedActionClient = PodToBridgedActionClient;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { PodBridgeConfig, BridgeRequest, BridgeRequestWithType } from "../../libs/types/pod-bridge.types";
|
|
1
|
+
import { PodBridgeConfig, BridgeRequest, BridgeRequestWithType, CertifiedLog } from "../../libs/types/pod-bridge.types";
|
|
2
2
|
export declare class PodBridgeTrackerClient {
|
|
3
3
|
private readonly config;
|
|
4
|
-
private readonly
|
|
5
|
-
private readonly
|
|
6
|
-
private readonly
|
|
7
|
-
private readonly
|
|
4
|
+
private readonly bridgedProvider;
|
|
5
|
+
private readonly podProvider;
|
|
6
|
+
private readonly bridgedBridge;
|
|
7
|
+
private readonly podBridge;
|
|
8
8
|
private readonly iface;
|
|
9
9
|
constructor(config: PodBridgeConfig);
|
|
10
10
|
/**
|
|
@@ -54,4 +54,36 @@ export declare class PodBridgeTrackerClient {
|
|
|
54
54
|
* @returns Request hash
|
|
55
55
|
*/
|
|
56
56
|
private computeRequestHash;
|
|
57
|
+
/**
|
|
58
|
+
* Get certified log for a deposit transaction from POD
|
|
59
|
+
* This is required to claim tokens on Sepolia from POD deposits
|
|
60
|
+
* @param txHash Transaction hash of the deposit on POD
|
|
61
|
+
* @returns Certified log with attestations and merkle proof
|
|
62
|
+
*/
|
|
63
|
+
getDepositCertifiedLog(txHash: string): Promise<CertifiedLog>;
|
|
64
|
+
/**
|
|
65
|
+
* Compute receipt root hash
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
private computeReceiptRoot;
|
|
69
|
+
/**
|
|
70
|
+
* Aggregate signatures from attestations
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
private aggregateSignatures;
|
|
74
|
+
/**
|
|
75
|
+
* Hash a log entry
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
private hashLog;
|
|
79
|
+
/**
|
|
80
|
+
* Hash a leaf for merkle tree
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
private hashLeaf;
|
|
84
|
+
/**
|
|
85
|
+
* Generate merkle proof for a log at given index
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
private generateMerkleProof;
|
|
57
89
|
}
|
|
@@ -8,11 +8,11 @@ class PodBridgeTrackerClient {
|
|
|
8
8
|
constructor(config) {
|
|
9
9
|
this.config = config;
|
|
10
10
|
// Initialize providers
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
11
|
+
this.bridgedProvider = new ethers_1.ethers.JsonRpcProvider(config.bridged.rpcUrl);
|
|
12
|
+
this.podProvider = new ethers_1.ethers.JsonRpcProvider(config.pod.rpcUrl);
|
|
13
13
|
// Initialize contract instances
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
14
|
+
this.bridgedBridge = new ethers_1.Contract(config.bridged.contractAddress, bridge_abi_1.POD_BRIDGE_ABI, this.bridgedProvider);
|
|
15
|
+
this.podBridge = new ethers_1.Contract(config.pod.contractAddress, bridge_abi_1.POD_BRIDGE_ABI, this.podProvider);
|
|
16
16
|
// Interface for parsing logs
|
|
17
17
|
this.iface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
|
|
18
18
|
}
|
|
@@ -23,24 +23,23 @@ class PodBridgeTrackerClient {
|
|
|
23
23
|
* @returns Array of bridge requests sent by this address
|
|
24
24
|
*/
|
|
25
25
|
async getDepositsSentBy(address, fromBlock = 0) {
|
|
26
|
-
console.log(`Fetching deposits sent by ${address}...`);
|
|
27
26
|
const deposits = [];
|
|
28
|
-
const currentBlock = await this.
|
|
27
|
+
const currentBlock = await this.bridgedProvider.getBlockNumber();
|
|
29
28
|
const BLOCK_BATCH_SIZE = 10000;
|
|
30
29
|
for (let start = fromBlock; start <= currentBlock; start += BLOCK_BATCH_SIZE) {
|
|
31
30
|
const end = Math.min(start + BLOCK_BATCH_SIZE - 1, currentBlock);
|
|
32
31
|
// With improved events, we can filter by 'from' directly!
|
|
33
|
-
const depositFilter = this.
|
|
32
|
+
const depositFilter = this.bridgedBridge.filters.Deposit(null, // id
|
|
34
33
|
address, // from (indexed) - Filter by sender!
|
|
35
34
|
null // to
|
|
36
35
|
);
|
|
37
|
-
const depositNativeFilter = this.
|
|
36
|
+
const depositNativeFilter = this.bridgedBridge.filters.DepositNative(null, // id
|
|
38
37
|
address, // from (indexed) - Filter by sender!
|
|
39
38
|
null // to
|
|
40
39
|
);
|
|
41
40
|
const [depositLogs, depositNativeLogs] = await Promise.all([
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
41
|
+
this.bridgedBridge.queryFilter(depositFilter, start, end),
|
|
42
|
+
this.bridgedBridge.queryFilter(depositNativeFilter, start, end)
|
|
44
43
|
]);
|
|
45
44
|
// Process Deposit events
|
|
46
45
|
for (const log of depositLogs) {
|
|
@@ -94,24 +93,23 @@ class PodBridgeTrackerClient {
|
|
|
94
93
|
* @returns Array of bridge requests sent to this address
|
|
95
94
|
*/
|
|
96
95
|
async getDepositsReceivedBy(address, fromBlock = 0) {
|
|
97
|
-
console.log(`Fetching deposits received by ${address}...`);
|
|
98
96
|
const deposits = [];
|
|
99
|
-
const currentBlock = await this.
|
|
97
|
+
const currentBlock = await this.bridgedProvider.getBlockNumber();
|
|
100
98
|
const BLOCK_BATCH_SIZE = 10000;
|
|
101
99
|
for (let start = fromBlock; start <= currentBlock; start += BLOCK_BATCH_SIZE) {
|
|
102
100
|
const end = Math.min(start + BLOCK_BATCH_SIZE - 1, currentBlock);
|
|
103
101
|
// With improved events, we can filter by 'to' directly!
|
|
104
|
-
const depositFilter = this.
|
|
102
|
+
const depositFilter = this.bridgedBridge.filters.Deposit(null, // id
|
|
105
103
|
null, // from
|
|
106
104
|
address // to (indexed) - Filter by recipient!
|
|
107
105
|
);
|
|
108
|
-
const depositNativeFilter = this.
|
|
106
|
+
const depositNativeFilter = this.bridgedBridge.filters.DepositNative(null, // id
|
|
109
107
|
null, // from
|
|
110
108
|
address // to (indexed) - Filter by recipient!
|
|
111
109
|
);
|
|
112
110
|
const [depositLogs, depositNativeLogs] = await Promise.all([
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
111
|
+
this.bridgedBridge.queryFilter(depositFilter, start, end),
|
|
112
|
+
this.bridgedBridge.queryFilter(depositNativeFilter, start, end)
|
|
115
113
|
]);
|
|
116
114
|
// Process Deposit events
|
|
117
115
|
for (const log of depositLogs) {
|
|
@@ -165,7 +163,6 @@ class PodBridgeTrackerClient {
|
|
|
165
163
|
* @returns Array of bridge requests with type indicator
|
|
166
164
|
*/
|
|
167
165
|
async getAllDepositsFor(address, fromBlock = 0) {
|
|
168
|
-
console.log(`Fetching all deposits for ${address}...`);
|
|
169
166
|
// Fetch both sent and received in parallel
|
|
170
167
|
const [sent, received] = await Promise.all([
|
|
171
168
|
this.getDepositsSentBy(address, fromBlock),
|
|
@@ -205,7 +202,7 @@ class PodBridgeTrackerClient {
|
|
|
205
202
|
* @returns True if the deposit can be claimed
|
|
206
203
|
*/
|
|
207
204
|
async canBeClaimed(deposit) {
|
|
208
|
-
const currentBlock = await this.
|
|
205
|
+
const currentBlock = await this.bridgedProvider.getBlockNumber();
|
|
209
206
|
const finalizedBlock = currentBlock - 15; // ~15 minutes on Sepolia
|
|
210
207
|
return deposit.blockNumber <= finalizedBlock && !deposit.isClaimed;
|
|
211
208
|
}
|
|
@@ -214,16 +211,16 @@ class PodBridgeTrackerClient {
|
|
|
214
211
|
* @param deposits Array of deposits to check
|
|
215
212
|
*/
|
|
216
213
|
async updateClaimStatus(deposits) {
|
|
217
|
-
if (deposits.length === 0)
|
|
214
|
+
if (deposits.length === 0) {
|
|
218
215
|
return;
|
|
219
|
-
|
|
216
|
+
}
|
|
220
217
|
try {
|
|
221
218
|
// Get claim events from destination chain
|
|
222
|
-
const claimFilter = this.
|
|
223
|
-
const claimNativeFilter = this.
|
|
219
|
+
const claimFilter = this.podBridge.filters.Claim();
|
|
220
|
+
const claimNativeFilter = this.podBridge.filters.ClaimNative();
|
|
224
221
|
const [claimLogs, claimNativeLogs] = await Promise.all([
|
|
225
|
-
this.
|
|
226
|
-
this.
|
|
222
|
+
this.podBridge.queryFilter(claimFilter),
|
|
223
|
+
this.podBridge.queryFilter(claimNativeFilter)
|
|
227
224
|
]);
|
|
228
225
|
// Create a map of claimed request IDs
|
|
229
226
|
const claimedMap = new Map();
|
|
@@ -268,8 +265,7 @@ class PodBridgeTrackerClient {
|
|
|
268
265
|
const tokens = deposits.map(d => d.token);
|
|
269
266
|
const amounts = deposits.map(d => d.amount);
|
|
270
267
|
const tos = deposits.map(d => d.to);
|
|
271
|
-
|
|
272
|
-
return results;
|
|
268
|
+
return await this.podBridge.areRequestsProcessed(ids, tokens, amounts, tos);
|
|
273
269
|
}
|
|
274
270
|
catch (error) {
|
|
275
271
|
console.error('Batch check failed, falling back to individual checks:', error);
|
|
@@ -278,7 +274,7 @@ class PodBridgeTrackerClient {
|
|
|
278
274
|
for (const deposit of deposits) {
|
|
279
275
|
try {
|
|
280
276
|
const hash = this.computeRequestHash(deposit.requestId, deposit.token, deposit.amount, deposit.to);
|
|
281
|
-
const isProcessed = await this.
|
|
277
|
+
const isProcessed = await this.podBridge.processedRequests(hash);
|
|
282
278
|
results.push(isProcessed);
|
|
283
279
|
}
|
|
284
280
|
catch (_a) {
|
|
@@ -299,5 +295,157 @@ class PodBridgeTrackerClient {
|
|
|
299
295
|
computeRequestHash(id, token, amount, to) {
|
|
300
296
|
return ethers_1.ethers.solidityPackedKeccak256(['uint256', 'address', 'uint256', 'address'], [id, token, amount, to]);
|
|
301
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Get certified log for a deposit transaction from POD
|
|
300
|
+
* This is required to claim tokens on Sepolia from POD deposits
|
|
301
|
+
* @param txHash Transaction hash of the deposit on POD
|
|
302
|
+
* @returns Certified log with attestations and merkle proof
|
|
303
|
+
*/
|
|
304
|
+
async getDepositCertifiedLog(txHash) {
|
|
305
|
+
// Fetch the transaction receipt with POD metadata
|
|
306
|
+
const receipt = await this.podProvider.getTransactionReceipt(txHash);
|
|
307
|
+
if (!receipt) {
|
|
308
|
+
throw new Error(`Transaction receipt not found for hash: ${txHash}`);
|
|
309
|
+
}
|
|
310
|
+
if (!receipt.pod_metadata || !receipt.pod_metadata.attestations) {
|
|
311
|
+
throw new Error(`Transaction ${txHash} does not have POD metadata with attestations`);
|
|
312
|
+
}
|
|
313
|
+
if (receipt.logs.length === 0) {
|
|
314
|
+
throw new Error(`No logs found in transaction ${txHash}`);
|
|
315
|
+
}
|
|
316
|
+
// Find the Deposit or DepositNative event
|
|
317
|
+
const depositLog = receipt.logs.find(log => {
|
|
318
|
+
const topics = log.topics;
|
|
319
|
+
if (topics.length === 0)
|
|
320
|
+
return false;
|
|
321
|
+
const depositEventSig = ethers_1.ethers.id('Deposit(uint256,address,address,address,uint256,uint256,uint256)');
|
|
322
|
+
const depositNativeEventSig = ethers_1.ethers.id('DepositNative(uint256,address,address,uint256,uint256,uint256)');
|
|
323
|
+
return topics[0] === depositEventSig || topics[0] === depositNativeEventSig;
|
|
324
|
+
});
|
|
325
|
+
if (!depositLog) {
|
|
326
|
+
throw new Error(`No Deposit or DepositNative event found in transaction ${txHash}`);
|
|
327
|
+
}
|
|
328
|
+
// Compute receipt root (hash of receipt without pod_metadata)
|
|
329
|
+
const receiptRoot = this.computeReceiptRoot(receipt);
|
|
330
|
+
// Generate aggregate signature from attestations
|
|
331
|
+
const sortedAttestations = [...receipt.pod_metadata.attestations].sort((a, b) => a.timestamp - b.timestamp);
|
|
332
|
+
const aggregateSignature = this.aggregateSignatures(sortedAttestations);
|
|
333
|
+
const sortedTimestamps = sortedAttestations.map(att => BigInt(Math.floor(att.timestamp / 1000000))); // Convert microseconds to seconds
|
|
334
|
+
// Generate merkle proof for the log
|
|
335
|
+
const proof = this.generateMerkleProof(receipt.logs, parseInt(depositLog.logIndex));
|
|
336
|
+
// Compute leaf hash
|
|
337
|
+
const logHash = this.hashLog({
|
|
338
|
+
addr: depositLog.address,
|
|
339
|
+
topics: depositLog.topics,
|
|
340
|
+
data: depositLog.data
|
|
341
|
+
});
|
|
342
|
+
const leaf = this.hashLeaf(`log_hashes[${depositLog.logIndex}]`, logHash);
|
|
343
|
+
return {
|
|
344
|
+
log: {
|
|
345
|
+
addr: depositLog.address,
|
|
346
|
+
topics: depositLog.topics,
|
|
347
|
+
data: depositLog.data
|
|
348
|
+
},
|
|
349
|
+
logIndex: BigInt(depositLog.logIndex),
|
|
350
|
+
certificate: {
|
|
351
|
+
leaf,
|
|
352
|
+
certifiedReceipt: {
|
|
353
|
+
receiptRoot,
|
|
354
|
+
aggregateSignature,
|
|
355
|
+
sortedAttestationTimestamps: sortedTimestamps
|
|
356
|
+
},
|
|
357
|
+
proof: {
|
|
358
|
+
path: proof
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Compute receipt root hash
|
|
365
|
+
* @private
|
|
366
|
+
*/
|
|
367
|
+
computeReceiptRoot(receipt) {
|
|
368
|
+
// Create a simplified receipt object without pod_metadata for hashing
|
|
369
|
+
const logs = receipt.logs.map(log => ({
|
|
370
|
+
address: log.address,
|
|
371
|
+
topics: log.topics,
|
|
372
|
+
data: log.data
|
|
373
|
+
}));
|
|
374
|
+
// Receipt RLP encoding fields (simplified - matches POD's receipt structure)
|
|
375
|
+
const receiptData = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['uint8', 'uint256', 'bytes32', 'uint256', 'tuple(address,bytes32[],bytes)[]'], [
|
|
376
|
+
parseInt(receipt.status),
|
|
377
|
+
BigInt(receipt.cumulativeGasUsed),
|
|
378
|
+
receipt.logsBloom,
|
|
379
|
+
BigInt(receipt.gasUsed),
|
|
380
|
+
logs.map(log => [log.address, log.topics, log.data])
|
|
381
|
+
]);
|
|
382
|
+
return ethers_1.ethers.keccak256(receiptData);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Aggregate signatures from attestations
|
|
386
|
+
* @private
|
|
387
|
+
*/
|
|
388
|
+
aggregateSignatures(attestations) {
|
|
389
|
+
// Concatenate all signatures (r, s, v format)
|
|
390
|
+
const signatures = attestations.map(att => {
|
|
391
|
+
const r = att.signature.r.startsWith('0x') ? att.signature.r.slice(2) : att.signature.r;
|
|
392
|
+
const s = att.signature.s.startsWith('0x') ? att.signature.s.slice(2) : att.signature.s;
|
|
393
|
+
const v = att.signature.v.startsWith('0x') ? att.signature.v.slice(2) : att.signature.v;
|
|
394
|
+
return r + s + v.padStart(2, '0');
|
|
395
|
+
});
|
|
396
|
+
return '0x' + signatures.join('');
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Hash a log entry
|
|
400
|
+
* @private
|
|
401
|
+
*/
|
|
402
|
+
hashLog(log) {
|
|
403
|
+
return ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['address', 'bytes32[]', 'bytes'], [log.addr, log.topics, log.data]));
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Hash a leaf for merkle tree
|
|
407
|
+
* @private
|
|
408
|
+
*/
|
|
409
|
+
hashLeaf(key, value) {
|
|
410
|
+
return ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['bytes', 'bytes32'], [ethers_1.ethers.toUtf8Bytes(key), value]));
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Generate merkle proof for a log at given index
|
|
414
|
+
* @private
|
|
415
|
+
*/
|
|
416
|
+
generateMerkleProof(logs, logIndex) {
|
|
417
|
+
// Hash all logs
|
|
418
|
+
const logHashes = logs.map((log, idx) => this.hashLeaf(`log_hashes[${idx}]`, this.hashLog({
|
|
419
|
+
addr: log.address,
|
|
420
|
+
topics: log.topics,
|
|
421
|
+
data: log.data
|
|
422
|
+
})));
|
|
423
|
+
// Build merkle tree and generate proof
|
|
424
|
+
const proof = [];
|
|
425
|
+
let currentIndex = logIndex;
|
|
426
|
+
let currentLevel = [...logHashes];
|
|
427
|
+
while (currentLevel.length > 1) {
|
|
428
|
+
const nextLevel = [];
|
|
429
|
+
for (let i = 0; i < currentLevel.length; i += 2) {
|
|
430
|
+
if (i + 1 < currentLevel.length) {
|
|
431
|
+
// Pair exists
|
|
432
|
+
const left = currentLevel[i];
|
|
433
|
+
const right = currentLevel[i + 1];
|
|
434
|
+
// Add sibling to proof if current index is in this pair
|
|
435
|
+
if (i === currentIndex || i + 1 === currentIndex) {
|
|
436
|
+
proof.push(i === currentIndex ? right : left);
|
|
437
|
+
}
|
|
438
|
+
nextLevel.push(ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'bytes32'], [left, right])));
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Odd node, promote to next level
|
|
442
|
+
nextLevel.push(currentLevel[i]);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
currentIndex = Math.floor(currentIndex / 2);
|
|
446
|
+
currentLevel = nextLevel;
|
|
447
|
+
}
|
|
448
|
+
return proof;
|
|
449
|
+
}
|
|
302
450
|
}
|
|
303
451
|
exports.PodBridgeTrackerClient = PodBridgeTrackerClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { PodToBridgedActionClient } from './clients/action/pod-to-bridged-client';
|
|
2
|
+
export { BridgedToPodActionClient } from './clients/action/bridged-to-pod-client';
|
|
2
3
|
export { PodBridgeTrackerClient } from './clients/tracker/client';
|
|
3
4
|
export { POD_BRIDGE_ABI } from './libs/abi/bridge.abi';
|
|
4
|
-
export { PodBridgeConfig, BridgeRequest, BridgeRequestWithType, DepositType, UnsignedTransaction, CertifiedLog, } from './libs/types/pod-bridge.types';
|
|
5
|
+
export { PodBridgeConfig, BridgeRequest, BridgeRequestWithType, DepositType, UnsignedTransaction, CertifiedLog, PodAttestation, PodMetadata, PodTransactionReceipt, } from './libs/types/pod-bridge.types';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DepositType = exports.POD_BRIDGE_ABI = exports.PodBridgeTrackerClient = exports.
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "
|
|
6
|
-
var
|
|
7
|
-
Object.defineProperty(exports, "
|
|
3
|
+
exports.DepositType = exports.POD_BRIDGE_ABI = exports.PodBridgeTrackerClient = exports.BridgedToPodActionClient = exports.PodToBridgedActionClient = void 0;
|
|
4
|
+
var pod_to_bridged_client_1 = require("./clients/action/pod-to-bridged-client");
|
|
5
|
+
Object.defineProperty(exports, "PodToBridgedActionClient", { enumerable: true, get: function () { return pod_to_bridged_client_1.PodToBridgedActionClient; } });
|
|
6
|
+
var bridged_to_pod_client_1 = require("./clients/action/bridged-to-pod-client");
|
|
7
|
+
Object.defineProperty(exports, "BridgedToPodActionClient", { enumerable: true, get: function () { return bridged_to_pod_client_1.BridgedToPodActionClient; } });
|
|
8
|
+
var client_1 = require("./clients/tracker/client");
|
|
9
|
+
Object.defineProperty(exports, "PodBridgeTrackerClient", { enumerable: true, get: function () { return client_1.PodBridgeTrackerClient; } });
|
|
8
10
|
var bridge_abi_1 = require("./libs/abi/bridge.abi");
|
|
9
11
|
Object.defineProperty(exports, "POD_BRIDGE_ABI", { enumerable: true, get: function () { return bridge_abi_1.POD_BRIDGE_ABI; } });
|
|
10
12
|
var pod_bridge_types_1 = require("./libs/types/pod-bridge.types");
|
|
@@ -2,9 +2,22 @@ export interface PodBridgeChainConfig {
|
|
|
2
2
|
rpcUrl: string;
|
|
3
3
|
contractAddress: string;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for the bridge
|
|
7
|
+
* * bridged: Configuration for the bridged chain. BridgeDepositWithdraw contract address
|
|
8
|
+
* * pod: Configuration for the POD chain. BridgeMintBurn contract address
|
|
9
|
+
*/
|
|
5
10
|
export interface PodBridgeConfig {
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
bridged: PodBridgeChainConfig;
|
|
12
|
+
pod: PodBridgeChainConfig;
|
|
13
|
+
}
|
|
14
|
+
export interface PodBridgeActionsClientConfig {
|
|
15
|
+
bridged: {
|
|
16
|
+
contractAddress: string;
|
|
17
|
+
};
|
|
18
|
+
pod: {
|
|
19
|
+
contractAddress: string;
|
|
20
|
+
};
|
|
8
21
|
}
|
|
9
22
|
export interface BridgeRequest {
|
|
10
23
|
requestId: string;
|
|
@@ -41,9 +54,56 @@ export interface CertifiedLog {
|
|
|
41
54
|
topics: string[];
|
|
42
55
|
data: string;
|
|
43
56
|
};
|
|
44
|
-
|
|
57
|
+
logIndex: bigint | string;
|
|
58
|
+
certificate: {
|
|
59
|
+
leaf: string;
|
|
60
|
+
certifiedReceipt: {
|
|
61
|
+
receiptRoot: string;
|
|
62
|
+
aggregateSignature: string;
|
|
63
|
+
sortedAttestationTimestamps: bigint[] | string[];
|
|
64
|
+
};
|
|
65
|
+
proof: {
|
|
66
|
+
path: string[];
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface PodAttestation {
|
|
71
|
+
public_key: string;
|
|
72
|
+
signature: {
|
|
45
73
|
r: string;
|
|
46
74
|
s: string;
|
|
47
|
-
v:
|
|
75
|
+
v: string;
|
|
76
|
+
yParity: string;
|
|
77
|
+
};
|
|
78
|
+
timestamp: number;
|
|
79
|
+
}
|
|
80
|
+
export interface PodMetadata {
|
|
81
|
+
attestations: PodAttestation[];
|
|
82
|
+
}
|
|
83
|
+
export interface PodTransactionReceipt {
|
|
84
|
+
blockHash: string;
|
|
85
|
+
blockNumber: string;
|
|
86
|
+
contractAddress: string | null;
|
|
87
|
+
cumulativeGasUsed: string;
|
|
88
|
+
effectiveGasPrice: string;
|
|
89
|
+
from: string;
|
|
90
|
+
gasUsed: string;
|
|
91
|
+
logs: Array<{
|
|
92
|
+
address: string;
|
|
93
|
+
topics: string[];
|
|
94
|
+
data: string;
|
|
95
|
+
blockNumber: string;
|
|
96
|
+
transactionHash: string;
|
|
97
|
+
transactionIndex: string;
|
|
98
|
+
blockHash: string;
|
|
99
|
+
logIndex: string;
|
|
100
|
+
removed: boolean;
|
|
48
101
|
}>;
|
|
102
|
+
logsBloom: string;
|
|
103
|
+
pod_metadata: PodMetadata;
|
|
104
|
+
status: string;
|
|
105
|
+
to: string;
|
|
106
|
+
transactionHash: string;
|
|
107
|
+
transactionIndex: string;
|
|
108
|
+
type: string;
|
|
49
109
|
}
|