@tapforce/pod-bridge-sdk 1.0.1

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