@tapforce/pod-bridge-sdk 1.0.1
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 +454 -0
- package/dist/clients/action/client.d.ts +93 -0
- package/dist/clients/action/client.js +125 -0
- package/dist/clients/tracker/client.d.ts +57 -0
- package/dist/clients/tracker/client.js +303 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11 -0
- package/dist/libs/abi/bridge.abi.d.ts +1 -0
- package/dist/libs/abi/bridge.abi.js +31 -0
- package/dist/libs/types/pod-bridge.types.d.ts +49 -0
- package/dist/libs/types/pod-bridge.types.js +8 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# Pod Bridge SDK
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for interacting with the Pod Bridge - enabling cross-chain token transfers between POD and other EVM chains (e.g., Sepolia).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🌉 **Bridge Token Deposits**: Deposit ERC20 tokens and native tokens to the bridge
|
|
8
|
+
- 🔍 **Track Bridge Requests**: Query deposits sent by or received by any address
|
|
9
|
+
- ✅ **Claim Status Tracking**: Monitor claim status and finality of bridge requests
|
|
10
|
+
- 📝 **Transaction Building**: Generate unsigned transactions ready for signing
|
|
11
|
+
- 🔐 **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @tapforce/pod-bridge-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
or
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn add @tapforce/pod-bridge-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add @tapforce/pod-bridge-sdk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### Import the SDK
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import {
|
|
37
|
+
PodBridgeActionClient,
|
|
38
|
+
PodBridgeTrackerClient,
|
|
39
|
+
PodBridgeConfig
|
|
40
|
+
} from '@tapforce/pod-bridge-sdk';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Configuration
|
|
44
|
+
|
|
45
|
+
Create a configuration object with source and destination chain details:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const config: PodBridgeConfig = {
|
|
49
|
+
source: {
|
|
50
|
+
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
51
|
+
contractAddress: '0x...' // Source chain bridge contract
|
|
52
|
+
},
|
|
53
|
+
destination: {
|
|
54
|
+
rpcUrl: 'https://pod-rpc.example.com',
|
|
55
|
+
contractAddress: '0x...' // Destination chain bridge contract
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### 1. Action Client - Creating Transactions
|
|
63
|
+
|
|
64
|
+
The `PodBridgeActionClient` creates unsigned transactions that you can sign and broadcast.
|
|
65
|
+
|
|
66
|
+
#### Deposit ERC20 Tokens
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const actionClient = new PodBridgeActionClient();
|
|
70
|
+
|
|
71
|
+
// Create unsigned transaction for ERC20 deposit
|
|
72
|
+
const unsignedTx = actionClient.depositToken({
|
|
73
|
+
token: '0x...', // ERC20 token address
|
|
74
|
+
amount: '1000000000000000000', // Amount in wei (1 token with 18 decimals)
|
|
75
|
+
recipient: '0x...', // Recipient address on destination chain
|
|
76
|
+
contractAddress: config.source.contractAddress,
|
|
77
|
+
from: '0x...' // Optional: sender address
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Sign and send transaction using your wallet/provider
|
|
81
|
+
// const tx = await signer.sendTransaction(unsignedTx);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Deposit Native Tokens (ETH)
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Create unsigned transaction for native token deposit
|
|
88
|
+
const unsignedTx = actionClient.depositNative({
|
|
89
|
+
amount: '1000000000000000000', // Amount in wei (1 ETH)
|
|
90
|
+
recipient: '0x...', // Recipient address on destination chain
|
|
91
|
+
contractAddress: config.source.contractAddress,
|
|
92
|
+
from: '0x...' // Optional: sender address
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Sign and send transaction
|
|
96
|
+
// const tx = await signer.sendTransaction(unsignedTx);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Claim Tokens (with Certificate) - Sepolia from POD
|
|
100
|
+
|
|
101
|
+
For claiming on Sepolia from POD deposits, use certificate-based claims:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Obtain certified log from POD certificate system
|
|
105
|
+
const certifiedLog = {
|
|
106
|
+
log: {
|
|
107
|
+
addr: '0x...',
|
|
108
|
+
topics: ['0x...'],
|
|
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,
|
|
120
|
+
from: '0x...' // Optional: claimer address
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// For native tokens
|
|
124
|
+
const unsignedNativeTx = actionClient.claimNativeWithCertificate({
|
|
125
|
+
certifiedLog,
|
|
126
|
+
contractAddress: config.destination.contractAddress,
|
|
127
|
+
from: '0x...'
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Claim Tokens (with Block Number) - POD from Sepolia
|
|
132
|
+
|
|
133
|
+
For claiming on POD from Sepolia deposits, use block number-based claims:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Create unsigned claim transaction for ERC20
|
|
137
|
+
const unsignedTx = actionClient.claimWithBlockNumber({
|
|
138
|
+
id: '123', // Request ID from deposit event
|
|
139
|
+
token: '0x...', // Token address on source chain
|
|
140
|
+
blockNumber: 12345678, // Block number of deposit on source chain
|
|
141
|
+
contractAddress: config.destination.contractAddress,
|
|
142
|
+
from: '0x...' // Optional: claimer address
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// For native tokens
|
|
146
|
+
const unsignedNativeTx = actionClient.claimNativeWithBlockNumber({
|
|
147
|
+
id: '123',
|
|
148
|
+
blockNumber: 12345678,
|
|
149
|
+
contractAddress: config.destination.contractAddress,
|
|
150
|
+
from: '0x...'
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 2. Tracker Client - Querying Bridge Activity
|
|
155
|
+
|
|
156
|
+
The `PodBridgeTrackerClient` queries the blockchain for deposit and claim events.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const trackerClient = new PodBridgeTrackerClient(config);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Get Deposits Sent by Address
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Get all deposits sent by an address
|
|
166
|
+
const deposits = await trackerClient.getDepositsSentBy(
|
|
167
|
+
'0x...', // sender address
|
|
168
|
+
0 // starting block (optional, default: 0)
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
deposits.forEach(deposit => {
|
|
172
|
+
console.log(`Request ID: ${deposit.requestId}`);
|
|
173
|
+
console.log(`From: ${deposit.from} -> To: ${deposit.to}`);
|
|
174
|
+
console.log(`Token: ${deposit.token}`);
|
|
175
|
+
console.log(`Amount: ${deposit.amount}`);
|
|
176
|
+
console.log(`Claimed: ${deposit.isClaimed}`);
|
|
177
|
+
if (deposit.isClaimed) {
|
|
178
|
+
console.log(`Claimed by: ${deposit.claimedBy}`);
|
|
179
|
+
console.log(`Claimed tx: ${deposit.claimedTxHash}`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Get Deposits Received by Address
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// Get all deposits where the address is the recipient
|
|
188
|
+
const deposits = await trackerClient.getDepositsReceivedBy(
|
|
189
|
+
'0x...', // recipient address
|
|
190
|
+
0 // starting block (optional)
|
|
191
|
+
);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Get All Deposits (Sent + Received)
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Get all deposits related to an address (both sent and received)
|
|
198
|
+
const allDeposits = await trackerClient.getAllDepositsFor(
|
|
199
|
+
'0x...', // address
|
|
200
|
+
0 // starting block (optional)
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Deposits include a 'type' field indicating SENT or RECEIVED
|
|
204
|
+
allDeposits.forEach(deposit => {
|
|
205
|
+
console.log(`Type: ${deposit.type}`); // 'sent' or 'received'
|
|
206
|
+
console.log(`Request ID: ${deposit.requestId}`);
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### Check if Deposit Can Be Claimed
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Check if a deposit has been finalized and can be claimed
|
|
214
|
+
const canClaim = await trackerClient.canBeClaimed(deposit);
|
|
215
|
+
|
|
216
|
+
if (canClaim) {
|
|
217
|
+
console.log('Deposit is ready to be claimed!');
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### Batch Check Processed Requests
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// Check multiple deposits at once
|
|
225
|
+
const deposits = await trackerClient.getDepositsSentBy('0x...');
|
|
226
|
+
const processedStatuses = await trackerClient.areRequestsProcessed(deposits);
|
|
227
|
+
|
|
228
|
+
processedStatuses.forEach((isProcessed, index) => {
|
|
229
|
+
console.log(`Deposit ${deposits[index].requestId} processed: ${isProcessed}`);
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## API Reference
|
|
234
|
+
|
|
235
|
+
### PodBridgeActionClient
|
|
236
|
+
|
|
237
|
+
#### Methods
|
|
238
|
+
|
|
239
|
+
- **`depositToken(args)`**: Create unsigned transaction for ERC20 deposit
|
|
240
|
+
- `token`: Token address
|
|
241
|
+
- `amount`: Amount in wei (string | bigint)
|
|
242
|
+
- `recipient`: Recipient address on destination chain
|
|
243
|
+
- `contractAddress`: Bridge contract address
|
|
244
|
+
- `from?`: Optional sender address
|
|
245
|
+
|
|
246
|
+
- **`depositNative(args)`**: Create unsigned transaction for native token deposit
|
|
247
|
+
- `amount`: Amount in wei (string | bigint)
|
|
248
|
+
- `recipient`: Recipient address on destination chain
|
|
249
|
+
- `contractAddress`: Bridge contract address
|
|
250
|
+
- `from?`: Optional sender address
|
|
251
|
+
|
|
252
|
+
- **`claimWithCertificate(args)`**: Create unsigned transaction for claiming with certificate
|
|
253
|
+
- `certifiedLog`: Certified log from POD certificate system
|
|
254
|
+
- `contractAddress`: Bridge contract address
|
|
255
|
+
- `from?`: Optional claimer address
|
|
256
|
+
|
|
257
|
+
- **`claimNativeWithCertificate(args)`**: Create unsigned transaction for claiming native tokens with certificate
|
|
258
|
+
- `certifiedLog`: Certified log from POD certificate system
|
|
259
|
+
- `contractAddress`: Bridge contract address
|
|
260
|
+
- `from?`: Optional claimer address
|
|
261
|
+
|
|
262
|
+
- **`claimWithBlockNumber(args)`**: Create unsigned transaction for claiming with block number
|
|
263
|
+
- `id`: Request ID
|
|
264
|
+
- `token`: Token address
|
|
265
|
+
- `blockNumber`: Block number of deposit
|
|
266
|
+
- `contractAddress`: Bridge contract address
|
|
267
|
+
- `from?`: Optional claimer address
|
|
268
|
+
|
|
269
|
+
- **`claimNativeWithBlockNumber(args)`**: Create unsigned transaction for claiming native tokens with block number
|
|
270
|
+
- `id`: Request ID
|
|
271
|
+
- `blockNumber`: Block number of deposit
|
|
272
|
+
- `contractAddress`: Bridge contract address
|
|
273
|
+
- `from?`: Optional claimer address
|
|
274
|
+
|
|
275
|
+
### PodBridgeTrackerClient
|
|
276
|
+
|
|
277
|
+
#### Methods
|
|
278
|
+
|
|
279
|
+
- **`getDepositsSentBy(address, fromBlock?)`**: Get deposits sent by address
|
|
280
|
+
- Returns: `Promise<BridgeRequest[]>`
|
|
281
|
+
|
|
282
|
+
- **`getDepositsReceivedBy(address, fromBlock?)`**: Get deposits received by address
|
|
283
|
+
- Returns: `Promise<BridgeRequest[]>`
|
|
284
|
+
|
|
285
|
+
- **`getAllDepositsFor(address, fromBlock?)`**: Get all deposits for address (sent + received)
|
|
286
|
+
- Returns: `Promise<BridgeRequestWithType[]>`
|
|
287
|
+
|
|
288
|
+
- **`canBeClaimed(deposit)`**: Check if deposit can be claimed
|
|
289
|
+
- Returns: `Promise<boolean>`
|
|
290
|
+
|
|
291
|
+
- **`areRequestsProcessed(deposits)`**: Batch check if requests are processed
|
|
292
|
+
- Returns: `Promise<boolean[]>`
|
|
293
|
+
|
|
294
|
+
## Types
|
|
295
|
+
|
|
296
|
+
### BridgeRequest
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
interface BridgeRequest {
|
|
300
|
+
requestId: string;
|
|
301
|
+
blockNumber: number;
|
|
302
|
+
timestamp: number;
|
|
303
|
+
txHash: string;
|
|
304
|
+
from: string;
|
|
305
|
+
to: string;
|
|
306
|
+
token: string; // Token address or ZeroAddress for native
|
|
307
|
+
amount: string; // Amount in wei
|
|
308
|
+
isClaimed: boolean;
|
|
309
|
+
claimedTxHash?: string;
|
|
310
|
+
claimedAt?: number;
|
|
311
|
+
claimedBy?: string;
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### UnsignedTransaction
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
interface UnsignedTransaction {
|
|
319
|
+
to: string;
|
|
320
|
+
data: string;
|
|
321
|
+
value: string;
|
|
322
|
+
from?: string;
|
|
323
|
+
chainId?: number;
|
|
324
|
+
gasLimit?: bigint;
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### CertifiedLog
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
interface CertifiedLog {
|
|
332
|
+
log: {
|
|
333
|
+
addr: string;
|
|
334
|
+
topics: string[];
|
|
335
|
+
data: string;
|
|
336
|
+
};
|
|
337
|
+
sigs: Array<{
|
|
338
|
+
r: string;
|
|
339
|
+
s: string;
|
|
340
|
+
v: number;
|
|
341
|
+
}>;
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Bridge Types
|
|
346
|
+
|
|
347
|
+
The SDK supports two bridge implementations:
|
|
348
|
+
|
|
349
|
+
1. **BridgeDepositWithdraw** (Certificate-based)
|
|
350
|
+
- Used for claiming on Sepolia from POD deposits
|
|
351
|
+
- Requires certified logs from POD's certificate system
|
|
352
|
+
- Methods: `claimWithCertificate`, `claimNativeWithCertificate`
|
|
353
|
+
|
|
354
|
+
2. **BridgeMintBurn** (Precompile-based)
|
|
355
|
+
- Used for claiming on POD from Sepolia deposits
|
|
356
|
+
- Uses block number verification via precompiles
|
|
357
|
+
- Methods: `claimWithBlockNumber`, `claimNativeWithBlockNumber`
|
|
358
|
+
|
|
359
|
+
## Examples
|
|
360
|
+
|
|
361
|
+
### Complete Deposit and Claim Flow
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { ethers } from 'ethers';
|
|
365
|
+
import { PodBridgeActionClient, PodBridgeTrackerClient } from '@tapforce/pod-bridge-sdk';
|
|
366
|
+
|
|
367
|
+
// Setup
|
|
368
|
+
const config = {
|
|
369
|
+
source: {
|
|
370
|
+
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
371
|
+
contractAddress: '0xSourceBridgeContract'
|
|
372
|
+
},
|
|
373
|
+
destination: {
|
|
374
|
+
rpcUrl: 'https://pod-rpc.example.com',
|
|
375
|
+
contractAddress: '0xDestBridgeContract'
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const actionClient = new PodBridgeActionClient();
|
|
380
|
+
const trackerClient = new PodBridgeTrackerClient(config);
|
|
381
|
+
const provider = new ethers.JsonRpcProvider(config.source.rpcUrl);
|
|
382
|
+
const signer = new ethers.Wallet('PRIVATE_KEY', provider);
|
|
383
|
+
|
|
384
|
+
// Step 1: Deposit tokens
|
|
385
|
+
const depositTx = actionClient.depositToken({
|
|
386
|
+
token: '0xTokenAddress',
|
|
387
|
+
amount: ethers.parseEther('10').toString(),
|
|
388
|
+
recipient: await signer.getAddress(),
|
|
389
|
+
contractAddress: config.source.contractAddress
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const tx = await signer.sendTransaction(depositTx);
|
|
393
|
+
await tx.wait();
|
|
394
|
+
console.log(`Deposit tx: ${tx.hash}`);
|
|
395
|
+
|
|
396
|
+
// Step 2: Track deposit
|
|
397
|
+
const deposits = await trackerClient.getDepositsSentBy(await signer.getAddress());
|
|
398
|
+
const latestDeposit = deposits[0];
|
|
399
|
+
|
|
400
|
+
console.log(`Request ID: ${latestDeposit.requestId}`);
|
|
401
|
+
console.log(`Amount: ${ethers.formatEther(latestDeposit.amount)}`);
|
|
402
|
+
|
|
403
|
+
// Step 3: Wait for finality and claim
|
|
404
|
+
const canClaim = await trackerClient.canBeClaimed(latestDeposit);
|
|
405
|
+
if (canClaim) {
|
|
406
|
+
const claimTx = actionClient.claimWithBlockNumber({
|
|
407
|
+
id: latestDeposit.requestId,
|
|
408
|
+
token: latestDeposit.token,
|
|
409
|
+
blockNumber: latestDeposit.blockNumber,
|
|
410
|
+
contractAddress: config.destination.contractAddress
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Submit claim on destination chain
|
|
414
|
+
// const destSigner = new ethers.Wallet('PRIVATE_KEY', destProvider);
|
|
415
|
+
// const claim = await destSigner.sendTransaction(claimTx);
|
|
416
|
+
// await claim.wait();
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Development
|
|
421
|
+
|
|
422
|
+
### Build
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
npm run build
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Clean
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
npm run clean
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Dependencies
|
|
435
|
+
|
|
436
|
+
- **ethers**: ^6.15.0 - Ethereum library for interacting with contracts
|
|
437
|
+
- **typescript**: ^5.8.3 - TypeScript support
|
|
438
|
+
|
|
439
|
+
## License
|
|
440
|
+
|
|
441
|
+
ISC
|
|
442
|
+
|
|
443
|
+
## Repository
|
|
444
|
+
|
|
445
|
+
- **Homepage**: [https://github.com/tapforce/pod-ts-bridge-sdk](https://github.com/tapforce/pod-ts-bridge-sdk)
|
|
446
|
+
- **Issues**: [https://github.com/tapforce/pod-ts-bridge-sdk/issues](https://github.com/tapforce/pod-ts-bridge-sdk/issues)
|
|
447
|
+
|
|
448
|
+
## Contributing
|
|
449
|
+
|
|
450
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
451
|
+
|
|
452
|
+
## Author
|
|
453
|
+
|
|
454
|
+
Tapforce
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { UnsignedTransaction, CertifiedLog } from "../../libs/types/pod-bridge.types";
|
|
2
|
+
export declare class PodBridgeActionClient {
|
|
3
|
+
private readonly iface;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* Create unsigned transaction for depositing ERC20 tokens
|
|
7
|
+
* @param args.token Token address to deposit
|
|
8
|
+
* @param args.amount Amount to deposit (in wei)
|
|
9
|
+
* @param args.recipient Recipient address on destination chain
|
|
10
|
+
* @param args.contractAddress Bridge contract address (source 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
|
+
recipient: string;
|
|
18
|
+
contractAddress: string;
|
|
19
|
+
from?: string;
|
|
20
|
+
}): UnsignedTransaction;
|
|
21
|
+
/**
|
|
22
|
+
* Create unsigned transaction for depositing native tokens (ETH)
|
|
23
|
+
* @param args.amount Amount to deposit (in wei) - also set as transaction value
|
|
24
|
+
* @param args.recipient Recipient address on destination chain
|
|
25
|
+
* @param args.contractAddress Bridge contract address (source chain)
|
|
26
|
+
* @param args.from Optional sender address
|
|
27
|
+
* @returns Unsigned transaction template with encoded depositNative call
|
|
28
|
+
*/
|
|
29
|
+
depositNative(args: {
|
|
30
|
+
amount: string | bigint;
|
|
31
|
+
recipient: string;
|
|
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;
|
|
59
|
+
from?: string;
|
|
60
|
+
}): UnsignedTransaction;
|
|
61
|
+
/**
|
|
62
|
+
* Create unsigned transaction for claiming tokens (BridgeMintBurn - using precompiles)
|
|
63
|
+
* Used for claiming on POD from Sepolia deposits
|
|
64
|
+
* @param args.id Request ID from deposit event
|
|
65
|
+
* @param args.token Token address that was deposited on source chain
|
|
66
|
+
* @param args.blockNumber Block number of the deposit on source chain
|
|
67
|
+
* @param args.contractAddress Bridge contract address (destination chain)
|
|
68
|
+
* @param args.from Optional sender address
|
|
69
|
+
* @returns Unsigned transaction template with encoded claim call
|
|
70
|
+
*/
|
|
71
|
+
claimWithBlockNumber(args: {
|
|
72
|
+
id: string | bigint;
|
|
73
|
+
token: string;
|
|
74
|
+
blockNumber: string | bigint | number;
|
|
75
|
+
contractAddress: string;
|
|
76
|
+
from?: string;
|
|
77
|
+
}): UnsignedTransaction;
|
|
78
|
+
/**
|
|
79
|
+
* Create unsigned transaction for claiming native tokens (BridgeMintBurn - using precompiles)
|
|
80
|
+
* Used for claiming native tokens on POD from Sepolia deposits
|
|
81
|
+
* @param args.id Request ID from deposit event
|
|
82
|
+
* @param args.blockNumber Block number of the deposit on source chain
|
|
83
|
+
* @param args.contractAddress Bridge contract address (destination chain)
|
|
84
|
+
* @param args.from Optional sender address
|
|
85
|
+
* @returns Unsigned transaction template with encoded claimNative call
|
|
86
|
+
*/
|
|
87
|
+
claimNativeWithBlockNumber(args: {
|
|
88
|
+
id: string | bigint;
|
|
89
|
+
blockNumber: string | bigint | number;
|
|
90
|
+
contractAddress: string;
|
|
91
|
+
from?: string;
|
|
92
|
+
}): UnsignedTransaction;
|
|
93
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PodBridgeActionClient = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const bridge_abi_1 = require("../../libs/abi/bridge.abi");
|
|
6
|
+
class PodBridgeActionClient {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.iface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Create unsigned transaction for depositing ERC20 tokens
|
|
12
|
+
* @param args.token Token address to deposit
|
|
13
|
+
* @param args.amount Amount to deposit (in wei)
|
|
14
|
+
* @param args.recipient Recipient address on destination chain
|
|
15
|
+
* @param args.contractAddress Bridge contract address (source 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.recipient
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
to: args.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 (ETH)
|
|
34
|
+
* @param args.amount Amount to deposit (in wei) - also set as transaction value
|
|
35
|
+
* @param args.recipient Recipient address on destination chain
|
|
36
|
+
* @param args.contractAddress Bridge contract address (source chain)
|
|
37
|
+
* @param args.from Optional sender address
|
|
38
|
+
* @returns Unsigned transaction template with encoded depositNative call
|
|
39
|
+
*/
|
|
40
|
+
depositNative(args) {
|
|
41
|
+
const data = this.iface.encodeFunctionData('depositNative', [args.recipient]);
|
|
42
|
+
return {
|
|
43
|
+
to: args.contractAddress,
|
|
44
|
+
data,
|
|
45
|
+
value: args.amount.toString(),
|
|
46
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
47
|
+
};
|
|
48
|
+
}
|
|
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
|
+
/**
|
|
84
|
+
* Create unsigned transaction for claiming tokens (BridgeMintBurn - using precompiles)
|
|
85
|
+
* Used for claiming on POD from Sepolia deposits
|
|
86
|
+
* @param args.id Request ID from deposit event
|
|
87
|
+
* @param args.token Token address that was deposited on source chain
|
|
88
|
+
* @param args.blockNumber Block number of the deposit on source chain
|
|
89
|
+
* @param args.contractAddress Bridge contract address (destination chain)
|
|
90
|
+
* @param args.from Optional sender address
|
|
91
|
+
* @returns Unsigned transaction template with encoded claim call
|
|
92
|
+
*/
|
|
93
|
+
claimWithBlockNumber(args) {
|
|
94
|
+
const data = this.iface.encodeFunctionData('claim(uint256,address,uint256)', [
|
|
95
|
+
args.id,
|
|
96
|
+
args.token,
|
|
97
|
+
args.blockNumber
|
|
98
|
+
]);
|
|
99
|
+
return {
|
|
100
|
+
to: args.contractAddress,
|
|
101
|
+
data,
|
|
102
|
+
value: '0',
|
|
103
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create unsigned transaction for claiming native tokens (BridgeMintBurn - using precompiles)
|
|
108
|
+
* Used for claiming native tokens on POD from Sepolia deposits
|
|
109
|
+
* @param args.id Request ID from deposit event
|
|
110
|
+
* @param args.blockNumber Block number of the deposit on source chain
|
|
111
|
+
* @param args.contractAddress Bridge contract address (destination chain)
|
|
112
|
+
* @param args.from Optional sender address
|
|
113
|
+
* @returns Unsigned transaction template with encoded claimNative call
|
|
114
|
+
*/
|
|
115
|
+
claimNativeWithBlockNumber(args) {
|
|
116
|
+
const data = this.iface.encodeFunctionData('claimNative(uint256,uint256)', [args.id, args.blockNumber]);
|
|
117
|
+
return {
|
|
118
|
+
to: args.contractAddress,
|
|
119
|
+
data,
|
|
120
|
+
value: '0',
|
|
121
|
+
from: args === null || args === void 0 ? void 0 : args.from
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.PodBridgeActionClient = PodBridgeActionClient;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PodBridgeConfig, BridgeRequest, BridgeRequestWithType } from "../../libs/types/pod-bridge.types";
|
|
2
|
+
export declare class PodBridgeTrackerClient {
|
|
3
|
+
private readonly config;
|
|
4
|
+
private readonly sourceProvider;
|
|
5
|
+
private readonly destProvider;
|
|
6
|
+
private readonly sourceBridge;
|
|
7
|
+
private readonly destBridge;
|
|
8
|
+
private readonly iface;
|
|
9
|
+
constructor(config: PodBridgeConfig);
|
|
10
|
+
/**
|
|
11
|
+
* Get all deposits SENT by an address
|
|
12
|
+
* @param address The address that sent deposits
|
|
13
|
+
* @param fromBlock Starting block number (default: 0)
|
|
14
|
+
* @returns Array of bridge requests sent by this address
|
|
15
|
+
*/
|
|
16
|
+
getDepositsSentBy(address: string, fromBlock?: number): Promise<BridgeRequest[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Get all deposits RECEIVED by an address
|
|
19
|
+
* @param address The address that will receive deposits
|
|
20
|
+
* @param fromBlock Starting block number (default: 0)
|
|
21
|
+
* @returns Array of bridge requests sent to this address
|
|
22
|
+
*/
|
|
23
|
+
getDepositsReceivedBy(address: string, fromBlock?: number): Promise<BridgeRequest[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Get ALL deposits for an address (both sent and received)
|
|
26
|
+
* @param address The address to check
|
|
27
|
+
* @param fromBlock Starting block number (default: 0)
|
|
28
|
+
* @returns Array of bridge requests with type indicator
|
|
29
|
+
*/
|
|
30
|
+
getAllDepositsFor(address: string, fromBlock?: number): Promise<BridgeRequestWithType[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if deposits can be claimed (block finalized on source chain)
|
|
33
|
+
* @param deposit The bridge request to check
|
|
34
|
+
* @returns True if the deposit can be claimed
|
|
35
|
+
*/
|
|
36
|
+
canBeClaimed(deposit: Pick<BridgeRequest, 'isClaimed' | 'blockNumber'>): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Batch check claim status for multiple deposits
|
|
39
|
+
* @param deposits Array of deposits to check
|
|
40
|
+
*/
|
|
41
|
+
private updateClaimStatus;
|
|
42
|
+
/**
|
|
43
|
+
* Batch check if multiple requests have been processed on-chain
|
|
44
|
+
* @param deposits Array of deposits to check
|
|
45
|
+
* @returns Array of boolean values
|
|
46
|
+
*/
|
|
47
|
+
areRequestsProcessed(deposits: BridgeRequest[]): Promise<boolean[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Compute the request hash (replicates Solidity's _hashRequest)
|
|
50
|
+
* @param id Request ID
|
|
51
|
+
* @param token Token address
|
|
52
|
+
* @param amount Amount in wei
|
|
53
|
+
* @param to Recipient address
|
|
54
|
+
* @returns Request hash
|
|
55
|
+
*/
|
|
56
|
+
private computeRequestHash;
|
|
57
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PodBridgeTrackerClient = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const pod_bridge_types_1 = require("../../libs/types/pod-bridge.types");
|
|
6
|
+
const bridge_abi_1 = require("../../libs/abi/bridge.abi");
|
|
7
|
+
class PodBridgeTrackerClient {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
// Initialize providers
|
|
11
|
+
this.sourceProvider = new ethers_1.ethers.JsonRpcProvider(config.source.rpcUrl);
|
|
12
|
+
this.destProvider = new ethers_1.ethers.JsonRpcProvider(config.destination.rpcUrl);
|
|
13
|
+
// Initialize contract instances
|
|
14
|
+
this.sourceBridge = new ethers_1.Contract(config.source.contractAddress, bridge_abi_1.POD_BRIDGE_ABI, this.sourceProvider);
|
|
15
|
+
this.destBridge = new ethers_1.Contract(config.destination.contractAddress, bridge_abi_1.POD_BRIDGE_ABI, this.destProvider);
|
|
16
|
+
// Interface for parsing logs
|
|
17
|
+
this.iface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get all deposits SENT by an address
|
|
21
|
+
* @param address The address that sent deposits
|
|
22
|
+
* @param fromBlock Starting block number (default: 0)
|
|
23
|
+
* @returns Array of bridge requests sent by this address
|
|
24
|
+
*/
|
|
25
|
+
async getDepositsSentBy(address, fromBlock = 0) {
|
|
26
|
+
console.log(`Fetching deposits sent by ${address}...`);
|
|
27
|
+
const deposits = [];
|
|
28
|
+
const currentBlock = await this.sourceProvider.getBlockNumber();
|
|
29
|
+
const BLOCK_BATCH_SIZE = 10000;
|
|
30
|
+
for (let start = fromBlock; start <= currentBlock; start += BLOCK_BATCH_SIZE) {
|
|
31
|
+
const end = Math.min(start + BLOCK_BATCH_SIZE - 1, currentBlock);
|
|
32
|
+
// With improved events, we can filter by 'from' directly!
|
|
33
|
+
const depositFilter = this.sourceBridge.filters.Deposit(null, // id
|
|
34
|
+
address, // from (indexed) - Filter by sender!
|
|
35
|
+
null // to
|
|
36
|
+
);
|
|
37
|
+
const depositNativeFilter = this.sourceBridge.filters.DepositNative(null, // id
|
|
38
|
+
address, // from (indexed) - Filter by sender!
|
|
39
|
+
null // to
|
|
40
|
+
);
|
|
41
|
+
const [depositLogs, depositNativeLogs] = await Promise.all([
|
|
42
|
+
this.sourceBridge.queryFilter(depositFilter, start, end),
|
|
43
|
+
this.sourceBridge.queryFilter(depositNativeFilter, start, end)
|
|
44
|
+
]);
|
|
45
|
+
// Process Deposit events
|
|
46
|
+
for (const log of depositLogs) {
|
|
47
|
+
const parsed = this.iface.parseLog({
|
|
48
|
+
topics: [...log.topics],
|
|
49
|
+
data: log.data
|
|
50
|
+
});
|
|
51
|
+
if (parsed) {
|
|
52
|
+
deposits.push({
|
|
53
|
+
requestId: parsed.args.id.toString(),
|
|
54
|
+
blockNumber: Number(parsed.args.blockNumber),
|
|
55
|
+
timestamp: Number(parsed.args.timestamp),
|
|
56
|
+
txHash: log.transactionHash,
|
|
57
|
+
from: parsed.args.from,
|
|
58
|
+
to: parsed.args.to,
|
|
59
|
+
token: parsed.args.token,
|
|
60
|
+
amount: parsed.args.amount.toString(),
|
|
61
|
+
isClaimed: false
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Process DepositNative events
|
|
66
|
+
for (const log of depositNativeLogs) {
|
|
67
|
+
const parsed = this.iface.parseLog({
|
|
68
|
+
topics: [...log.topics],
|
|
69
|
+
data: log.data
|
|
70
|
+
});
|
|
71
|
+
if (parsed) {
|
|
72
|
+
deposits.push({
|
|
73
|
+
requestId: parsed.args.id.toString(),
|
|
74
|
+
blockNumber: Number(parsed.args.blockNumber),
|
|
75
|
+
timestamp: Number(parsed.args.timestamp),
|
|
76
|
+
txHash: log.transactionHash,
|
|
77
|
+
from: parsed.args.from,
|
|
78
|
+
to: parsed.args.to,
|
|
79
|
+
token: ethers_1.ethers.ZeroAddress, // Native token
|
|
80
|
+
amount: parsed.args.amount.toString(),
|
|
81
|
+
isClaimed: false
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check claim status
|
|
87
|
+
await this.updateClaimStatus(deposits);
|
|
88
|
+
return deposits.sort((a, b) => b.timestamp - a.timestamp);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all deposits RECEIVED by an address
|
|
92
|
+
* @param address The address that will receive deposits
|
|
93
|
+
* @param fromBlock Starting block number (default: 0)
|
|
94
|
+
* @returns Array of bridge requests sent to this address
|
|
95
|
+
*/
|
|
96
|
+
async getDepositsReceivedBy(address, fromBlock = 0) {
|
|
97
|
+
console.log(`Fetching deposits received by ${address}...`);
|
|
98
|
+
const deposits = [];
|
|
99
|
+
const currentBlock = await this.sourceProvider.getBlockNumber();
|
|
100
|
+
const BLOCK_BATCH_SIZE = 10000;
|
|
101
|
+
for (let start = fromBlock; start <= currentBlock; start += BLOCK_BATCH_SIZE) {
|
|
102
|
+
const end = Math.min(start + BLOCK_BATCH_SIZE - 1, currentBlock);
|
|
103
|
+
// With improved events, we can filter by 'to' directly!
|
|
104
|
+
const depositFilter = this.sourceBridge.filters.Deposit(null, // id
|
|
105
|
+
null, // from
|
|
106
|
+
address // to (indexed) - Filter by recipient!
|
|
107
|
+
);
|
|
108
|
+
const depositNativeFilter = this.sourceBridge.filters.DepositNative(null, // id
|
|
109
|
+
null, // from
|
|
110
|
+
address // to (indexed) - Filter by recipient!
|
|
111
|
+
);
|
|
112
|
+
const [depositLogs, depositNativeLogs] = await Promise.all([
|
|
113
|
+
this.sourceBridge.queryFilter(depositFilter, start, end),
|
|
114
|
+
this.sourceBridge.queryFilter(depositNativeFilter, start, end)
|
|
115
|
+
]);
|
|
116
|
+
// Process Deposit events
|
|
117
|
+
for (const log of depositLogs) {
|
|
118
|
+
const parsed = this.iface.parseLog({
|
|
119
|
+
topics: [...log.topics],
|
|
120
|
+
data: log.data
|
|
121
|
+
});
|
|
122
|
+
if (parsed) {
|
|
123
|
+
deposits.push({
|
|
124
|
+
requestId: parsed.args.id.toString(),
|
|
125
|
+
blockNumber: Number(parsed.args.blockNumber),
|
|
126
|
+
timestamp: Number(parsed.args.timestamp),
|
|
127
|
+
txHash: log.transactionHash,
|
|
128
|
+
from: parsed.args.from,
|
|
129
|
+
to: parsed.args.to,
|
|
130
|
+
token: parsed.args.token,
|
|
131
|
+
amount: parsed.args.amount.toString(),
|
|
132
|
+
isClaimed: false
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Process DepositNative events
|
|
137
|
+
for (const log of depositNativeLogs) {
|
|
138
|
+
const parsed = this.iface.parseLog({
|
|
139
|
+
topics: [...log.topics],
|
|
140
|
+
data: log.data
|
|
141
|
+
});
|
|
142
|
+
if (parsed) {
|
|
143
|
+
deposits.push({
|
|
144
|
+
requestId: parsed.args.id.toString(),
|
|
145
|
+
blockNumber: Number(parsed.args.blockNumber),
|
|
146
|
+
timestamp: Number(parsed.args.timestamp),
|
|
147
|
+
txHash: log.transactionHash,
|
|
148
|
+
from: parsed.args.from,
|
|
149
|
+
to: parsed.args.to,
|
|
150
|
+
token: ethers_1.ethers.ZeroAddress,
|
|
151
|
+
amount: parsed.args.amount.toString(),
|
|
152
|
+
isClaimed: false
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Check claim status
|
|
158
|
+
await this.updateClaimStatus(deposits);
|
|
159
|
+
return deposits.sort((a, b) => b.timestamp - a.timestamp);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get ALL deposits for an address (both sent and received)
|
|
163
|
+
* @param address The address to check
|
|
164
|
+
* @param fromBlock Starting block number (default: 0)
|
|
165
|
+
* @returns Array of bridge requests with type indicator
|
|
166
|
+
*/
|
|
167
|
+
async getAllDepositsFor(address, fromBlock = 0) {
|
|
168
|
+
console.log(`Fetching all deposits for ${address}...`);
|
|
169
|
+
// Fetch both sent and received in parallel
|
|
170
|
+
const [sent, received] = await Promise.all([
|
|
171
|
+
this.getDepositsSentBy(address, fromBlock),
|
|
172
|
+
this.getDepositsReceivedBy(address, fromBlock)
|
|
173
|
+
]);
|
|
174
|
+
// Mark with type and combine
|
|
175
|
+
const sentWithType = sent.map(d => ({
|
|
176
|
+
...d,
|
|
177
|
+
type: pod_bridge_types_1.DepositType.SENT
|
|
178
|
+
}));
|
|
179
|
+
const receivedWithType = received.map(d => ({
|
|
180
|
+
...d,
|
|
181
|
+
type: pod_bridge_types_1.DepositType.RECEIVED
|
|
182
|
+
}));
|
|
183
|
+
// Combine and deduplicate (in case user sent to themselves)
|
|
184
|
+
const allDeposits = [...sentWithType, ...receivedWithType];
|
|
185
|
+
const uniqueDeposits = new Map();
|
|
186
|
+
for (const deposit of allDeposits) {
|
|
187
|
+
const key = `${deposit.requestId}-${deposit.txHash}`;
|
|
188
|
+
if (!uniqueDeposits.has(key)) {
|
|
189
|
+
uniqueDeposits.set(key, deposit);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// If deposit appears in both (sent to self), mark as both
|
|
193
|
+
const existing = uniqueDeposits.get(key);
|
|
194
|
+
if (existing.from === address && existing.to === address) {
|
|
195
|
+
uniqueDeposits.set(key, { ...existing, type: pod_bridge_types_1.DepositType.SENT });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return Array.from(uniqueDeposits.values())
|
|
200
|
+
.sort((a, b) => b.timestamp - a.timestamp);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check if deposits can be claimed (block finalized on source chain)
|
|
204
|
+
* @param deposit The bridge request to check
|
|
205
|
+
* @returns True if the deposit can be claimed
|
|
206
|
+
*/
|
|
207
|
+
async canBeClaimed(deposit) {
|
|
208
|
+
const currentBlock = await this.sourceProvider.getBlockNumber();
|
|
209
|
+
const finalizedBlock = currentBlock - 15; // ~15 minutes on Sepolia
|
|
210
|
+
return deposit.blockNumber <= finalizedBlock && !deposit.isClaimed;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Batch check claim status for multiple deposits
|
|
214
|
+
* @param deposits Array of deposits to check
|
|
215
|
+
*/
|
|
216
|
+
async updateClaimStatus(deposits) {
|
|
217
|
+
if (deposits.length === 0)
|
|
218
|
+
return;
|
|
219
|
+
console.log(`Checking claim status for ${deposits.length} deposits...`);
|
|
220
|
+
try {
|
|
221
|
+
// Get claim events from destination chain
|
|
222
|
+
const claimFilter = this.destBridge.filters.Claim();
|
|
223
|
+
const claimNativeFilter = this.destBridge.filters.ClaimNative();
|
|
224
|
+
const [claimLogs, claimNativeLogs] = await Promise.all([
|
|
225
|
+
this.destBridge.queryFilter(claimFilter),
|
|
226
|
+
this.destBridge.queryFilter(claimNativeFilter)
|
|
227
|
+
]);
|
|
228
|
+
// Create a map of claimed request IDs
|
|
229
|
+
const claimedMap = new Map();
|
|
230
|
+
for (const log of [...claimLogs, ...claimNativeLogs]) {
|
|
231
|
+
const parsed = this.iface.parseLog({
|
|
232
|
+
topics: [...log.topics],
|
|
233
|
+
data: log.data
|
|
234
|
+
});
|
|
235
|
+
if (parsed) {
|
|
236
|
+
claimedMap.set(parsed.args.id.toString(), {
|
|
237
|
+
txHash: log.transactionHash,
|
|
238
|
+
timestamp: Number(parsed.args.timestamp),
|
|
239
|
+
claimer: parsed.args.claimer
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Update deposit status
|
|
244
|
+
for (const deposit of deposits) {
|
|
245
|
+
const claimInfo = claimedMap.get(deposit.requestId);
|
|
246
|
+
if (claimInfo) {
|
|
247
|
+
deposit.isClaimed = true;
|
|
248
|
+
deposit.claimedTxHash = claimInfo.txHash;
|
|
249
|
+
deposit.claimedAt = claimInfo.timestamp;
|
|
250
|
+
deposit.claimedBy = claimInfo.claimer;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
console.error('Error checking claim status:', error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Batch check if multiple requests have been processed on-chain
|
|
260
|
+
* @param deposits Array of deposits to check
|
|
261
|
+
* @returns Array of boolean values
|
|
262
|
+
*/
|
|
263
|
+
async areRequestsProcessed(deposits) {
|
|
264
|
+
if (deposits.length === 0)
|
|
265
|
+
return [];
|
|
266
|
+
try {
|
|
267
|
+
const ids = deposits.map(d => d.requestId);
|
|
268
|
+
const tokens = deposits.map(d => d.token);
|
|
269
|
+
const amounts = deposits.map(d => d.amount);
|
|
270
|
+
const tos = deposits.map(d => d.to);
|
|
271
|
+
const results = await this.destBridge.areRequestsProcessed(ids, tokens, amounts, tos);
|
|
272
|
+
return results;
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
console.error('Batch check failed, falling back to individual checks:', error);
|
|
276
|
+
// Fallback: check individually
|
|
277
|
+
const results = [];
|
|
278
|
+
for (const deposit of deposits) {
|
|
279
|
+
try {
|
|
280
|
+
const hash = this.computeRequestHash(deposit.requestId, deposit.token, deposit.amount, deposit.to);
|
|
281
|
+
const isProcessed = await this.destBridge.processedRequests(hash);
|
|
282
|
+
results.push(isProcessed);
|
|
283
|
+
}
|
|
284
|
+
catch (_a) {
|
|
285
|
+
results.push(false);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return results;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Compute the request hash (replicates Solidity's _hashRequest)
|
|
293
|
+
* @param id Request ID
|
|
294
|
+
* @param token Token address
|
|
295
|
+
* @param amount Amount in wei
|
|
296
|
+
* @param to Recipient address
|
|
297
|
+
* @returns Request hash
|
|
298
|
+
*/
|
|
299
|
+
computeRequestHash(id, token, amount, to) {
|
|
300
|
+
return ethers_1.ethers.solidityPackedKeccak256(['uint256', 'address', 'uint256', 'address'], [id, token, amount, to]);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
exports.PodBridgeTrackerClient = PodBridgeTrackerClient;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { PodBridgeActionClient } from './clients/action/client';
|
|
2
|
+
export { PodBridgeTrackerClient } from './clients/tracker/client';
|
|
3
|
+
export { POD_BRIDGE_ABI } from './libs/abi/bridge.abi';
|
|
4
|
+
export { PodBridgeConfig, BridgeRequest, BridgeRequestWithType, DepositType, UnsignedTransaction, CertifiedLog, } from './libs/types/pod-bridge.types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DepositType = exports.POD_BRIDGE_ABI = exports.PodBridgeTrackerClient = exports.PodBridgeActionClient = void 0;
|
|
4
|
+
var client_1 = require("./clients/action/client");
|
|
5
|
+
Object.defineProperty(exports, "PodBridgeActionClient", { enumerable: true, get: function () { return client_1.PodBridgeActionClient; } });
|
|
6
|
+
var client_2 = require("./clients/tracker/client");
|
|
7
|
+
Object.defineProperty(exports, "PodBridgeTrackerClient", { enumerable: true, get: function () { return client_2.PodBridgeTrackerClient; } });
|
|
8
|
+
var bridge_abi_1 = require("./libs/abi/bridge.abi");
|
|
9
|
+
Object.defineProperty(exports, "POD_BRIDGE_ABI", { enumerable: true, get: function () { return bridge_abi_1.POD_BRIDGE_ABI; } });
|
|
10
|
+
var pod_bridge_types_1 = require("./libs/types/pod-bridge.types");
|
|
11
|
+
Object.defineProperty(exports, "DepositType", { enumerable: true, get: function () { return pod_bridge_types_1.DepositType; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const POD_BRIDGE_ABI: string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POD_BRIDGE_ABI = void 0;
|
|
4
|
+
// Updated ABI with improved event signatures
|
|
5
|
+
exports.POD_BRIDGE_ABI = [
|
|
6
|
+
// Improved Deposit events with indexed from/to and additional data
|
|
7
|
+
"event Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount, uint256 timestamp, uint256 blockNumber)",
|
|
8
|
+
"event DepositNative(uint256 indexed id, address indexed from, address indexed to, uint256 amount, uint256 timestamp, uint256 blockNumber)",
|
|
9
|
+
// Improved Claim events with indexed claimer/to
|
|
10
|
+
"event Claim(uint256 indexed id, address indexed claimer, address indexed to, address mirrorToken, address token, uint256 amount, uint256 timestamp)",
|
|
11
|
+
"event ClaimNative(uint256 indexed id, address indexed claimer, address indexed to, uint256 amount, uint256 timestamp)",
|
|
12
|
+
// Action functions
|
|
13
|
+
"function deposit(address token, uint256 amount, address to) returns (uint256)",
|
|
14
|
+
"function depositNative(address to) payable returns (uint256)",
|
|
15
|
+
"function claim(tuple(tuple(address addr, bytes32[] topics, bytes data) log, tuple(bytes32 r, bytes32 s, uint8 v)[] sigs) certifiedLog)",
|
|
16
|
+
"function claimNative(tuple(tuple(address addr, bytes32[] topics, bytes data) log, tuple(bytes32 r, bytes32 s, uint8 v)[] sigs) certifiedLog)",
|
|
17
|
+
"function claim(uint256 id, address token, uint256 blockNumber)",
|
|
18
|
+
"function claimNative(uint256 id, uint256 blockNumber)",
|
|
19
|
+
// View functions
|
|
20
|
+
"function processedRequests(bytes32) view returns (bool)",
|
|
21
|
+
"function bridgeContract() view returns (address)",
|
|
22
|
+
"function getSourceChainId() view returns (uint96)",
|
|
23
|
+
"function getDestinationChainId() view returns (uint256)",
|
|
24
|
+
"function getBridgeCounterpart() view returns (address)",
|
|
25
|
+
// Batch status checks
|
|
26
|
+
"function isRequestProcessed(uint256 id, address token, uint256 amount, address to) view returns (bool)",
|
|
27
|
+
"function areRequestsProcessed(uint256[] ids, address[] tokens, uint256[] amounts, address[] tos) view returns (bool[])",
|
|
28
|
+
// Finality checks (BridgeMintBurn only)
|
|
29
|
+
"function canBeClaimed(uint256 blockNumber) view returns (bool)",
|
|
30
|
+
"function canBeClaimedBatch(uint256[] blockNumbers) view returns (bool[])",
|
|
31
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface PodBridgeChainConfig {
|
|
2
|
+
rpcUrl: string;
|
|
3
|
+
contractAddress: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PodBridgeConfig {
|
|
6
|
+
source: PodBridgeChainConfig;
|
|
7
|
+
destination: PodBridgeChainConfig;
|
|
8
|
+
}
|
|
9
|
+
export interface BridgeRequest {
|
|
10
|
+
requestId: string;
|
|
11
|
+
blockNumber: number;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
txHash: string;
|
|
14
|
+
from: string;
|
|
15
|
+
to: string;
|
|
16
|
+
token: string;
|
|
17
|
+
amount: string;
|
|
18
|
+
isClaimed: boolean;
|
|
19
|
+
claimedTxHash?: string;
|
|
20
|
+
claimedAt?: number;
|
|
21
|
+
claimedBy?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare enum DepositType {
|
|
24
|
+
SENT = "sent",
|
|
25
|
+
RECEIVED = "received"
|
|
26
|
+
}
|
|
27
|
+
export interface BridgeRequestWithType extends BridgeRequest {
|
|
28
|
+
type: DepositType;
|
|
29
|
+
}
|
|
30
|
+
export interface UnsignedTransaction {
|
|
31
|
+
to: string;
|
|
32
|
+
data: string;
|
|
33
|
+
value: string;
|
|
34
|
+
from?: string;
|
|
35
|
+
chainId?: number;
|
|
36
|
+
gasLimit?: bigint;
|
|
37
|
+
}
|
|
38
|
+
export interface CertifiedLog {
|
|
39
|
+
log: {
|
|
40
|
+
addr: string;
|
|
41
|
+
topics: string[];
|
|
42
|
+
data: string;
|
|
43
|
+
};
|
|
44
|
+
sigs: Array<{
|
|
45
|
+
r: string;
|
|
46
|
+
s: string;
|
|
47
|
+
v: number;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DepositType = void 0;
|
|
4
|
+
var DepositType;
|
|
5
|
+
(function (DepositType) {
|
|
6
|
+
DepositType["SENT"] = "sent";
|
|
7
|
+
DepositType["RECEIVED"] = "received";
|
|
8
|
+
})(DepositType || (exports.DepositType = DepositType = {}));
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tapforce/pod-bridge-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "SDK for interacting with Bridges between pod and other chains",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pod",
|
|
7
|
+
"bridge",
|
|
8
|
+
"sdk"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/tapforce/pod-ts-bridge-sdk#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/tapforce/pod-ts-bridge-sdk/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/tapforce/pod-ts-bridge-sdk.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"author": "Tapforce",
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"directories": {
|
|
23
|
+
"example": "examples"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/**/*"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"clean": "rimraf dist",
|
|
31
|
+
"prepare": "npm run clean && npm run build",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"ethers": "^6.15.0",
|
|
36
|
+
"typescript": "^5.8.3"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.15.18",
|
|
40
|
+
"prettier": "3.5.3",
|
|
41
|
+
"rimraf": "^5.0.5"
|
|
42
|
+
}
|
|
43
|
+
}
|