@@ -910,6 +910,227 @@ parsed.result.forEach(row => {
910910});
911911```
912912
913+ ## Bridge Operations
914+
915+ The SDK provides methods for interacting with bridge instances on TN, enabling token transfers between TN and supported blockchain networks.
916+
917+ ### Understanding Bridge Identifiers
918+
919+ Bridge instances on TN are identified by specific names that may differ from network names. For example:
920+ - Network ` "sepolia" ` → Bridge identifier ` "sepolia" ` (matches)
921+ - Network ` "hoodi" ` → Bridge identifier ` "hoodi_tt" ` (different due to multiple token support)
922+
923+ Always use the ** bridge identifier** when calling bridge methods, not the network name.
924+
925+ ### ` client.getWalletBalance(bridgeIdentifier: string, walletAddress: string): Promise<string> `
926+
927+ Gets the wallet balance for a specific bridge instance.
928+
929+ #### Parameters
930+ - ` bridgeIdentifier: string ` - Bridge instance identifier (e.g., ` "sepolia" ` , ` "hoodi_tt" ` , ` "ethereum" ` )
931+ - ` walletAddress: string ` - Ethereum address to check balance for
932+
933+ #### Returns
934+ - ` Promise<string> ` - Balance in wei as a string (to handle large numbers safely)
935+
936+ #### Example
937+ ``` typescript
938+ // Simple case - identifier matches network name
939+ const sepoliaBalance = await client .getWalletBalance (" sepolia" , " 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" );
940+ console .log (` Balance: ${sepoliaBalance } wei ` );
941+
942+ // Multi-token bridge - specify bridge instance explicitly
943+ const hoodiBalance = await client .getWalletBalance (" hoodi_tt" , " 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" );
944+
945+ // Convert wei to human-readable format
946+ import { formatEther } from ' ethers' ;
947+ const balanceInTokens = formatEther (hoodiBalance );
948+ console .log (` Balance: ${balanceInTokens } tokens ` );
949+ ```
950+
951+ ### ` client.withdraw(bridgeIdentifier: string, amount: string, recipient: string): Promise<string> `
952+
953+ Initiates a withdrawal by bridging tokens from TN to a destination chain. This is a convenience method that calls ` bridgeTokens ` and waits for transaction confirmation.
954+
955+ #### Parameters
956+ - ` bridgeIdentifier: string ` - Bridge instance identifier (e.g., ` "sepolia" ` , ` "hoodi_tt" ` )
957+ - ` amount: string ` - Amount to withdraw in wei (as string to preserve precision)
958+ - ` recipient: string ` - Recipient address on the destination chain
959+
960+ #### Returns
961+ - ` Promise<string> ` - Transaction hash of the withdrawal
962+
963+ #### Example
964+ ``` typescript
965+ import { parseEther } from ' ethers' ;
966+
967+ // Withdraw 100 tokens to Sepolia
968+ const amount = parseEther (" 100" ); // Convert to wei
969+ const txHash = await client .withdraw (" sepolia" , amount .toString (), " 0x742d35Cc..." );
970+
971+ console .log (` Withdrawal initiated: ${txHash } ` );
972+
973+ // For non-custodial bridges (like Hoodi), you must claim the withdrawal manually
974+ // See getWithdrawalProof() for claiming process
975+ ```
976+
977+ ** Important Notes:**
978+ - ** Non-custodial bridges** (Hoodi): You must manually claim withdrawals using ` getWithdrawalProof() `
979+ - ** Wait time** : Withdrawals become claimable after the epoch period (typically 10 minutes)
980+
981+ ### ` client.getWithdrawalProof(bridgeIdentifier: string, walletAddress: string): Promise<WithdrawalProof[]> `
982+
983+ Gets withdrawal proofs for claiming withdrawals on non-custodial bridges. Returns merkle proofs and validator signatures needed for submitting claims to the destination chain contract.
984+
985+ #### Parameters
986+ - ` bridgeIdentifier: string ` - Bridge instance identifier (e.g., ` "hoodi_tt" ` )
987+ - ` walletAddress: string ` - Wallet address to get withdrawal proofs for
988+
989+ #### Returns
990+ - ` Promise<WithdrawalProof[]> ` - Array of withdrawal proofs (empty array if no unclaimed withdrawals)
991+
992+ #### WithdrawalProof Type
993+ ``` typescript
994+ interface WithdrawalProof {
995+ chain: string ; // Source chain name (e.g., "hoodi")
996+ chain_id: string ; // Numeric chain ID (e.g., "560048")
997+ contract: string ; // Bridge contract address on destination chain
998+ created_at: number ; // Block number when withdrawal was created
999+ recipient: string ; // Recipient wallet address
1000+ amount: string ; // Withdrawal amount in wei
1001+ block_hash: string ; // Kwil block hash (base64-encoded)
1002+ root: string ; // Merkle root (base64-encoded)
1003+ proofs: string []; // Merkle proofs (base64-encoded, usually empty)
1004+ signatures: string []; // Validator signatures (base64-encoded, 65 bytes each)
1005+ }
1006+ ```
1007+
1008+ #### Example - Check for Claimable Withdrawals
1009+ ``` typescript
1010+ // Check for claimable withdrawals
1011+ const proofs = await client .getWithdrawalProof (" hoodi_tt" , " 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" );
1012+
1013+ if (proofs .length === 0 ) {
1014+ console .log (" No withdrawals ready to claim" );
1015+ } else {
1016+ console .log (` ${proofs .length } withdrawal(s) ready to claim ` );
1017+
1018+ for (const proof of proofs ) {
1019+ console .log (` Amount: ${proof .amount } wei ` );
1020+ console .log (` Recipient: ${proof .recipient } ` );
1021+ console .log (` Contract: ${proof .contract } ` );
1022+ }
1023+ }
1024+ ```
1025+
1026+ #### Example - Claim Withdrawal On-Chain
1027+ ``` typescript
1028+ import { Contract , ethers } from ' ethers' ;
1029+
1030+ // 1. Get withdrawal proof from TN
1031+ const proofs = await client .getWithdrawalProof (" hoodi_tt" , walletAddress );
1032+ if (proofs .length === 0 ) {
1033+ throw new Error (" No withdrawals to claim" );
1034+ }
1035+
1036+ const proof = proofs [0 ];
1037+
1038+ // 2. Decode base64 data for smart contract call
1039+ const blockHash = Buffer .from (proof .block_hash , ' base64' );
1040+ const root = Buffer .from (proof .root , ' base64' );
1041+ const merkleProofs = proof .proofs .map (p => Buffer .from (p , ' base64' ));
1042+
1043+ // 3. Split signatures into v, r, s components
1044+ const signatures = proof .signatures .map (sig => {
1045+ const sigBytes = Buffer .from (sig , ' base64' );
1046+ return {
1047+ v: sigBytes [64 ],
1048+ r: ' 0x' + sigBytes .slice (0 , 32 ).toString (' hex' ),
1049+ s: ' 0x' + sigBytes .slice (32 , 64 ).toString (' hex' )
1050+ };
1051+ });
1052+
1053+ // 4. Call bridge contract to claim withdrawal
1054+ const bridgeContract = new Contract (proof .contract , BRIDGE_ABI , signer );
1055+
1056+ const tx = await bridgeContract .claimWithdrawal (
1057+ proof .recipient ,
1058+ proof .amount ,
1059+ ' 0x' + blockHash .toString (' hex' ),
1060+ ' 0x' + root .toString (' hex' ),
1061+ merkleProofs .map (p => ' 0x' + p .toString (' hex' )),
1062+ signatures .map (s => ({ v: s .v , r: s .r , s: s .s }))
1063+ );
1064+
1065+ await tx .wait ();
1066+ console .log (` Withdrawal claimed! Tx: ${tx .hash } ` );
1067+ ```
1068+
1069+ ### ` action.listWalletRewards(bridgeIdentifier: string, wallet: string, withPending: boolean): Promise<any[]> `
1070+
1071+ Lists wallet rewards for a specific bridge instance. This is a low-level method that directly accesses the bridge extension namespace.
1072+
1073+ ** ⚠️ Deprecated** : Most users should use ` getWithdrawalProof() ` instead, which provides a higher-level interface.
1074+
1075+ #### Parameters
1076+ - ` bridgeIdentifier: string ` - Bridge instance identifier
1077+ - ` wallet: string ` - Wallet address to query
1078+ - ` withPending: boolean ` - Whether to include pending (not yet finalized) rewards
1079+
1080+ #### Returns
1081+ - ` Promise<any[]> ` - Array of reward records
1082+
1083+ #### Example
1084+ ``` typescript
1085+ const action = client .loadAction ();
1086+ const rewards = await action .listWalletRewards (" hoodi_tt" , walletAddress , true );
1087+ console .log (` Found ${rewards .length } reward(s) ` );
1088+ ```
1089+
1090+ ### Bridge Configuration Best Practices
1091+
1092+ When integrating bridge functionality in your application:
1093+
1094+ 1 . ** Use bridge identifiers directly** :
1095+ ``` typescript
1096+ // Always use the exact bridge identifier
1097+ const balance = await client .getWalletBalance (' hoodi_tt' , address );
1098+ const sepoliaBalance = await client .getWalletBalance (' sepolia' , address );
1099+
1100+ // For multiple Hoodi bridges
1101+ const tt2Balance = await client .getWalletBalance (' hoodi_tt2' , address );
1102+ ```
1103+
1104+ 2 . ** Handle custodial vs non-custodial bridges differently** :
1105+ ``` typescript
1106+ const isCustodial = {
1107+ ethereum: true , // Auto-claimed
1108+ sepolia: true , // Auto-claimed
1109+ hoodi_tt: false , // Manual claim required
1110+ };
1111+
1112+ if (isCustodial [bridgeId ]) {
1113+ console .log (" Withdrawal will be automatically claimed" );
1114+ } else {
1115+ console .log (" You must claim withdrawal manually using getWithdrawalProof()" );
1116+ }
1117+ ```
1118+
1119+ 3 . ** Poll for withdrawal proofs** on non-custodial bridges:
1120+ ``` typescript
1121+ async function waitForClaimableWithdrawal(bridgeId : string , address : string , maxAttempts = 60 ) {
1122+ for (let i = 0 ; i < maxAttempts ; i ++ ) {
1123+ const proofs = await client .getWithdrawalProof (bridgeId , address );
1124+ if (proofs .length > 0 ) {
1125+ return proofs [0 ];
1126+ }
1127+ // Wait 10 seconds before checking again
1128+ await new Promise (resolve => setTimeout (resolve , 10000 ));
1129+ }
1130+ throw new Error (" Withdrawal not ready after 10 minutes" );
1131+ }
1132+ ```
1133+
9131134## Performance Recommendations
9141135- Use batch record insertions
9151136- Implement client-side caching
0 commit comments