@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 CHANGED
@@ -1,26 +1,27 @@
1
1
  # Pod Bridge SDK
2
2
 
3
- TypeScript SDK for bridging ERC20 tokens between POD and EVM chains (e.g., Sepolia/ETH).
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 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
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
- - 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
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 ERC20 Deposits**: Deposit tokens to the bridge
20
- - **Track Bridge Requests**: Query deposits sent/received by any address
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
- PodBridgeConfig,
43
- BRIDGE_ABI
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...' // Bridge contract on ETH/Sepolia
58
+ contractAddress: '0x...' // BridgeDepositWithdraw on ETH
57
59
  },
58
60
  pod: {
59
- contractAddress: '0x...' // Bridge contract on Pod
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://sepolia.infura.io/v3/YOUR_KEY'),
74
+ provider: new ethers.JsonRpcProvider('https://eth-mainnet.rpc.example'),
73
75
  contractAddress: '0x...',
74
- deploymentBlock: 9502541 // Optional: avoids indexing empty blocks
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: '0x...',
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 Pod (Auto-claim)
88
+ ### ETH -> Pod (Auto-claim)
87
89
 
88
- Deposits on ETH are automatically claimed on Pod after block finalization (~15 min on Sepolia).
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
- // Create deposit transaction
95
+ // Simple deposit (bridge only)
94
96
  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
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
- // Send transaction
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 ETH (Manual claim with signatures)
121
+ ### Pod -> ETH (Claim with proof)
109
122
 
110
- Deposits on Pod require manual claim on ETH with aggregated validator signatures.
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
- extractAggregatedSignaturesWithValidators
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: '0xPOD_TOKEN_ADDRESS', // ERC20 token on Pod
123
- amount: ethers.parseEther('1'),
124
- destinationWalletAddress: '0x...', // Recipient on ETH
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
- const tx = await podSigner.sendTransaction(depositTx);
129
- const receipt = await tx.wait();
130
-
131
- // Step 2: Get the receipt with validator signatures
132
- const podReceipt = await podProvider.send('eth_getTransactionReceipt', [tx.hash]);
133
-
134
- // Step 3: Get committee for v-value recovery
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 4: Extract aggregated signatures (65-byte format with correct v-values)
138
- const aggregatedSignatures = extractAggregatedSignaturesWithValidators(
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 5: Claim on ETH
144
- const claimTx = client.claim({
145
- claimData: {
146
- ethTokenAddress: '0xETH_TOKEN_ADDRESS', // Token on ETH (different from Pod)
147
- amount: ethers.parseEther('1'),
148
- to: '0x...', // Recipient
149
- committeeEpoch: 0, // Hardcoded for now
150
- aggregatedSignatures, // 65-byte signatures (r,s,v) concatenated
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 Pod' : 'Pod 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('Claimed:', deposit.isClaimed);
175
- console.log('Claimable:', deposit.isClaimable);
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 (Pod → ETH only)
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 Pod deposits (auto-claim on Pod).
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 ETH deposits and claims.
214
+ For Pod -> ETH deposits and claims.
210
215
 
211
216
  ```typescript
212
- // Deposit ERC20 tokens on Pod
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 aggregated signatures
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
- ### ClaimProofData
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
- // Utility: Derive address from uncompressed public key
316
- const address = addressFromPublicKey(uncompressedPubKey);
317
- ```
308
+ The bridge contracts emit different events per chain (id type differs):
318
309
 
319
- #### Pod Receipt Format
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
- ```typescript
322
- interface PodTransactionReceipt {
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
- #### Committee Format
319
+ ## ABIs
337
320
 
338
- ```typescript
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
- ## Events
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
- The bridge contract emits:
327
+ ## Pod-specific Notes
353
328
 
354
- ```solidity
355
- event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount);
356
- event Claim(bytes32 indexed id, address indexed to, address token, uint256 amount);
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
- * New architecture:
6
- * - User deposits ERC20 tokens on Pod
7
- * - Wait for TX receipt from Pod
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 (user deposits AAAA on Pod, claims BBBB on ETH)
13
- * - committee_epoch is hardcoded to 0 for now
14
- * - aggregated_signatures is concatenation of 65-byte ECDSA signatures (r,s,v)
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 iface;
16
+ private readonly podIface;
17
+ private readonly sourceChainIface;
20
18
  constructor(config: PodBridgeActionsClientConfig);
21
19
  /**
22
- * Create unsigned transaction for depositing ERC20 tokens from Pod to ETH
20
+ * Create unsigned transaction for depositing tokens from Pod to ETH
23
21
  *
24
22
  * After deposit:
25
- * - Get the TX receipt from Pod
26
- * - Call claim() on ETH with aggregated validator signatures
23
+ * - Call pod_getBridgeClaimProof(depositTxHash) on Pod RPC
24
+ * - Call claim() on ETH with the proof
27
25
  *
28
- * Note: Native tokens are not supported. Only ERC20.
29
- * Note: Token addresses differ between Pod and ETH chains.
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 wei)
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 ERC20 tokens on ETH (source chain)
49
+ * Create unsigned transaction for claiming tokens on ETH (source chain)
45
50
  *
46
- * This is called after depositing on Pod and getting the TX receipt.
47
- * The aggregated_signatures must be recovered to 65-byte format (r,s,v).
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 aggregated signatures
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
- * New architecture:
10
- * - User deposits ERC20 tokens on Pod
11
- * - Wait for TX receipt from Pod
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 (user deposits AAAA on Pod, claims BBBB on ETH)
17
- * - committee_epoch is hardcoded to 0 for now
18
- * - aggregated_signatures is concatenation of 65-byte ECDSA signatures (r,s,v)
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.iface = new ethers_1.Interface(bridge_abi_1.BRIDGE_ABI);
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 ERC20 tokens from Pod to ETH
25
+ * Create unsigned transaction for depositing tokens from Pod to ETH
28
26
  *
29
27
  * After deposit:
30
- * - Get the TX receipt from Pod
31
- * - Call claim() on ETH with aggregated validator signatures
28
+ * - Call pod_getBridgeClaimProof(depositTxHash) on Pod RPC
29
+ * - Call claim() on ETH with the proof
32
30
  *
33
- * Note: Native tokens are not supported. Only ERC20.
34
- * Note: Token addresses differ between Pod and ETH chains.
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 wei)
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.iface.encodeFunctionData('deposit', [
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 ERC20 tokens on ETH (source chain)
61
+ * Create unsigned transaction for claiming tokens on ETH (source chain)
57
62
  *
58
- * This is called after depositing on Pod and getting the TX receipt.
59
- * The aggregated_signatures must be recovered to 65-byte format (r,s,v).
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 aggregated signatures
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, committeeEpoch, aggregatedSignatures, depositTxHash } = args.claimData;
67
- const data = this.iface.encodeFunctionData('claim', [
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
- committeeEpoch,
72
- aggregatedSignatures,
73
- depositTxHash
76
+ proof,
77
+ auxTxSuffix
74
78
  ]);
75
79
  return {
76
80
  to: this.config.sourceChain.contractAddress,