@tapforce/pod-bridge-sdk 1.2.5 → 2.1.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 +132 -156
- package/dist/clients/action/pod-to-source-chain-client.d.ts +26 -21
- package/dist/clients/action/pod-to-source-chain-client.js +32 -28
- package/dist/clients/action/source-chain-to-pod-client.d.ts +13 -6
- package/dist/clients/action/source-chain-to-pod-client.js +15 -8
- package/dist/clients/tracker/client.js +2 -1
- package/dist/clients/tracker/pod-tracker.service.d.ts +9 -13
- package/dist/clients/tracker/pod-tracker.service.js +48 -32
- package/dist/clients/tracker/source-chain-tracker.service.d.ts +5 -10
- package/dist/clients/tracker/source-chain-tracker.service.js +16 -18
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -11
- package/dist/libs/abi/bridge.abi.d.ts +13 -6
- package/dist/libs/abi/bridge.abi.js +29 -20
- package/dist/libs/helpers/bridge-claim-proof.helper.d.ts +16 -0
- package/dist/libs/helpers/bridge-claim-proof.helper.js +27 -0
- package/dist/libs/helpers/convert-certified-log.helper.d.ts +2 -6
- package/dist/libs/helpers/convert-certified-log.helper.js +4 -8
- package/dist/libs/types/pod-bridge.types.d.ts +56 -17
- package/package.json +1 -1
- package/dist/libs/helpers/signature-recovery.helper.d.ts +0 -161
- package/dist/libs/helpers/signature-recovery.helper.js +0 -447
package/README.md
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
# Pod Bridge SDK
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for bridging
|
|
3
|
+
TypeScript SDK for bridging tokens between POD and EVM chains (e.g., ETH Mainnet).
|
|
4
4
|
|
|
5
5
|
## Architecture
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
ETH
|
|
9
|
-
Pod
|
|
8
|
+
ETH -> Pod: Deposit on ETH, AUTO-CLAIM on Pod (no claim TX needed)
|
|
9
|
+
Pod -> ETH: Deposit on Pod, claim on ETH with proof from pod_getBridgeClaimProof RPC
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
**Key points:**
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
13
|
+
- ERC20 and native token bridging supported
|
|
14
|
+
- Token addresses differ between chains (e.g., USDC on ETH maps to native `0xEeee...EEeE` on Pod)
|
|
15
|
+
- Use ETH-side decimals for amounts (e.g., `1e6` for 1 USDC, not `1e18`)
|
|
16
|
+
- Pod transactions require EIP-1559 (type 2) gas params, not legacy `gasPrice`
|
|
16
17
|
|
|
17
18
|
## Features
|
|
18
19
|
|
|
19
|
-
- **Bridge
|
|
20
|
-
- **
|
|
20
|
+
- **Bridge Deposits**: Deposit ERC20 or native tokens to the bridge (with optional CLOB integration)
|
|
21
|
+
- **Claim with Proof**: Claim on ETH using `pod_getBridgeClaimProof` RPC
|
|
22
|
+
- **Track Bridge Requests**: Query deposits sent/received by any address
|
|
21
23
|
- **Claim Status Tracking**: Monitor claim status and finality
|
|
22
24
|
- **Transaction Building**: Generate unsigned transactions ready for signing
|
|
23
|
-
- **Type-Safe**: Full TypeScript support
|
|
24
25
|
|
|
25
26
|
## Installation
|
|
26
27
|
|
|
@@ -35,12 +36,13 @@ pnpm add @tapforce/pod-bridge-sdk
|
|
|
35
36
|
## Quick Start
|
|
36
37
|
|
|
37
38
|
```typescript
|
|
38
|
-
import {
|
|
39
|
+
import {
|
|
39
40
|
SourceChainToPodActionClient,
|
|
40
41
|
PodToSourceChainActionClient,
|
|
41
42
|
PodBridgeTrackerClient,
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
getBridgeClaimProof,
|
|
44
|
+
SOURCE_CHAIN_BRIDGE_ABI,
|
|
45
|
+
POD_BRIDGE_ABI,
|
|
44
46
|
} from '@tapforce/pod-bridge-sdk';
|
|
45
47
|
```
|
|
46
48
|
|
|
@@ -53,10 +55,10 @@ import { PodBridgeActionsClientConfig } from '@tapforce/pod-bridge-sdk';
|
|
|
53
55
|
|
|
54
56
|
const actionConfig: PodBridgeActionsClientConfig = {
|
|
55
57
|
sourceChain: {
|
|
56
|
-
contractAddress: '0x...' //
|
|
58
|
+
contractAddress: '0x...' // BridgeDepositWithdraw on ETH
|
|
57
59
|
},
|
|
58
60
|
pod: {
|
|
59
|
-
contractAddress: '
|
|
61
|
+
contractAddress: '0x0000000000000000000000000000000000B41D9E' // BridgeMintBurn on Pod
|
|
60
62
|
}
|
|
61
63
|
};
|
|
62
64
|
```
|
|
@@ -69,13 +71,13 @@ import { ethers } from 'ethers';
|
|
|
69
71
|
|
|
70
72
|
const trackerConfig: PodBridgeConfig = {
|
|
71
73
|
sourceChain: {
|
|
72
|
-
provider: new ethers.JsonRpcProvider('https://
|
|
74
|
+
provider: new ethers.JsonRpcProvider('https://eth-mainnet.rpc.example'),
|
|
73
75
|
contractAddress: '0x...',
|
|
74
|
-
deploymentBlock:
|
|
76
|
+
deploymentBlock: 12345678 // Optional: avoids indexing empty blocks
|
|
75
77
|
},
|
|
76
78
|
pod: {
|
|
77
79
|
provider: new ethers.JsonRpcProvider('https://rpc.pod.network'),
|
|
78
|
-
contractAddress: '
|
|
80
|
+
contractAddress: '0x0000000000000000000000000000000000B41D9E',
|
|
79
81
|
deploymentBlock: 0
|
|
80
82
|
}
|
|
81
83
|
};
|
|
@@ -83,76 +85,81 @@ const trackerConfig: PodBridgeConfig = {
|
|
|
83
85
|
|
|
84
86
|
## Usage
|
|
85
87
|
|
|
86
|
-
### ETH
|
|
88
|
+
### ETH -> Pod (Auto-claim)
|
|
87
89
|
|
|
88
|
-
Deposits on ETH are automatically claimed on Pod after block finalization
|
|
90
|
+
Deposits on ETH are automatically claimed on Pod after block finalization.
|
|
89
91
|
|
|
90
92
|
```typescript
|
|
91
93
|
const client = new SourceChainToPodActionClient(actionConfig);
|
|
92
94
|
|
|
93
|
-
//
|
|
95
|
+
// Simple deposit (bridge only)
|
|
94
96
|
const depositTx = client.deposit({
|
|
95
|
-
token: '
|
|
96
|
-
amount: ethers.
|
|
97
|
-
destinationWalletAddress: '0x...',
|
|
98
|
-
from: '0x...'
|
|
97
|
+
token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on ETH
|
|
98
|
+
amount: ethers.parseUnits('1', 6), // 1 USDC (use ETH-side decimals)
|
|
99
|
+
destinationWalletAddress: '0x...',
|
|
100
|
+
from: '0x...',
|
|
101
|
+
// permit: '0x...' // Optional: ERC20 permit bytes
|
|
99
102
|
});
|
|
100
103
|
|
|
101
|
-
//
|
|
104
|
+
// CLOB deposit — bridge and deposit to orderbook in one TX:
|
|
105
|
+
// const clobTx = client.deposit({
|
|
106
|
+
// token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT on ETH
|
|
107
|
+
// amount: ethers.parseUnits('1', 6),
|
|
108
|
+
// destinationWalletAddress: '0x...',
|
|
109
|
+
// callContract: '0x000000000000000000000000000000000000C10B', // CLOB orderbook
|
|
110
|
+
// reserveBalance: ethers.parseUnits('0.5', 6), // Keep 0.5, forward 0.5 to CLOB
|
|
111
|
+
// from: '0x...',
|
|
112
|
+
// });
|
|
113
|
+
|
|
114
|
+
// Don't forget to approve the bridge contract first for ERC20 tokens
|
|
102
115
|
const tx = await signer.sendTransaction(depositTx);
|
|
103
116
|
const receipt = await tx.wait();
|
|
104
117
|
|
|
105
118
|
// No claim needed - balance auto-credited on Pod after finalization
|
|
106
119
|
```
|
|
107
120
|
|
|
108
|
-
### Pod
|
|
121
|
+
### Pod -> ETH (Claim with proof)
|
|
109
122
|
|
|
110
|
-
Deposits on Pod require
|
|
123
|
+
Deposits on Pod require claiming on ETH with a proof from `pod_getBridgeClaimProof`.
|
|
111
124
|
|
|
112
125
|
```typescript
|
|
113
|
-
import {
|
|
126
|
+
import {
|
|
114
127
|
PodToSourceChainActionClient,
|
|
115
|
-
|
|
128
|
+
getBridgeClaimProof,
|
|
129
|
+
ClaimProofData,
|
|
116
130
|
} from '@tapforce/pod-bridge-sdk';
|
|
117
131
|
|
|
118
132
|
const client = new PodToSourceChainActionClient(actionConfig);
|
|
119
133
|
|
|
120
134
|
// Step 1: Deposit on Pod
|
|
121
135
|
const depositTx = client.deposit({
|
|
122
|
-
token: '
|
|
123
|
-
amount: ethers.
|
|
124
|
-
destinationWalletAddress: '0x...',
|
|
125
|
-
from: '0x...'
|
|
136
|
+
token: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', // Native token on Pod
|
|
137
|
+
amount: ethers.parseUnits('1', 6), // Use ETH-side decimals (1 USDC = 1e6)
|
|
138
|
+
destinationWalletAddress: '0x...',
|
|
139
|
+
from: '0x...',
|
|
126
140
|
});
|
|
127
141
|
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const committee = await podProvider.send('pod_getCommittee', {});
|
|
142
|
+
// Pod requires EIP-1559 gas params (all zeros for free transactions)
|
|
143
|
+
const tx = await podSigner.sendTransaction({
|
|
144
|
+
...depositTx,
|
|
145
|
+
maxFeePerGas: 0n,
|
|
146
|
+
maxPriorityFeePerGas: 0n,
|
|
147
|
+
gasLimit: 0n,
|
|
148
|
+
});
|
|
136
149
|
|
|
137
|
-
// Step
|
|
138
|
-
const
|
|
139
|
-
podReceipt,
|
|
140
|
-
committee.validators // [[0, "02..."], [1, "03..."], ...] compressed pubkeys
|
|
141
|
-
);
|
|
150
|
+
// Step 2: Get claim proof from Pod RPC
|
|
151
|
+
const { proof, auxTxSuffix } = await getBridgeClaimProof(podProvider, tx.hash);
|
|
142
152
|
|
|
143
|
-
// Step
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
depositTxHash: tx.hash // Deposit TX hash from Pod
|
|
152
|
-
},
|
|
153
|
-
from: '0x...'
|
|
154
|
-
});
|
|
153
|
+
// Step 3: Claim on ETH
|
|
154
|
+
const claimData: ClaimProofData = {
|
|
155
|
+
ethTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on ETH
|
|
156
|
+
amount: ethers.parseUnits('1', 6), // Must match deposited amount
|
|
157
|
+
to: '0x...',
|
|
158
|
+
proof,
|
|
159
|
+
auxTxSuffix,
|
|
160
|
+
};
|
|
155
161
|
|
|
162
|
+
const claimTx = client.claim({ claimData, from: '0x...' });
|
|
156
163
|
const claim = await ethSigner.sendTransaction(claimTx);
|
|
157
164
|
```
|
|
158
165
|
|
|
@@ -166,22 +173,18 @@ const deposits = await tracker.getAllDepositsFor('0x...');
|
|
|
166
173
|
|
|
167
174
|
for (const deposit of deposits) {
|
|
168
175
|
console.log('Request ID:', deposit.requestId);
|
|
169
|
-
console.log('Direction:', deposit.deposit.chain === 'sourceChain' ? 'ETH
|
|
170
|
-
console.log('From:', deposit.deposit.depositor);
|
|
171
|
-
console.log('To:', deposit.deposit.destination);
|
|
176
|
+
console.log('Direction:', deposit.deposit.chain === 'sourceChain' ? 'ETH -> Pod' : 'Pod -> ETH');
|
|
172
177
|
console.log('Token:', deposit.deposit.token);
|
|
173
178
|
console.log('Amount:', deposit.deposit.amount);
|
|
174
|
-
console.log('
|
|
175
|
-
console.log('
|
|
179
|
+
console.log('Timestamp:', new Date(deposit.deposit.timestamp * 1000).toISOString());
|
|
180
|
+
console.log('Status:', deposit.isClaimed ? 'Claimed' : (deposit.isClaimable ? 'Claimable' : 'Pending'));
|
|
176
181
|
}
|
|
177
182
|
|
|
178
|
-
// Get deposits sent by address
|
|
183
|
+
// Get deposits sent/received by address
|
|
179
184
|
const sent = await tracker.getDepositsSentBy('0x...');
|
|
180
|
-
|
|
181
|
-
// Get deposits received by address
|
|
182
185
|
const received = await tracker.getDepositsReceivedBy('0x...');
|
|
183
186
|
|
|
184
|
-
// Check if deposit can be claimed
|
|
187
|
+
// Check if deposit can be claimed
|
|
185
188
|
const canClaim = await tracker.canBeClaimed(deposit);
|
|
186
189
|
|
|
187
190
|
// Batch check processed status
|
|
@@ -192,38 +195,56 @@ const statuses = await tracker.areRequestsProcessed(deposits);
|
|
|
192
195
|
|
|
193
196
|
### SourceChainToPodActionClient
|
|
194
197
|
|
|
195
|
-
For ETH
|
|
198
|
+
For ETH -> Pod deposits (auto-claim on Pod).
|
|
196
199
|
|
|
197
200
|
```typescript
|
|
198
|
-
// Deposit ERC20 tokens
|
|
199
201
|
deposit(args: {
|
|
200
202
|
token: string;
|
|
201
203
|
amount: string | bigint;
|
|
202
204
|
destinationWalletAddress: string;
|
|
205
|
+
callContract?: string; // Contract to call on Pod after deposit (default: address(0))
|
|
206
|
+
reserveBalance?: string | bigint; // Amount to reserve when using callContract (default: 0)
|
|
203
207
|
from?: string;
|
|
208
|
+
permit?: string; // Optional ERC20 permit bytes (default: '0x')
|
|
204
209
|
}): UnsignedTransaction
|
|
205
210
|
```
|
|
206
211
|
|
|
207
212
|
### PodToSourceChainActionClient
|
|
208
213
|
|
|
209
|
-
For Pod
|
|
214
|
+
For Pod -> ETH deposits and claims.
|
|
210
215
|
|
|
211
216
|
```typescript
|
|
212
|
-
// Deposit
|
|
217
|
+
// Deposit tokens on Pod
|
|
213
218
|
deposit(args: {
|
|
214
|
-
token: string;
|
|
215
|
-
amount: string | bigint;
|
|
219
|
+
token: string; // Use 0xEeee...EEeE for native token
|
|
220
|
+
amount: string | bigint; // Use ETH-side decimals
|
|
216
221
|
destinationWalletAddress: string;
|
|
222
|
+
callContract?: string; // Contract to call after deposit (default: address(0))
|
|
223
|
+
reserveBalance?: string | bigint; // Amount to reserve when using callContract (default: 0)
|
|
224
|
+
permit?: string; // Optional permit bytes (default: '0x')
|
|
217
225
|
from?: string;
|
|
218
226
|
}): UnsignedTransaction
|
|
219
227
|
|
|
220
|
-
// Claim on ETH with
|
|
228
|
+
// Claim on ETH with proof
|
|
221
229
|
claim(args: {
|
|
222
230
|
claimData: ClaimProofData;
|
|
223
231
|
from?: string;
|
|
224
232
|
}): UnsignedTransaction
|
|
225
233
|
```
|
|
226
234
|
|
|
235
|
+
### getBridgeClaimProof
|
|
236
|
+
|
|
237
|
+
Helper to call `pod_getBridgeClaimProof` RPC and format the result.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { getBridgeClaimProof } from '@tapforce/pod-bridge-sdk';
|
|
241
|
+
|
|
242
|
+
const { proof, committeeEpoch, auxTxSuffix } = await getBridgeClaimProof(
|
|
243
|
+
podProvider, // ethers.JsonRpcProvider connected to Pod RPC
|
|
244
|
+
depositTxHash // TX hash of the deposit on Pod
|
|
245
|
+
);
|
|
246
|
+
```
|
|
247
|
+
|
|
227
248
|
### PodBridgeTrackerClient
|
|
228
249
|
|
|
229
250
|
```typescript
|
|
@@ -236,12 +257,24 @@ areRequestsProcessed(deposits: BridgeRequest[]): Promise<boolean[]>
|
|
|
236
257
|
|
|
237
258
|
## Types
|
|
238
259
|
|
|
260
|
+
### ClaimProofData
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
interface ClaimProofData {
|
|
264
|
+
ethTokenAddress: string; // Token address on ETH (different from Pod)
|
|
265
|
+
amount: string | bigint; // Must match deposited amount
|
|
266
|
+
to: string; // Recipient address
|
|
267
|
+
proof: string; // Hex bytes from pod_getBridgeClaimProof
|
|
268
|
+
auxTxSuffix: string; // Hex bytes from pod_getBridgeClaimProof
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
239
272
|
### BridgeRequest
|
|
240
273
|
|
|
241
274
|
```typescript
|
|
242
275
|
interface BridgeRequest {
|
|
243
276
|
requestId: string;
|
|
244
|
-
|
|
277
|
+
|
|
245
278
|
deposit: {
|
|
246
279
|
chain: 'sourceChain' | 'pod';
|
|
247
280
|
txHash: string;
|
|
@@ -249,11 +282,13 @@ interface BridgeRequest {
|
|
|
249
282
|
destination: string;
|
|
250
283
|
token: string;
|
|
251
284
|
amount: string;
|
|
285
|
+
callContract?: string; // Contract called after deposit (address(0) for simple bridge)
|
|
286
|
+
reserveBalance?: string; // Amount reserved when using callContract ('0' for simple bridge)
|
|
252
287
|
chainId: number;
|
|
253
288
|
blockNumber: number;
|
|
254
|
-
timestamp: number;
|
|
289
|
+
timestamp: number; // Unix timestamp (seconds)
|
|
255
290
|
};
|
|
256
|
-
|
|
291
|
+
|
|
257
292
|
claim?: {
|
|
258
293
|
chain: 'sourceChain' | 'pod';
|
|
259
294
|
txHash: string;
|
|
@@ -262,99 +297,40 @@ interface BridgeRequest {
|
|
|
262
297
|
blockNumber: number;
|
|
263
298
|
timestamp: number;
|
|
264
299
|
};
|
|
265
|
-
|
|
300
|
+
|
|
266
301
|
isClaimed: boolean;
|
|
267
302
|
isClaimable: boolean;
|
|
268
303
|
}
|
|
269
304
|
```
|
|
270
305
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
```typescript
|
|
274
|
-
interface ClaimProofData {
|
|
275
|
-
ethTokenAddress: string; // Token address on ETH (different from Pod)
|
|
276
|
-
amount: string | bigint; // Same amount as deposited
|
|
277
|
-
to: string; // Recipient address
|
|
278
|
-
committeeEpoch: number; // Hardcoded to 0 for now
|
|
279
|
-
aggregatedSignatures: string; // Concatenated 65-byte ECDSA signatures (r,s,v)
|
|
280
|
-
depositTxHash: string; // Deposit TX hash from Pod
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### Signature Recovery Helpers
|
|
285
|
-
|
|
286
|
-
Pod returns DER-encoded 64-byte signatures without the parity bit (v), but the ETH contract requires 65-byte (r,s,v) format.
|
|
287
|
-
The SDK handles this by trying both v values (27/28) and verifying against the committee's validator public keys.
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
import {
|
|
291
|
-
extractAggregatedSignaturesWithValidators,
|
|
292
|
-
extractAggregatedSignatures,
|
|
293
|
-
parseDerSignature,
|
|
294
|
-
addressFromPublicKey,
|
|
295
|
-
recoverSignatureWithoutPubkey
|
|
296
|
-
} from '@tapforce/pod-bridge-sdk';
|
|
297
|
-
|
|
298
|
-
// Recommended: Extract with validator verification (correct v-values)
|
|
299
|
-
const podReceipt = await podProvider.send('eth_getTransactionReceipt', [depositTxHash]);
|
|
300
|
-
const committee = await podProvider.send('pod_getCommittee', {});
|
|
301
|
-
const aggregatedSignatures = extractAggregatedSignaturesWithValidators(
|
|
302
|
-
podReceipt,
|
|
303
|
-
committee.validators // [[index, compressedPubKey], ...]
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
// Alternative: Extract without validator verification (may have wrong v-values)
|
|
307
|
-
const sigs = extractAggregatedSignatures(podReceipt);
|
|
308
|
-
|
|
309
|
-
// Low-level: Parse DER signature to r,s components
|
|
310
|
-
const { r, s } = parseDerSignature(derEncodedSig);
|
|
311
|
-
|
|
312
|
-
// Low-level: Recover 65-byte signature without pubkey (tries v=27 first)
|
|
313
|
-
const sig65 = recoverSignatureWithoutPubkey(r, s, msgHash);
|
|
306
|
+
## Events
|
|
314
307
|
|
|
315
|
-
|
|
316
|
-
const address = addressFromPublicKey(uncompressedPubKey);
|
|
317
|
-
```
|
|
308
|
+
The bridge contracts emit different events per chain (id type differs):
|
|
318
309
|
|
|
319
|
-
|
|
310
|
+
```solidity
|
|
311
|
+
// ETH (Source Chain)
|
|
312
|
+
event Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance);
|
|
313
|
+
event Claim(bytes32 indexed txHash, address token, address mirrorToken, uint256 amount, address indexed to);
|
|
320
314
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// ... standard fields ...
|
|
324
|
-
attested_tx: {
|
|
325
|
-
hash: string; // Hash signed by validators
|
|
326
|
-
committee_epoch: number;
|
|
327
|
-
};
|
|
328
|
-
signatures: {
|
|
329
|
-
"0": string; // DER-encoded signature from validator 0
|
|
330
|
-
"1": string; // DER-encoded signature from validator 1
|
|
331
|
-
// ...
|
|
332
|
-
};
|
|
333
|
-
}
|
|
315
|
+
// Pod
|
|
316
|
+
event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance);
|
|
334
317
|
```
|
|
335
318
|
|
|
336
|
-
|
|
319
|
+
## ABIs
|
|
337
320
|
|
|
338
|
-
|
|
339
|
-
// pod_getCommittee response
|
|
340
|
-
{
|
|
341
|
-
validators: [
|
|
342
|
-
[0, "024ee7..."], // [index, compressed_secp256k1_pubkey]
|
|
343
|
-
[1, "025c59..."],
|
|
344
|
-
// ...
|
|
345
|
-
],
|
|
346
|
-
quorum_size: 3
|
|
347
|
-
}
|
|
348
|
-
```
|
|
321
|
+
The SDK exports separate ABIs for each chain:
|
|
349
322
|
|
|
350
|
-
|
|
323
|
+
- `SOURCE_CHAIN_BRIDGE_ABI` - ETH bridge (6-param deposit with callContract/reserveBalance/permit, claim with proof)
|
|
324
|
+
- `POD_BRIDGE_ABI` - Pod bridge (6-param deposit, bytes32 id events)
|
|
325
|
+
- `BRIDGE_ABI` - Alias for `SOURCE_CHAIN_BRIDGE_ABI`
|
|
351
326
|
|
|
352
|
-
|
|
327
|
+
## Pod-specific Notes
|
|
353
328
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
329
|
+
- Pod system contract address: `0x0000000000000000000000000000000000B41D9E`
|
|
330
|
+
- Pod transactions are free: use `maxFeePerGas: 0, maxPriorityFeePerGas: 0, gasLimit: 0`
|
|
331
|
+
- Must use EIP-1559 (type 2) transactions, not legacy `gasPrice`
|
|
332
|
+
- Pod does NOT need `value` set for native token deposits - the system contract handles balance internally
|
|
333
|
+
- Pod returns `blockNumber` as a Unix timestamp in receipts
|
|
358
334
|
|
|
359
335
|
## Development
|
|
360
336
|
|
|
@@ -2,35 +2,37 @@ import { UnsignedTransaction, ClaimProofData, PodBridgeActionsClientConfig } fro
|
|
|
2
2
|
/**
|
|
3
3
|
* PodToSourceChainActionClient - Handles Pod -> ETH bridging
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* - User claims on ETH with aggregated validator signatures
|
|
9
|
-
* - Only ERC20 tokens supported (no native tokens)
|
|
5
|
+
* - User deposits tokens on Pod
|
|
6
|
+
* - Call pod_getBridgeClaimProof(depositTxHash) to get proof
|
|
7
|
+
* - User claims on ETH with proof + auxTxSuffix
|
|
10
8
|
*
|
|
11
9
|
* Important notes:
|
|
12
|
-
* - Token addresses on Pod and ETH are different (
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* - proof is the deposit TX hash (subject to change in the future)
|
|
10
|
+
* - Token addresses on Pod and ETH are different (deposit AAAA on Pod, claim BBBB on ETH)
|
|
11
|
+
* - For USDC-like tokens, use ETH-side decimals (e.g. parseUnits('1', 6) for USDC)
|
|
12
|
+
* - Pod system contract handles balance internally - do NOT set tx value
|
|
16
13
|
*/
|
|
17
14
|
export declare class PodToSourceChainActionClient {
|
|
18
15
|
private readonly config;
|
|
19
|
-
private readonly
|
|
16
|
+
private readonly podIface;
|
|
17
|
+
private readonly sourceChainIface;
|
|
20
18
|
constructor(config: PodBridgeActionsClientConfig);
|
|
21
19
|
/**
|
|
22
|
-
* Create unsigned transaction for depositing
|
|
20
|
+
* Create unsigned transaction for depositing tokens from Pod to ETH
|
|
23
21
|
*
|
|
24
22
|
* After deposit:
|
|
25
|
-
* -
|
|
26
|
-
* - Call claim() on ETH with
|
|
23
|
+
* - Call pod_getBridgeClaimProof(depositTxHash) on Pod RPC
|
|
24
|
+
* - Call claim() on ETH with the proof
|
|
27
25
|
*
|
|
28
|
-
* Note:
|
|
29
|
-
*
|
|
26
|
+
* Note: Pod system contract handles balance deduction internally.
|
|
27
|
+
* Do NOT set transaction value - even for native token (0xEeee...EEeE) deposits.
|
|
28
|
+
* Use ETH-side decimals for amount (e.g. 1e6 for 1 USDC, not 1e18).
|
|
30
29
|
*
|
|
31
|
-
* @param args.token Token address on Pod to deposit
|
|
32
|
-
* @param args.amount Amount to deposit (in
|
|
30
|
+
* @param args.token Token address on Pod to deposit (use 0xEeee...EEeE for native)
|
|
31
|
+
* @param args.amount Amount to deposit (in ETH-side decimals)
|
|
33
32
|
* @param args.destinationWalletAddress Recipient address on ETH
|
|
33
|
+
* @param args.callContract Optional: contract to call after deposit (default: address(0) for simple bridge)
|
|
34
|
+
* @param args.reserveBalance Optional: amount to keep as reserve when using callContract (default: 0)
|
|
35
|
+
* @param args.permit Optional permit bytes (default '0x')
|
|
34
36
|
* @param args.from Optional sender address
|
|
35
37
|
* @returns Unsigned transaction template with encoded deposit call
|
|
36
38
|
*/
|
|
@@ -38,15 +40,18 @@ export declare class PodToSourceChainActionClient {
|
|
|
38
40
|
token: string;
|
|
39
41
|
amount: string | bigint;
|
|
40
42
|
destinationWalletAddress: string;
|
|
43
|
+
callContract?: string;
|
|
44
|
+
reserveBalance?: string | bigint;
|
|
45
|
+
permit?: string;
|
|
41
46
|
from?: string;
|
|
42
47
|
}): UnsignedTransaction;
|
|
43
48
|
/**
|
|
44
|
-
* Create unsigned transaction for claiming
|
|
49
|
+
* Create unsigned transaction for claiming tokens on ETH (source chain)
|
|
45
50
|
*
|
|
46
|
-
* This is called after depositing on Pod and
|
|
47
|
-
*
|
|
51
|
+
* This is called after depositing on Pod and obtaining the claim proof
|
|
52
|
+
* via pod_getBridgeClaimProof RPC (or the getBridgeClaimProof helper).
|
|
48
53
|
*
|
|
49
|
-
* @param args.claimData Claim proof data with
|
|
54
|
+
* @param args.claimData Claim proof data with proof + auxTxSuffix
|
|
50
55
|
* @param args.from Optional sender address
|
|
51
56
|
* @returns Unsigned transaction template with encoded claim call
|
|
52
57
|
*/
|
|
@@ -6,44 +6,49 @@ const bridge_abi_1 = require("../../libs/abi/bridge.abi");
|
|
|
6
6
|
/**
|
|
7
7
|
* PodToSourceChainActionClient - Handles Pod -> ETH bridging
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* - User claims on ETH with aggregated validator signatures
|
|
13
|
-
* - Only ERC20 tokens supported (no native tokens)
|
|
9
|
+
* - User deposits tokens on Pod
|
|
10
|
+
* - Call pod_getBridgeClaimProof(depositTxHash) to get proof
|
|
11
|
+
* - User claims on ETH with proof + auxTxSuffix
|
|
14
12
|
*
|
|
15
13
|
* Important notes:
|
|
16
|
-
* - Token addresses on Pod and ETH are different (
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
19
|
-
* - proof is the deposit TX hash (subject to change in the future)
|
|
14
|
+
* - Token addresses on Pod and ETH are different (deposit AAAA on Pod, claim BBBB on ETH)
|
|
15
|
+
* - For USDC-like tokens, use ETH-side decimals (e.g. parseUnits('1', 6) for USDC)
|
|
16
|
+
* - Pod system contract handles balance internally - do NOT set tx value
|
|
20
17
|
*/
|
|
21
18
|
class PodToSourceChainActionClient {
|
|
22
19
|
constructor(config) {
|
|
23
20
|
this.config = config;
|
|
24
|
-
this.
|
|
21
|
+
this.podIface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
|
|
22
|
+
this.sourceChainIface = new ethers_1.Interface(bridge_abi_1.SOURCE_CHAIN_BRIDGE_ABI);
|
|
25
23
|
}
|
|
26
24
|
/**
|
|
27
|
-
* Create unsigned transaction for depositing
|
|
25
|
+
* Create unsigned transaction for depositing tokens from Pod to ETH
|
|
28
26
|
*
|
|
29
27
|
* After deposit:
|
|
30
|
-
* -
|
|
31
|
-
* - Call claim() on ETH with
|
|
28
|
+
* - Call pod_getBridgeClaimProof(depositTxHash) on Pod RPC
|
|
29
|
+
* - Call claim() on ETH with the proof
|
|
32
30
|
*
|
|
33
|
-
* Note:
|
|
34
|
-
*
|
|
31
|
+
* Note: Pod system contract handles balance deduction internally.
|
|
32
|
+
* Do NOT set transaction value - even for native token (0xEeee...EEeE) deposits.
|
|
33
|
+
* Use ETH-side decimals for amount (e.g. 1e6 for 1 USDC, not 1e18).
|
|
35
34
|
*
|
|
36
|
-
* @param args.token Token address on Pod to deposit
|
|
37
|
-
* @param args.amount Amount to deposit (in
|
|
35
|
+
* @param args.token Token address on Pod to deposit (use 0xEeee...EEeE for native)
|
|
36
|
+
* @param args.amount Amount to deposit (in ETH-side decimals)
|
|
38
37
|
* @param args.destinationWalletAddress Recipient address on ETH
|
|
38
|
+
* @param args.callContract Optional: contract to call after deposit (default: address(0) for simple bridge)
|
|
39
|
+
* @param args.reserveBalance Optional: amount to keep as reserve when using callContract (default: 0)
|
|
40
|
+
* @param args.permit Optional permit bytes (default '0x')
|
|
39
41
|
* @param args.from Optional sender address
|
|
40
42
|
* @returns Unsigned transaction template with encoded deposit call
|
|
41
43
|
*/
|
|
42
44
|
deposit(args) {
|
|
43
|
-
const data = this.
|
|
45
|
+
const data = this.podIface.encodeFunctionData('deposit', [
|
|
44
46
|
args.token,
|
|
45
47
|
args.amount,
|
|
46
|
-
args.destinationWalletAddress
|
|
48
|
+
args.destinationWalletAddress,
|
|
49
|
+
args.callContract ?? ethers_1.ZeroAddress,
|
|
50
|
+
args.reserveBalance ?? 0,
|
|
51
|
+
args.permit ?? '0x',
|
|
47
52
|
]);
|
|
48
53
|
return {
|
|
49
54
|
to: this.config.pod.contractAddress,
|
|
@@ -53,24 +58,23 @@ class PodToSourceChainActionClient {
|
|
|
53
58
|
};
|
|
54
59
|
}
|
|
55
60
|
/**
|
|
56
|
-
* Create unsigned transaction for claiming
|
|
61
|
+
* Create unsigned transaction for claiming tokens on ETH (source chain)
|
|
57
62
|
*
|
|
58
|
-
* This is called after depositing on Pod and
|
|
59
|
-
*
|
|
63
|
+
* This is called after depositing on Pod and obtaining the claim proof
|
|
64
|
+
* via pod_getBridgeClaimProof RPC (or the getBridgeClaimProof helper).
|
|
60
65
|
*
|
|
61
|
-
* @param args.claimData Claim proof data with
|
|
66
|
+
* @param args.claimData Claim proof data with proof + auxTxSuffix
|
|
62
67
|
* @param args.from Optional sender address
|
|
63
68
|
* @returns Unsigned transaction template with encoded claim call
|
|
64
69
|
*/
|
|
65
70
|
claim(args) {
|
|
66
|
-
const { ethTokenAddress, amount, to,
|
|
67
|
-
const data = this.
|
|
71
|
+
const { ethTokenAddress, amount, to, proof, auxTxSuffix } = args.claimData;
|
|
72
|
+
const data = this.sourceChainIface.encodeFunctionData('claim', [
|
|
68
73
|
ethTokenAddress,
|
|
69
74
|
amount,
|
|
70
75
|
to,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
depositTxHash
|
|
76
|
+
proof,
|
|
77
|
+
auxTxSuffix
|
|
74
78
|
]);
|
|
75
79
|
return {
|
|
76
80
|
to: this.config.sourceChain.contractAddress,
|