@tapforce/pod-bridge-sdk 2.0.1 → 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
@@ -17,7 +17,7 @@ Pod -> ETH: Deposit on Pod, claim on ETH with proof from pod_getBridgeClaimProof
17
17
 
18
18
  ## Features
19
19
 
20
- - **Bridge Deposits**: Deposit ERC20 or native tokens to the bridge
20
+ - **Bridge Deposits**: Deposit ERC20 or native tokens to the bridge (with optional CLOB integration)
21
21
  - **Claim with Proof**: Claim on ETH using `pod_getBridgeClaimProof` RPC
22
22
  - **Track Bridge Requests**: Query deposits sent/received by any address
23
23
  - **Claim Status Tracking**: Monitor claim status and finality
@@ -92,7 +92,7 @@ Deposits on ETH are automatically claimed on Pod after block finalization.
92
92
  ```typescript
93
93
  const client = new SourceChainToPodActionClient(actionConfig);
94
94
 
95
- // Create deposit transaction (with optional permit for gasless approval)
95
+ // Simple deposit (bridge only)
96
96
  const depositTx = client.deposit({
97
97
  token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on ETH
98
98
  amount: ethers.parseUnits('1', 6), // 1 USDC (use ETH-side decimals)
@@ -101,6 +101,16 @@ const depositTx = client.deposit({
101
101
  // permit: '0x...' // Optional: ERC20 permit bytes
102
102
  });
103
103
 
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
+
104
114
  // Don't forget to approve the bridge contract first for ERC20 tokens
105
115
  const tx = await signer.sendTransaction(depositTx);
106
116
  const receipt = await tx.wait();
@@ -192,8 +202,10 @@ deposit(args: {
192
202
  token: string;
193
203
  amount: string | bigint;
194
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)
195
207
  from?: string;
196
- permit?: string; // Optional ERC20 permit bytes (default: '0x')
208
+ permit?: string; // Optional ERC20 permit bytes (default: '0x')
197
209
  }): UnsignedTransaction
198
210
  ```
199
211
 
@@ -204,9 +216,12 @@ For Pod -> ETH deposits and claims.
204
216
  ```typescript
205
217
  // Deposit tokens on Pod
206
218
  deposit(args: {
207
- token: string; // Use 0xEeee...EEeE for native token
208
- amount: string | bigint; // Use ETH-side decimals
219
+ token: string; // Use 0xEeee...EEeE for native token
220
+ amount: string | bigint; // Use ETH-side decimals
209
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')
210
225
  from?: string;
211
226
  }): UnsignedTransaction
212
227
 
@@ -267,6 +282,8 @@ interface BridgeRequest {
267
282
  destination: string;
268
283
  token: string;
269
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)
270
287
  chainId: number;
271
288
  blockNumber: number;
272
289
  timestamp: number; // Unix timestamp (seconds)
@@ -288,23 +305,23 @@ interface BridgeRequest {
288
305
 
289
306
  ## Events
290
307
 
291
- The bridge contracts emit different events per chain:
308
+ The bridge contracts emit different events per chain (id type differs):
292
309
 
293
310
  ```solidity
294
311
  // ETH (Source Chain)
295
- event Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount);
312
+ event Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance);
296
313
  event Claim(bytes32 indexed txHash, address token, address mirrorToken, uint256 amount, address indexed to);
297
314
 
298
315
  // Pod
299
- event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount);
316
+ event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance);
300
317
  ```
301
318
 
302
319
  ## ABIs
303
320
 
304
321
  The SDK exports separate ABIs for each chain:
305
322
 
306
- - `SOURCE_CHAIN_BRIDGE_ABI` - ETH bridge (deposit with permit, claim with proof)
307
- - `POD_BRIDGE_ABI` - Pod bridge (3-param deposit)
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)
308
325
  - `BRIDGE_ABI` - Alias for `SOURCE_CHAIN_BRIDGE_ABI`
309
326
 
310
327
  ## Pod-specific Notes
@@ -30,6 +30,9 @@ export declare class PodToSourceChainActionClient {
30
30
  * @param args.token Token address on Pod to deposit (use 0xEeee...EEeE for native)
31
31
  * @param args.amount Amount to deposit (in ETH-side decimals)
32
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')
33
36
  * @param args.from Optional sender address
34
37
  * @returns Unsigned transaction template with encoded deposit call
35
38
  */
@@ -37,6 +40,9 @@ export declare class PodToSourceChainActionClient {
37
40
  token: string;
38
41
  amount: string | bigint;
39
42
  destinationWalletAddress: string;
43
+ callContract?: string;
44
+ reserveBalance?: string | bigint;
45
+ permit?: string;
40
46
  from?: string;
41
47
  }): UnsignedTransaction;
42
48
  /**
@@ -35,6 +35,9 @@ class PodToSourceChainActionClient {
35
35
  * @param args.token Token address on Pod to deposit (use 0xEeee...EEeE for native)
36
36
  * @param args.amount Amount to deposit (in ETH-side decimals)
37
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')
38
41
  * @param args.from Optional sender address
39
42
  * @returns Unsigned transaction template with encoded deposit call
40
43
  */
@@ -42,7 +45,10 @@ class PodToSourceChainActionClient {
42
45
  const data = this.podIface.encodeFunctionData('deposit', [
43
46
  args.token,
44
47
  args.amount,
45
- args.destinationWalletAddress
48
+ args.destinationWalletAddress,
49
+ args.callContract ?? ethers_1.ZeroAddress,
50
+ args.reserveBalance ?? 0,
51
+ args.permit ?? '0x',
46
52
  ]);
47
53
  return {
48
54
  to: this.config.pod.contractAddress,
@@ -24,6 +24,10 @@ export declare class SourceChainToPodActionClient {
24
24
  * @param args.token Token address on source chain (ETH) to deposit
25
25
  * @param args.amount Amount to deposit (in token's smallest unit)
26
26
  * @param args.destinationWalletAddress Recipient address on Pod
27
+ * @param args.callContract Optional: contract to call on Pod after deposit (default: address(0) for simple bridge)
28
+ * @param args.reserveBalance Optional: amount to keep as reserve when using callContract (default: 0).
29
+ * When callContract is address(0), reserveBalance MUST be 0.
30
+ * When callContract is set, amount must exceed reserveBalance; contract must be whitelisted.
27
31
  * @param args.permit Optional permit bytes for gasless approval (default '0x')
28
32
  * @param args.from Optional sender address
29
33
  * @returns Unsigned transaction template with encoded deposit call
@@ -32,6 +36,8 @@ export declare class SourceChainToPodActionClient {
32
36
  token: string;
33
37
  amount: string | bigint;
34
38
  destinationWalletAddress: string;
39
+ callContract?: string;
40
+ reserveBalance?: string | bigint;
35
41
  permit?: string;
36
42
  from?: string;
37
43
  }): UnsignedTransaction;
@@ -29,6 +29,10 @@ class SourceChainToPodActionClient {
29
29
  * @param args.token Token address on source chain (ETH) to deposit
30
30
  * @param args.amount Amount to deposit (in token's smallest unit)
31
31
  * @param args.destinationWalletAddress Recipient address on Pod
32
+ * @param args.callContract Optional: contract to call on Pod after deposit (default: address(0) for simple bridge)
33
+ * @param args.reserveBalance Optional: amount to keep as reserve when using callContract (default: 0).
34
+ * When callContract is address(0), reserveBalance MUST be 0.
35
+ * When callContract is set, amount must exceed reserveBalance; contract must be whitelisted.
32
36
  * @param args.permit Optional permit bytes for gasless approval (default '0x')
33
37
  * @param args.from Optional sender address
34
38
  * @returns Unsigned transaction template with encoded deposit call
@@ -38,6 +42,8 @@ class SourceChainToPodActionClient {
38
42
  args.token,
39
43
  args.amount,
40
44
  args.destinationWalletAddress,
45
+ args.callContract ?? ethers_1.ZeroAddress,
46
+ args.reserveBalance ?? 0,
41
47
  args.permit ?? '0x',
42
48
  ]);
43
49
  return {
@@ -12,6 +12,8 @@ export declare class PodTrackerService {
12
12
  private readonly config;
13
13
  private readonly provider;
14
14
  private readonly iface;
15
+ /** Interface with uint256 id variant — Pod system contract may emit either bytes32 or uint256 id */
16
+ private readonly ifaceUint256Id;
15
17
  private chainId;
16
18
  constructor(config: PodBridgeChainConfig);
17
19
  private initChainId;
@@ -27,6 +29,9 @@ export declare class PodTrackerService {
27
29
  /**
28
30
  * Fetch deposits from POD using raw eth_getLogs RPC.
29
31
  * Pod returns logs with blockNumber: null, so we can't use ethers queryFilter.
32
+ *
33
+ * Queries both bytes32 and uint256 id event topics since Pod system contract
34
+ * may emit either variant.
30
35
  */
31
36
  private getDeposits;
32
37
  }
@@ -19,6 +19,7 @@ class PodTrackerService {
19
19
  this.chainId = null;
20
20
  this.provider = config.provider;
21
21
  this.iface = new ethers_1.Interface(bridge_abi_1.POD_BRIDGE_ABI);
22
+ this.ifaceUint256Id = new ethers_1.Interface(bridge_abi_1.SOURCE_CHAIN_BRIDGE_ABI);
22
23
  this.initChainId();
23
24
  }
24
25
  async initChainId() {
@@ -54,13 +55,20 @@ class PodTrackerService {
54
55
  /**
55
56
  * Fetch deposits from POD using raw eth_getLogs RPC.
56
57
  * Pod returns logs with blockNumber: null, so we can't use ethers queryFilter.
58
+ *
59
+ * Queries both bytes32 and uint256 id event topics since Pod system contract
60
+ * may emit either variant.
57
61
  */
58
62
  async getDeposits(topicFilters) {
59
63
  const deposits = [];
60
- // Deposit event topic
61
- const depositEventTopic = this.iface.getEvent('Deposit').topicHash;
62
- // Build topics array: [eventSig, ...filters]
63
- const topics = [depositEventTopic, ...topicFilters];
64
+ // Pod may emit Deposit with bytes32 id or uint256 id — query both topics
65
+ const bytes32IdTopic = this.iface.getEvent('Deposit').topicHash;
66
+ const uint256IdTopic = this.ifaceUint256Id.getEvent('Deposit').topicHash;
67
+ // Use array in first topic position for OR matching (standard EVM eth_getLogs)
68
+ const topics = [
69
+ [bytes32IdTopic, uint256IdTopic],
70
+ ...topicFilters,
71
+ ];
64
72
  // Use raw RPC to avoid ethers.js blockNumber validation
65
73
  const rpcProvider = this.provider;
66
74
  const rawLogs = await rpcProvider.send('eth_getLogs', [{
@@ -76,7 +84,10 @@ class PodTrackerService {
76
84
  for (let i = 0; i < rawLogs.length; i++) {
77
85
  const log = rawLogs[i];
78
86
  const receipt = receipts[i];
79
- const parsed = this.iface.parseLog({
87
+ // Parse with the matching interface based on which topic was emitted
88
+ const isUint256Id = log.topics[0] === uint256IdTopic;
89
+ const parseIface = isUint256Id ? this.ifaceUint256Id : this.iface;
90
+ const parsed = parseIface.parseLog({
80
91
  topics: log.topics,
81
92
  data: log.data
82
93
  });
@@ -92,6 +103,8 @@ class PodTrackerService {
92
103
  destination: parsed.args.to,
93
104
  token: parsed.args.token,
94
105
  amount: parsed.args.amount.toString(),
106
+ callContract: parsed.args.callContract,
107
+ reserveBalance: parsed.args.reserveBalance.toString(),
95
108
  chainId: this.chainId,
96
109
  blockNumber: timestamp,
97
110
  timestamp,
@@ -56,7 +56,7 @@ class SourceChainTrackerService {
56
56
  const BLOCK_BATCH_SIZE = 10000;
57
57
  for (let start = startBlock; start <= currentBlock; start += BLOCK_BATCH_SIZE) {
58
58
  const end = Math.min(start + BLOCK_BATCH_SIZE - 1, currentBlock);
59
- // Event: Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount)
59
+ // Event: Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance)
60
60
  const depositFilter = this.bridge.filters.Deposit(...options.depositEventFilter);
61
61
  const depositLogs = await this.bridge.queryFilter(depositFilter, start, end);
62
62
  if (start + BLOCK_BATCH_SIZE <= currentBlock) {
@@ -80,6 +80,8 @@ class SourceChainTrackerService {
80
80
  destination: parsed.args.to,
81
81
  token: parsed.args.token,
82
82
  amount: parsed.args.amount.toString(),
83
+ callContract: parsed.args.callContract,
84
+ reserveBalance: parsed.args.reserveBalance.toString(),
83
85
  chainId: this.chainId,
84
86
  blockNumber: log.blockNumber,
85
87
  timestamp: block ? Number(block.timestamp) : 0
@@ -1,18 +1,17 @@
1
1
  /**
2
2
  * ABI for Bridge contract on Source Chain (ETH/Mainnet)
3
3
  *
4
- * ETH -> Pod: deposit on ETH (4-param with permit), auto-claim on Pod
4
+ * ETH -> Pod: deposit on ETH (6-param with callContract, reserveBalance, permit), auto-claim on Pod
5
5
  * Pod -> ETH: deposit on Pod, claim on ETH with proof from pod_getBridgeClaimProof RPC
6
6
  *
7
- * Note: Pod and ETH bridge contracts have DIFFERENT event signatures:
8
- * - ETH Deposit: uint256 indexed id (topic 0x71894900...)
9
- * - Pod Deposit: bytes32 indexed id (topic 0x980e6de4...)
7
+ * New deposit params (callContract, reserveBalance) enable depositing to CLOB orderbook in one TX.
8
+ * For simple bridge deposits: callContract=address(0), reserveBalance=0.
10
9
  */
11
10
  export declare const SOURCE_CHAIN_BRIDGE_ABI: string[];
12
11
  /**
13
12
  * ABI for Bridge system contract on Pod chain.
14
13
  * Pod uses bytes32 indexed id in Deposit events (different from ETH's uint256).
15
- * Pod deposit is 3-param (no permit).
14
+ * Same 6-param deposit as source chain.
16
15
  */
17
16
  export declare const POD_BRIDGE_ABI: string[];
18
17
  export declare const BRIDGE_ABI: string[];
@@ -4,19 +4,18 @@ exports.BRIDGE_ABI = exports.POD_BRIDGE_ABI = exports.SOURCE_CHAIN_BRIDGE_ABI =
4
4
  /**
5
5
  * ABI for Bridge contract on Source Chain (ETH/Mainnet)
6
6
  *
7
- * ETH -> Pod: deposit on ETH (4-param with permit), auto-claim on Pod
7
+ * ETH -> Pod: deposit on ETH (6-param with callContract, reserveBalance, permit), auto-claim on Pod
8
8
  * Pod -> ETH: deposit on Pod, claim on ETH with proof from pod_getBridgeClaimProof RPC
9
9
  *
10
- * Note: Pod and ETH bridge contracts have DIFFERENT event signatures:
11
- * - ETH Deposit: uint256 indexed id (topic 0x71894900...)
12
- * - Pod Deposit: bytes32 indexed id (topic 0x980e6de4...)
10
+ * New deposit params (callContract, reserveBalance) enable depositing to CLOB orderbook in one TX.
11
+ * For simple bridge deposits: callContract=address(0), reserveBalance=0.
13
12
  */
14
13
  exports.SOURCE_CHAIN_BRIDGE_ABI = [
15
14
  // Events
16
- "event Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount)",
15
+ "event Deposit(uint256 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance)",
17
16
  "event Claim(bytes32 indexed txHash, address token, address mirrorToken, uint256 amount, address indexed to)",
18
- // Deposit function (ERC20, with optional permit bytes)
19
- "function deposit(address token, uint256 amount, address to, bytes permit) returns (uint256)",
17
+ // Deposit function (6-param: token, amount, to, callContract, reserveBalance, permit)
18
+ "function deposit(address token, uint256 amount, address to, address callContract, uint256 reserveBalance, bytes permit) returns (uint256)",
20
19
  // Claim function with proof from pod_getBridgeClaimProof
21
20
  "function claim(address token, uint256 amount, address to, bytes proof, bytes auxTxSuffix)",
22
21
  // View functions
@@ -27,13 +26,13 @@ exports.SOURCE_CHAIN_BRIDGE_ABI = [
27
26
  /**
28
27
  * ABI for Bridge system contract on Pod chain.
29
28
  * Pod uses bytes32 indexed id in Deposit events (different from ETH's uint256).
30
- * Pod deposit is 3-param (no permit).
29
+ * Same 6-param deposit as source chain.
31
30
  */
32
31
  exports.POD_BRIDGE_ABI = [
33
32
  // Events
34
- "event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount)",
35
- // Deposit function (3-param, no permit)
36
- "function deposit(address token, uint256 amount, address to)",
33
+ "event Deposit(bytes32 indexed id, address indexed from, address indexed to, address token, uint256 amount, address callContract, uint256 reserveBalance)",
34
+ // Deposit function (6-param, same as source chain)
35
+ "function deposit(address token, uint256 amount, address to, address callContract, uint256 reserveBalance, bytes permit) returns (uint256)",
37
36
  ];
38
37
  // Backward compatibility alias
39
38
  exports.BRIDGE_ABI = exports.SOURCE_CHAIN_BRIDGE_ABI;
@@ -34,6 +34,8 @@ export interface BridgeRequest {
34
34
  destination: string;
35
35
  token: string;
36
36
  amount: string;
37
+ callContract?: string;
38
+ reserveBalance?: string;
37
39
  chainId: number;
38
40
  blockNumber: number;
39
41
  timestamp: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapforce/pod-bridge-sdk",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "SDK for interacting with Bridges between pod and other chains",
5
5
  "keywords": [
6
6
  "pod",