@tapforce/pod-bridge-sdk 1.1.17 → 1.2.2
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 +180 -521
- package/dist/clients/action/pod-to-source-chain-client.d.ts +37 -35
- package/dist/clients/action/pod-to-source-chain-client.js +47 -49
- package/dist/clients/action/source-chain-to-pod-client.d.ts +21 -48
- package/dist/clients/action/source-chain-to-pod-client.js +23 -65
- package/dist/clients/tracker/client.d.ts +27 -9
- package/dist/clients/tracker/client.js +89 -64
- package/dist/clients/tracker/pod-tracker.service.d.ts +8 -19
- package/dist/clients/tracker/pod-tracker.service.js +20 -59
- package/dist/clients/tracker/source-chain-tracker.service.d.ts +11 -3
- package/dist/clients/tracker/source-chain-tracker.service.js +37 -32
- package/dist/index.d.ts +3 -2
- package/dist/index.js +12 -1
- package/dist/libs/abi/bridge.abi.d.ts +7 -9
- package/dist/libs/abi/bridge.abi.js +21 -41
- package/dist/libs/helpers/convert-certified-log.helper.d.ts +9 -21
- package/dist/libs/helpers/convert-certified-log.helper.js +14 -23
- package/dist/libs/helpers/signature-recovery.helper.d.ts +161 -0
- package/dist/libs/helpers/signature-recovery.helper.js +447 -0
- package/dist/libs/pod-sdk/src/provider/provider-builder.js +16 -11
- package/dist/libs/pod-sdk/src/types/responses.d.ts +14 -37
- package/dist/libs/types/pod-bridge.types.d.ts +35 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,387 +1,223 @@
|
|
|
1
1
|
# Pod Bridge SDK
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for
|
|
3
|
+
TypeScript SDK for bridging ERC20 tokens between POD and EVM chains (e.g., Sepolia/ETH).
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ETH → Pod: Deposit on ETH, AUTO-CLAIM on Pod (no claim TX needed)
|
|
9
|
+
Pod → ETH: Deposit on Pod, claim on ETH with aggregated validator signatures
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Key points:**
|
|
13
|
+
- Only ERC20 tokens supported (wrap ETH to WETH first)
|
|
14
|
+
- `Deposit.id` on source chain = `Claim.id` on destination chain (for tracking)
|
|
15
|
+
- Token addresses differ between chains
|
|
4
16
|
|
|
5
17
|
## Features
|
|
6
18
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- 🔗 **Dual Architecture Support**: Handles both certificate-based (Source Chain) and precompile-based (POD) claim verification
|
|
19
|
+
- **Bridge ERC20 Deposits**: Deposit tokens to the bridge
|
|
20
|
+
- **Track Bridge Requests**: Query deposits sent/received by any address
|
|
21
|
+
- **Claim Status Tracking**: Monitor claim status and finality
|
|
22
|
+
- **Transaction Building**: Generate unsigned transactions ready for signing
|
|
23
|
+
- **Type-Safe**: Full TypeScript support
|
|
13
24
|
|
|
14
25
|
## Installation
|
|
15
26
|
|
|
16
27
|
```bash
|
|
17
28
|
npm install @tapforce/pod-bridge-sdk
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
or
|
|
21
|
-
|
|
22
|
-
```bash
|
|
29
|
+
# or
|
|
23
30
|
yarn add @tapforce/pod-bridge-sdk
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
or
|
|
27
|
-
|
|
28
|
-
```bash
|
|
31
|
+
# or
|
|
29
32
|
pnpm add @tapforce/pod-bridge-sdk
|
|
30
33
|
```
|
|
31
34
|
|
|
32
35
|
## Quick Start
|
|
33
36
|
|
|
34
|
-
### Import the SDK
|
|
35
|
-
|
|
36
37
|
```typescript
|
|
37
38
|
import {
|
|
38
39
|
SourceChainToPodActionClient,
|
|
39
40
|
PodToSourceChainActionClient,
|
|
40
41
|
PodBridgeTrackerClient,
|
|
41
42
|
PodBridgeConfig,
|
|
42
|
-
|
|
43
|
-
SOURCE_CHAIN_BRIDGE_ABI
|
|
43
|
+
BRIDGE_ABI
|
|
44
44
|
} from '@tapforce/pod-bridge-sdk';
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
## Configuration
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
### For Action Clients (creating transactions)
|
|
50
50
|
|
|
51
51
|
```typescript
|
|
52
|
-
|
|
52
|
+
import { PodBridgeActionsClientConfig } from '@tapforce/pod-bridge-sdk';
|
|
53
|
+
|
|
54
|
+
const actionConfig: PodBridgeActionsClientConfig = {
|
|
53
55
|
sourceChain: {
|
|
54
|
-
|
|
55
|
-
contractAddress: '0x...', // Source chain bridge contract
|
|
56
|
-
deploymentBlock: 9502541 // Optional: Start indexing from deployment block (avoids querying empty blocks)
|
|
56
|
+
contractAddress: '0x...' // Bridge contract on ETH/Sepolia
|
|
57
57
|
},
|
|
58
58
|
pod: {
|
|
59
|
-
|
|
60
|
-
contractAddress: '0x...', // Destination chain bridge contract
|
|
61
|
-
deploymentBlock: 0 // Optional: POD deployment block
|
|
59
|
+
contractAddress: '0x...' // Bridge contract on Pod
|
|
62
60
|
}
|
|
63
61
|
};
|
|
64
62
|
```
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
### 1. Action Clients - Creating Transactions
|
|
69
|
-
|
|
70
|
-
The SDK provides two action clients for different bridge directions:
|
|
71
|
-
|
|
72
|
-
- **`SourceChainToPodActionClient`**: For Source Chain → POD transfers (e.g., Sepolia → POD)
|
|
73
|
-
- **`PodToSourceChainActionClient`**: For POD → Source Chain transfers (e.g., POD → Sepolia)
|
|
74
|
-
|
|
75
|
-
#### Configuration for Action Clients
|
|
64
|
+
### For Tracker Client (querying events)
|
|
76
65
|
|
|
77
66
|
```typescript
|
|
78
|
-
|
|
67
|
+
import { PodBridgeConfig } from '@tapforce/pod-bridge-sdk';
|
|
68
|
+
import { ethers } from 'ethers';
|
|
69
|
+
|
|
70
|
+
const trackerConfig: PodBridgeConfig = {
|
|
79
71
|
sourceChain: {
|
|
80
|
-
|
|
72
|
+
provider: new ethers.JsonRpcProvider('https://sepolia.infura.io/v3/YOUR_KEY'),
|
|
73
|
+
contractAddress: '0x...',
|
|
74
|
+
deploymentBlock: 9502541 // Optional: avoids indexing empty blocks
|
|
81
75
|
},
|
|
82
76
|
pod: {
|
|
83
|
-
|
|
77
|
+
provider: new ethers.JsonRpcProvider('https://rpc.pod.network'),
|
|
78
|
+
contractAddress: '0x...',
|
|
79
|
+
deploymentBlock: 0
|
|
84
80
|
}
|
|
85
81
|
};
|
|
86
|
-
|
|
87
|
-
const sourceChainToPodClient = new SourceChainToPodActionClient(actionConfig);
|
|
88
|
-
const podToSourceChainClient = new PodToSourceChainActionClient(actionConfig);
|
|
89
82
|
```
|
|
90
83
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// Create unsigned transaction for ERC20 deposit on Source chain
|
|
95
|
-
const unsignedTx = sourceChainToPodClient.depositToken({
|
|
96
|
-
token: '0x...', // ERC20 token address
|
|
97
|
-
amount: '1000000000000000000', // Amount in wei (1 token with 18 decimals)
|
|
98
|
-
destinationWalletAddress: '0x...', // Recipient address on POD
|
|
99
|
-
from: '0x...' // Optional: sender address
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Deposit native tokens
|
|
103
|
-
const unsignedNativeTx = sourceChainToPodClient.depositNative({
|
|
104
|
-
amount: '1000000000000000000', // Amount in wei (1 ETH)
|
|
105
|
-
destinationWalletAddress: '0x...', // Recipient address on POD
|
|
106
|
-
from: '0x...' // Optional: sender address
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Sign and send transaction using your wallet/provider
|
|
110
|
-
// const tx = await signer.sendTransaction(unsignedTx);
|
|
111
|
-
```
|
|
84
|
+
## Usage
|
|
112
85
|
|
|
113
|
-
|
|
86
|
+
### ETH → Pod (Auto-claim)
|
|
114
87
|
|
|
115
|
-
|
|
88
|
+
Deposits on ETH are automatically claimed on Pod after block finalization (~15 min on Sepolia).
|
|
116
89
|
|
|
117
90
|
```typescript
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// For native tokens
|
|
127
|
-
const unsignedNativeTx = sourceChainToPodClient.claimNativeWithBlockNumber({
|
|
128
|
-
id: '123',
|
|
129
|
-
blockNumber: 12345678,
|
|
130
|
-
from: '0x...'
|
|
91
|
+
const client = new SourceChainToPodActionClient(actionConfig);
|
|
92
|
+
|
|
93
|
+
// Create deposit transaction
|
|
94
|
+
const depositTx = client.deposit({
|
|
95
|
+
token: '0xWETH_ADDRESS', // ERC20 token on ETH
|
|
96
|
+
amount: ethers.parseEther('1'),
|
|
97
|
+
destinationWalletAddress: '0x...', // Recipient on Pod
|
|
98
|
+
from: '0x...' // Optional
|
|
131
99
|
});
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
#### Deposit from POD (POD → Source Chain)
|
|
135
100
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
token: '0x...', // ERC20 token address
|
|
140
|
-
amount: '1000000000000000000', // Amount in wei
|
|
141
|
-
destinationWalletAddress: '0x...', // Recipient address on Source chain
|
|
142
|
-
from: '0x...' // Optional: sender address
|
|
143
|
-
});
|
|
101
|
+
// Send transaction
|
|
102
|
+
const tx = await signer.sendTransaction(depositTx);
|
|
103
|
+
const receipt = await tx.wait();
|
|
144
104
|
|
|
145
|
-
//
|
|
146
|
-
const unsignedNativeTx = podToSourceChainClient.depositNative({
|
|
147
|
-
amount: '1000000000000000000',
|
|
148
|
-
destinationWalletAddress: '0x...',
|
|
149
|
-
from: '0x...'
|
|
150
|
-
});
|
|
105
|
+
// No claim needed - balance auto-credited on Pod after finalization
|
|
151
106
|
```
|
|
152
107
|
|
|
153
|
-
|
|
108
|
+
### Pod → ETH (Manual claim with signatures)
|
|
154
109
|
|
|
155
|
-
|
|
110
|
+
Deposits on Pod require manual claim on ETH with aggregated validator signatures.
|
|
156
111
|
|
|
157
112
|
```typescript
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Initialize the POD indexer client
|
|
161
|
-
const indexerClient = new PodIndexerHttpClient({
|
|
162
|
-
apiUrl: 'YOUR_INDEXER_API_URL',
|
|
163
|
-
apiKey: 'YOUR_INDEXER_API_KEY'
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Get certified log from POD deposit transaction
|
|
167
|
-
const certifiedLogResponse = await indexerClient.getBridgeCertifiedLog({
|
|
168
|
-
transactionHash: '0x...', // Transaction hash of deposit on POD
|
|
169
|
-
bridgeContractAddress: config.pod.contractAddress,
|
|
170
|
-
rpcUrl: config.pod.rpcUrl
|
|
171
|
-
});
|
|
113
|
+
const client = new PodToSourceChainActionClient(actionConfig);
|
|
172
114
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
from: '0x...' // Optional: claimer address
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// For native tokens
|
|
182
|
-
const unsignedNativeTx = podToSourceChainClient.claimNativeWithCertificate({
|
|
183
|
-
certifiedLog,
|
|
115
|
+
// Step 1: Deposit on Pod
|
|
116
|
+
const depositTx = client.deposit({
|
|
117
|
+
token: '0xPOD_TOKEN_ADDRESS', // ERC20 token on Pod
|
|
118
|
+
amount: ethers.parseEther('1'),
|
|
119
|
+
destinationWalletAddress: '0x...', // Recipient on ETH
|
|
184
120
|
from: '0x...'
|
|
185
121
|
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### 2. Tracker Client - Querying Bridge Activity
|
|
189
|
-
|
|
190
|
-
The `PodBridgeTrackerClient` queries the blockchain for deposit and claim events.
|
|
191
122
|
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
#### Get Deposits Sent by Address
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// Get all deposits sent by an address
|
|
200
|
-
const deposits = await trackerClient.getDepositsSentBy(
|
|
201
|
-
'0x...', // sender address
|
|
202
|
-
0 // starting block (optional, default: 0)
|
|
203
|
-
);
|
|
123
|
+
const tx = await podSigner.sendTransaction(depositTx);
|
|
124
|
+
const receipt = await tx.wait();
|
|
204
125
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
126
|
+
// Step 2: Get aggregated signatures from your indexer/API
|
|
127
|
+
// (Implementation depends on your backend)
|
|
128
|
+
|
|
129
|
+
// Step 3: Claim on ETH
|
|
130
|
+
const claimTx = client.claim({
|
|
131
|
+
claimData: {
|
|
132
|
+
ethTokenAddress: '0xETH_TOKEN_ADDRESS', // Token on ETH (different from Pod)
|
|
133
|
+
amount: ethers.parseEther('1'),
|
|
134
|
+
to: '0x...', // Recipient
|
|
135
|
+
committeeEpoch: 0, // Hardcoded for now
|
|
136
|
+
aggregatedSignatures: '0x...', // 65-byte signatures (r,s,v) concatenated
|
|
137
|
+
depositTxHash: tx.hash // Deposit TX hash from Pod
|
|
138
|
+
},
|
|
139
|
+
from: '0x...'
|
|
218
140
|
});
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
#### Get Deposits Received by Address
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
// Get all deposits where the address is the recipient
|
|
225
|
-
const deposits = await trackerClient.getDepositsReceivedBy(
|
|
226
|
-
'0x...', // recipient address
|
|
227
|
-
0 // starting block (optional)
|
|
228
|
-
);
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
#### Get All Deposits (Sent + Received)
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
// Get all deposits related to an address (both sent and received)
|
|
235
|
-
const allDeposits = await trackerClient.getAllDepositsFor(
|
|
236
|
-
'0x...', // address
|
|
237
|
-
0 // starting block (optional)
|
|
238
|
-
);
|
|
239
141
|
|
|
240
|
-
|
|
241
|
-
allDeposits.forEach(deposit => {
|
|
242
|
-
console.log(`Type: ${deposit.type}`); // 'sent' or 'received'
|
|
243
|
-
console.log(`Request ID: ${deposit.requestId}`);
|
|
244
|
-
});
|
|
142
|
+
const claim = await ethSigner.sendTransaction(claimTx);
|
|
245
143
|
```
|
|
246
144
|
|
|
247
|
-
|
|
145
|
+
### Tracking Deposits
|
|
248
146
|
|
|
249
147
|
```typescript
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
148
|
+
const tracker = new PodBridgeTrackerClient(trackerConfig);
|
|
149
|
+
|
|
150
|
+
// Get all deposits for an address
|
|
151
|
+
const deposits = await tracker.getAllDepositsFor('0x...');
|
|
152
|
+
|
|
153
|
+
for (const deposit of deposits) {
|
|
154
|
+
console.log('Request ID:', deposit.requestId);
|
|
155
|
+
console.log('Direction:', deposit.deposit.chain === 'sourceChain' ? 'ETH → Pod' : 'Pod → ETH');
|
|
156
|
+
console.log('From:', deposit.deposit.depositor);
|
|
157
|
+
console.log('To:', deposit.deposit.destination);
|
|
158
|
+
console.log('Token:', deposit.deposit.token);
|
|
159
|
+
console.log('Amount:', deposit.deposit.amount);
|
|
160
|
+
console.log('Claimed:', deposit.isClaimed);
|
|
161
|
+
console.log('Claimable:', deposit.isClaimable);
|
|
255
162
|
}
|
|
256
|
-
```
|
|
257
163
|
|
|
258
|
-
|
|
164
|
+
// Get deposits sent by address
|
|
165
|
+
const sent = await tracker.getDepositsSentBy('0x...');
|
|
259
166
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const deposits = await trackerClient.getDepositsSentBy('0x...');
|
|
263
|
-
const processedStatuses = await trackerClient.areRequestsProcessed(deposits);
|
|
167
|
+
// Get deposits received by address
|
|
168
|
+
const received = await tracker.getDepositsReceivedBy('0x...');
|
|
264
169
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
170
|
+
// Check if deposit can be claimed (Pod → ETH only)
|
|
171
|
+
const canClaim = await tracker.canBeClaimed(deposit);
|
|
172
|
+
|
|
173
|
+
// Batch check processed status
|
|
174
|
+
const statuses = await tracker.areRequestsProcessed(deposits);
|
|
268
175
|
```
|
|
269
176
|
|
|
270
177
|
## API Reference
|
|
271
178
|
|
|
272
179
|
### SourceChainToPodActionClient
|
|
273
180
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
#### Constructor
|
|
181
|
+
For ETH → Pod deposits (auto-claim on Pod).
|
|
277
182
|
|
|
278
183
|
```typescript
|
|
279
|
-
|
|
184
|
+
// Deposit ERC20 tokens
|
|
185
|
+
deposit(args: {
|
|
186
|
+
token: string;
|
|
187
|
+
amount: string | bigint;
|
|
188
|
+
destinationWalletAddress: string;
|
|
189
|
+
from?: string;
|
|
190
|
+
}): UnsignedTransaction
|
|
280
191
|
```
|
|
281
192
|
|
|
282
|
-
#### Methods
|
|
283
|
-
|
|
284
|
-
- **`depositToken(args)`**: Create unsigned transaction for ERC20 deposit on Source chain
|
|
285
|
-
- `token`: Token address
|
|
286
|
-
- `amount`: Amount in wei (string | bigint)
|
|
287
|
-
- `destinationWalletAddress`: Recipient address on POD
|
|
288
|
-
- `from?`: Optional sender address
|
|
289
|
-
|
|
290
|
-
- **`depositNative(args)`**: Create unsigned transaction for native token deposit on Source chain
|
|
291
|
-
- `amount`: Amount in wei (string | bigint)
|
|
292
|
-
- `destinationWalletAddress`: Recipient address on POD
|
|
293
|
-
- `from?`: Optional sender address
|
|
294
|
-
|
|
295
|
-
- **`claimWithBlockNumber(args)`**: Create unsigned transaction for claiming on POD with block number
|
|
296
|
-
- `id`: Request ID
|
|
297
|
-
- `token`: Token address
|
|
298
|
-
- `blockNumber`: Block number of deposit on Source chain
|
|
299
|
-
- `from?`: Optional claimer address
|
|
300
|
-
|
|
301
|
-
- **`claimNativeWithBlockNumber(args)`**: Create unsigned transaction for claiming native tokens on POD with block number
|
|
302
|
-
- `id`: Request ID
|
|
303
|
-
- `blockNumber`: Block number of deposit on Source chain
|
|
304
|
-
- `from?`: Optional claimer address
|
|
305
|
-
|
|
306
193
|
### PodToSourceChainActionClient
|
|
307
194
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
#### Constructor
|
|
195
|
+
For Pod → ETH deposits and claims.
|
|
311
196
|
|
|
312
197
|
```typescript
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
- `amount`: Amount in wei (string | bigint)
|
|
321
|
-
- `destinationWalletAddress`: Recipient address on Source chain
|
|
322
|
-
- `from?`: Optional sender address
|
|
323
|
-
|
|
324
|
-
- **`depositNative(args)`**: Create unsigned transaction for native token deposit on POD
|
|
325
|
-
- `amount`: Amount in wei (string | bigint)
|
|
326
|
-
- `destinationWalletAddress`: Recipient address on Source chain
|
|
327
|
-
- `from?`: Optional sender address
|
|
328
|
-
|
|
329
|
-
- **`claimWithCertificate(args)`**: Create unsigned transaction for claiming on Source chain with certificate
|
|
330
|
-
- `certifiedLog`: Certified log from POD certificate system
|
|
331
|
-
- `from?`: Optional claimer address
|
|
198
|
+
// Deposit ERC20 tokens on Pod
|
|
199
|
+
deposit(args: {
|
|
200
|
+
token: string;
|
|
201
|
+
amount: string | bigint;
|
|
202
|
+
destinationWalletAddress: string;
|
|
203
|
+
from?: string;
|
|
204
|
+
}): UnsignedTransaction
|
|
332
205
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
206
|
+
// Claim on ETH with aggregated signatures
|
|
207
|
+
claim(args: {
|
|
208
|
+
claimData: ClaimProofData;
|
|
209
|
+
from?: string;
|
|
210
|
+
}): UnsignedTransaction
|
|
211
|
+
```
|
|
336
212
|
|
|
337
213
|
### PodBridgeTrackerClient
|
|
338
214
|
|
|
339
|
-
#### Methods
|
|
340
|
-
|
|
341
|
-
- **`getDepositsSentBy(address, fromBlock?)`**: Get deposits sent by address
|
|
342
|
-
- Returns: `Promise<BridgeRequest[]>`
|
|
343
|
-
|
|
344
|
-
- **`getDepositsReceivedBy(address, fromBlock?)`**: Get deposits received by address
|
|
345
|
-
- Returns: `Promise<BridgeRequest[]>`
|
|
346
|
-
|
|
347
|
-
- **`getAllDepositsFor(address, fromBlock?)`**: Get all deposits for address (sent + received)
|
|
348
|
-
- Returns: `Promise<BridgeRequestWithType[]>`
|
|
349
|
-
|
|
350
|
-
- **`canBeClaimed(deposit)`**: Check if deposit can be claimed
|
|
351
|
-
- Returns: `Promise<boolean>`
|
|
352
|
-
|
|
353
|
-
- **`areRequestsProcessed(deposits)`**: Batch check if requests are processed
|
|
354
|
-
- Returns: `Promise<boolean[]>`
|
|
355
|
-
|
|
356
|
-
## ABIs
|
|
357
|
-
|
|
358
|
-
The SDK exports two separate ABIs for the different bridge contract types:
|
|
359
|
-
|
|
360
|
-
### POD_BRIDGE_ABI
|
|
361
|
-
|
|
362
|
-
Used for the **BridgeMintBurn** contract on POD Chain:
|
|
363
|
-
- Deposit functions: `deposit()`, `depositNative()`
|
|
364
|
-
- Claim functions: `claim(uint256 id, address token, uint256 blockNumber)`, `claimNative(uint256 id, uint256 blockNumber)`
|
|
365
|
-
- Claims use block number verification via POD precompiles
|
|
366
|
-
- Events: `Deposit`, `DepositNative`, `Claim`, `ClaimNative`
|
|
367
|
-
|
|
368
|
-
### SOURCE_CHAIN_BRIDGE_ABI
|
|
369
|
-
|
|
370
|
-
Used for the **BridgeDepositWithdraw** contract on Source Chain (e.g., Sepolia):
|
|
371
|
-
- Deposit functions: `deposit()`, `depositNative()`
|
|
372
|
-
- Claim functions: `claim(CertifiedLog)`, `claimNative(CertifiedLog)`
|
|
373
|
-
- Claims use POD certificate system with attestations and merkle proofs
|
|
374
|
-
- Events: `Deposit`, `DepositNative`, `Claim`, `ClaimNative`
|
|
375
|
-
|
|
376
|
-
Both are exported from the package and can be used with ethers.js:
|
|
377
|
-
|
|
378
215
|
```typescript
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const sourceChainBridge = new ethers.Contract(sepoliaAddress, SOURCE_CHAIN_BRIDGE_ABI, provider);
|
|
216
|
+
getDepositsSentBy(address: string, fromBlock?: number): Promise<BridgeRequest[]>
|
|
217
|
+
getDepositsReceivedBy(address: string, fromBlock?: number): Promise<BridgeRequest[]>
|
|
218
|
+
getAllDepositsFor(address: string, fromBlock?: number): Promise<BridgeRequestWithType[]>
|
|
219
|
+
canBeClaimed(deposit: BridgeRequest): Promise<boolean>
|
|
220
|
+
areRequestsProcessed(deposits: BridgeRequest[]): Promise<boolean[]>
|
|
385
221
|
```
|
|
386
222
|
|
|
387
223
|
## Types
|
|
@@ -389,264 +225,95 @@ const sourceChainBridge = new ethers.Contract(sepoliaAddress, SOURCE_CHAIN_BRIDG
|
|
|
389
225
|
### BridgeRequest
|
|
390
226
|
|
|
391
227
|
```typescript
|
|
392
|
-
enum BridgeChain {
|
|
393
|
-
SOURCE_CHAIN = 'sourceChain',
|
|
394
|
-
POD = 'pod'
|
|
395
|
-
}
|
|
396
|
-
|
|
397
228
|
interface BridgeRequest {
|
|
398
229
|
requestId: string;
|
|
399
230
|
|
|
400
|
-
// Deposit information
|
|
401
231
|
deposit: {
|
|
402
|
-
chain:
|
|
232
|
+
chain: 'sourceChain' | 'pod';
|
|
403
233
|
txHash: string;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
token: string;
|
|
407
|
-
amount: string;
|
|
234
|
+
depositor: string;
|
|
235
|
+
destination: string;
|
|
236
|
+
token: string;
|
|
237
|
+
amount: string;
|
|
408
238
|
chainId: number;
|
|
409
|
-
blockNumber: number;
|
|
410
|
-
timestamp: number;
|
|
239
|
+
blockNumber: number;
|
|
240
|
+
timestamp: number;
|
|
411
241
|
};
|
|
412
242
|
|
|
413
|
-
// Claim information (optional, only if claimed)
|
|
414
243
|
claim?: {
|
|
415
|
-
chain:
|
|
244
|
+
chain: 'sourceChain' | 'pod';
|
|
416
245
|
txHash: string;
|
|
417
|
-
claimer: string;
|
|
246
|
+
claimer: string;
|
|
418
247
|
chainId: number;
|
|
419
|
-
blockNumber: number;
|
|
420
|
-
timestamp: number;
|
|
248
|
+
blockNumber: number;
|
|
249
|
+
timestamp: number;
|
|
421
250
|
};
|
|
422
251
|
|
|
423
|
-
// Status
|
|
424
252
|
isClaimed: boolean;
|
|
425
|
-
isClaimable: boolean;
|
|
253
|
+
isClaimable: boolean;
|
|
426
254
|
}
|
|
427
255
|
```
|
|
428
256
|
|
|
429
|
-
###
|
|
257
|
+
### ClaimProofData
|
|
430
258
|
|
|
431
259
|
```typescript
|
|
432
|
-
interface
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
260
|
+
interface ClaimProofData {
|
|
261
|
+
ethTokenAddress: string; // Token address on ETH (different from Pod)
|
|
262
|
+
amount: string | bigint; // Same amount as deposited
|
|
263
|
+
to: string; // Recipient address
|
|
264
|
+
committeeEpoch: number; // Hardcoded to 0 for now
|
|
265
|
+
aggregatedSignatures: string; // Concatenated 65-byte ECDSA signatures (r,s,v)
|
|
266
|
+
depositTxHash: string; // Deposit TX hash from Pod
|
|
439
267
|
}
|
|
440
268
|
```
|
|
441
269
|
|
|
442
|
-
###
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
interface CertifiedLog {
|
|
446
|
-
log: {
|
|
447
|
-
addr: string;
|
|
448
|
-
topics: string[];
|
|
449
|
-
data: string;
|
|
450
|
-
};
|
|
451
|
-
log_index: bigint | string;
|
|
452
|
-
certificate: {
|
|
453
|
-
leaf: string;
|
|
454
|
-
certified_receipt: {
|
|
455
|
-
receipt_root: string;
|
|
456
|
-
aggregate_signature: string;
|
|
457
|
-
sorted_attestation_timestamps: bigint[] | string[];
|
|
458
|
-
};
|
|
459
|
-
proof: {
|
|
460
|
-
path: string[];
|
|
461
|
-
};
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
## Bridge Types
|
|
467
|
-
|
|
468
|
-
The SDK supports two bridge implementations:
|
|
469
|
-
|
|
470
|
-
1. **BridgeDepositWithdraw** (Certificate-based) - Source Chain
|
|
471
|
-
- Used for claiming on Source chain from POD deposits
|
|
472
|
-
- Requires certified logs from POD's certificate system via indexer
|
|
473
|
-
- Uses `SOURCE_CHAIN_BRIDGE_ABI`
|
|
474
|
-
- Methods: `claimWithCertificate`, `claimNativeWithCertificate`
|
|
475
|
-
- Certified logs include attestations, receipt root, aggregate signatures, and merkle proofs
|
|
476
|
-
|
|
477
|
-
2. **BridgeMintBurn** (Precompile-based) - POD Chain
|
|
478
|
-
- Used for claiming on POD from Source chain deposits
|
|
479
|
-
- Uses block number verification via POD precompiles
|
|
480
|
-
- Uses `POD_BRIDGE_ABI`
|
|
481
|
-
- Methods: `claimWithBlockNumber`, `claimNativeWithBlockNumber`
|
|
482
|
-
- POD has instant finality (single-block architecture)
|
|
270
|
+
### Signature Recovery Helpers
|
|
483
271
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
### Complete Deposit and Claim Flow (Sepolia → POD)
|
|
272
|
+
Pod uses 64-byte signatures without the parity bit (v), but the ETH contract requires 65-byte (r,s,v) format.
|
|
273
|
+
The helpers exactly match the Rust implementation - they try both recovery IDs and verify against the public key.
|
|
487
274
|
|
|
488
275
|
```typescript
|
|
489
|
-
import {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
sourceChain: {
|
|
495
|
-
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
496
|
-
contractAddress: '0xSepoliaBridgeContract'
|
|
497
|
-
},
|
|
498
|
-
pod: {
|
|
499
|
-
rpcUrl: 'https://rpc.v1.dev.pod.network',
|
|
500
|
-
contractAddress: '0xPodBridgeContract'
|
|
501
|
-
}
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
const actionConfig = {
|
|
505
|
-
sourceChain: { contractAddress: '0xSepoliaBridgeContract' },
|
|
506
|
-
pod: { contractAddress: '0xPodBridgeContract' }
|
|
507
|
-
};
|
|
276
|
+
import {
|
|
277
|
+
extractAggregatedSignatures,
|
|
278
|
+
recoverSignature65B,
|
|
279
|
+
addressFromPublicKey
|
|
280
|
+
} from '@tapforce/pod-bridge-sdk';
|
|
508
281
|
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
const
|
|
512
|
-
const sepoliaSigner = new ethers.Wallet('PRIVATE_KEY', sepoliaProvider);
|
|
282
|
+
// Recommended: Extract signatures directly from Pod receipt
|
|
283
|
+
const podReceipt = await podProvider.send('eth_getTransactionReceipt', [depositTxHash]);
|
|
284
|
+
const aggregatedSignatures = extractAggregatedSignatures(podReceipt, msgHash);
|
|
513
285
|
|
|
514
|
-
//
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
286
|
+
// Low-level: Recover single signature (matches Rust recover_65b_signature)
|
|
287
|
+
const sig65 = recoverSignature65B(
|
|
288
|
+
r, // r component (32 bytes hex)
|
|
289
|
+
s, // s component (32 bytes hex)
|
|
290
|
+
msgHash, // message hash that was signed
|
|
291
|
+
publicKey // signer's public key (to verify recovery)
|
|
292
|
+
);
|
|
520
293
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
console.log(`Deposit tx: ${tx.hash}`);
|
|
524
|
-
|
|
525
|
-
// Step 2: Track deposit
|
|
526
|
-
const deposits = await trackerClient.getDepositsSentBy(await sepoliaSigner.getAddress());
|
|
527
|
-
const latestDeposit = deposits[0];
|
|
528
|
-
|
|
529
|
-
console.log(`Request ID: ${latestDeposit.requestId}`);
|
|
530
|
-
console.log(`Amount: ${ethers.formatEther(latestDeposit.deposit.amount)}`);
|
|
531
|
-
|
|
532
|
-
// Step 3: Wait for finality and claim on POD
|
|
533
|
-
const canClaim = await trackerClient.canBeClaimed(latestDeposit);
|
|
534
|
-
if (canClaim) {
|
|
535
|
-
const claimTx = actionClient.claimWithBlockNumber({
|
|
536
|
-
id: latestDeposit.requestId,
|
|
537
|
-
token: latestDeposit.deposit.token,
|
|
538
|
-
blockNumber: latestDeposit.deposit.blockNumber
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
// Submit claim on POD
|
|
542
|
-
const podProvider = new ethers.JsonRpcProvider(trackerConfig.pod.rpcUrl);
|
|
543
|
-
const podSigner = new ethers.Wallet('PRIVATE_KEY', podProvider);
|
|
544
|
-
const claim = await podSigner.sendTransaction(claimTx);
|
|
545
|
-
await claim.wait();
|
|
546
|
-
console.log(`Claimed on POD: ${claim.hash}`);
|
|
547
|
-
}
|
|
294
|
+
// Utility: Derive address from public key
|
|
295
|
+
const address = addressFromPublicKey(publicKey);
|
|
548
296
|
```
|
|
549
297
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
```typescript
|
|
553
|
-
import { ethers } from 'ethers';
|
|
554
|
-
import { PodToSourceChainActionClient, PodBridgeTrackerClient } from '@tapforce/pod-bridge-sdk';
|
|
555
|
-
import { PodIndexerHttpClient } from '@tapforce/pod-indexer-sdk';
|
|
556
|
-
|
|
557
|
-
// Setup
|
|
558
|
-
const trackerConfig = {
|
|
559
|
-
sourceChain: {
|
|
560
|
-
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_KEY',
|
|
561
|
-
contractAddress: '0xSepoliaBridgeContract'
|
|
562
|
-
},
|
|
563
|
-
pod: {
|
|
564
|
-
rpcUrl: 'https://rpc.v1.dev.pod.network',
|
|
565
|
-
contractAddress: '0xPodBridgeContract'
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
|
|
569
|
-
const actionConfig = {
|
|
570
|
-
sourceChain: { contractAddress: '0xSepoliaBridgeContract' },
|
|
571
|
-
pod: { contractAddress: '0xPodBridgeContract' }
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
const actionClient = new PodToSourceChainActionClient(actionConfig);
|
|
575
|
-
const trackerClient = new PodBridgeTrackerClient(trackerConfig);
|
|
576
|
-
const podProvider = new ethers.JsonRpcProvider(trackerConfig.pod.rpcUrl);
|
|
577
|
-
const podSigner = new ethers.Wallet('PRIVATE_KEY', podProvider);
|
|
578
|
-
|
|
579
|
-
// Initialize indexer client for getting certified logs
|
|
580
|
-
const indexerClient = new PodIndexerHttpClient({
|
|
581
|
-
apiUrl: 'YOUR_INDEXER_API_URL',
|
|
582
|
-
apiKey: 'YOUR_INDEXER_API_KEY'
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
// Step 1: Deposit tokens on POD
|
|
586
|
-
const depositTx = actionClient.depositToken({
|
|
587
|
-
token: '0xTokenAddress',
|
|
588
|
-
amount: ethers.parseEther('10').toString(),
|
|
589
|
-
destinationWalletAddress: await podSigner.getAddress()
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
const tx = await podSigner.sendTransaction(depositTx);
|
|
593
|
-
const receipt = await tx.wait();
|
|
594
|
-
console.log(`Deposit tx on POD: ${tx.hash}`);
|
|
595
|
-
|
|
596
|
-
// Step 2: Get certified log from POD transaction using indexer
|
|
597
|
-
const certifiedLogResponse = await indexerClient.getBridgeCertifiedLog({
|
|
598
|
-
transactionHash: tx.hash,
|
|
599
|
-
bridgeContractAddress: trackerConfig.pod.contractAddress,
|
|
600
|
-
rpcUrl: trackerConfig.pod.rpcUrl
|
|
601
|
-
});
|
|
298
|
+
## Events
|
|
602
299
|
|
|
603
|
-
|
|
604
|
-
if (!certifiedLog) {
|
|
605
|
-
throw new Error('Certified log not found');
|
|
606
|
-
}
|
|
607
|
-
console.log('Got certified log with attestations');
|
|
300
|
+
The bridge contract emits:
|
|
608
301
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
from: await podSigner.getAddress()
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
const sepoliaProvider = new ethers.JsonRpcProvider(trackerConfig.sourceChain.rpcUrl);
|
|
616
|
-
const sepoliaSigner = new ethers.Wallet('PRIVATE_KEY', sepoliaProvider);
|
|
617
|
-
const claim = await sepoliaSigner.sendTransaction(claimTx);
|
|
618
|
-
await claim.wait();
|
|
619
|
-
console.log(`Claimed on Sepolia: ${claim.hash}`);
|
|
302
|
+
```solidity
|
|
303
|
+
event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount);
|
|
304
|
+
event Claim(bytes32 indexed id, address indexed to, address token, uint256 amount);
|
|
620
305
|
```
|
|
621
306
|
|
|
622
307
|
## Development
|
|
623
308
|
|
|
624
|
-
### Build
|
|
625
|
-
|
|
626
|
-
```bash
|
|
627
|
-
npm run build
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
### Clean
|
|
631
|
-
|
|
632
309
|
```bash
|
|
633
|
-
npm run
|
|
310
|
+
npm run build # Build
|
|
311
|
+
npm run clean # Clean
|
|
634
312
|
```
|
|
635
313
|
|
|
636
314
|
## Dependencies
|
|
637
315
|
|
|
638
|
-
- **ethers**: ^6.15.0
|
|
639
|
-
- **typescript**: ^5.8.3 - TypeScript support
|
|
640
|
-
|
|
641
|
-
### External Dependencies for Claims
|
|
642
|
-
|
|
643
|
-
To claim tokens on Source Chain from POD deposits, you'll need:
|
|
644
|
-
|
|
645
|
-
- **@tapforce/pod-indexer-sdk**: For retrieving certified logs from POD transactions
|
|
646
|
-
|
|
647
|
-
```bash
|
|
648
|
-
npm install @tapforce/pod-indexer-sdk
|
|
649
|
-
```
|
|
316
|
+
- **ethers**: ^6.15.0
|
|
650
317
|
|
|
651
318
|
## License
|
|
652
319
|
|
|
@@ -654,13 +321,5 @@ ISC
|
|
|
654
321
|
|
|
655
322
|
## Repository
|
|
656
323
|
|
|
657
|
-
-
|
|
658
|
-
-
|
|
659
|
-
|
|
660
|
-
## Contributing
|
|
661
|
-
|
|
662
|
-
Contributions are welcome! Please open an issue or submit a pull request.
|
|
663
|
-
|
|
664
|
-
## Author
|
|
665
|
-
|
|
666
|
-
Tapforce
|
|
324
|
+
- [GitHub](https://github.com/tapforce/pod-ts-bridge-sdk)
|
|
325
|
+
- [Issues](https://github.com/tapforce/pod-ts-bridge-sdk/issues)
|