@tapforce/pod-bridge-sdk 1.2.3 → 1.2.4

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.
Files changed (2) hide show
  1. package/README.md +71 -19
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -110,6 +110,11 @@ const receipt = await tx.wait();
110
110
  Deposits on Pod require manual claim on ETH with aggregated validator signatures.
111
111
 
112
112
  ```typescript
113
+ import {
114
+ PodToSourceChainActionClient,
115
+ extractAggregatedSignaturesWithValidators
116
+ } from '@tapforce/pod-bridge-sdk';
117
+
113
118
  const client = new PodToSourceChainActionClient(actionConfig);
114
119
 
115
120
  // Step 1: Deposit on Pod
@@ -123,17 +128,26 @@ const depositTx = client.deposit({
123
128
  const tx = await podSigner.sendTransaction(depositTx);
124
129
  const receipt = await tx.wait();
125
130
 
126
- // Step 2: Get aggregated signatures from your indexer/API
127
- // (Implementation depends on your backend)
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', {});
128
136
 
129
- // Step 3: Claim on ETH
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
+ );
142
+
143
+ // Step 5: Claim on ETH
130
144
  const claimTx = client.claim({
131
145
  claimData: {
132
146
  ethTokenAddress: '0xETH_TOKEN_ADDRESS', // Token on ETH (different from Pod)
133
147
  amount: ethers.parseEther('1'),
134
148
  to: '0x...', // Recipient
135
149
  committeeEpoch: 0, // Hardcoded for now
136
- aggregatedSignatures: '0x...', // 65-byte signatures (r,s,v) concatenated
150
+ aggregatedSignatures, // 65-byte signatures (r,s,v) concatenated
137
151
  depositTxHash: tx.hash // Deposit TX hash from Pod
138
152
  },
139
153
  from: '0x...'
@@ -269,30 +283,68 @@ interface ClaimProofData {
269
283
 
270
284
  ### Signature Recovery Helpers
271
285
 
272
- Pod uses 64-byte signatures without the parity bit (v), but the ETH contract requires 65-byte (r,s,v) format.
273
- The helpers exactly match the Rust implementation - they try both recovery IDs and verify against the public key.
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.
274
288
 
275
289
  ```typescript
276
290
  import {
291
+ extractAggregatedSignaturesWithValidators,
277
292
  extractAggregatedSignatures,
278
- recoverSignature65B,
279
- addressFromPublicKey
293
+ parseDerSignature,
294
+ addressFromPublicKey,
295
+ recoverSignatureWithoutPubkey
280
296
  } from '@tapforce/pod-bridge-sdk';
281
297
 
282
- // Recommended: Extract signatures directly from Pod receipt
298
+ // Recommended: Extract with validator verification (correct v-values)
283
299
  const podReceipt = await podProvider.send('eth_getTransactionReceipt', [depositTxHash]);
284
- const aggregatedSignatures = extractAggregatedSignatures(podReceipt, msgHash);
285
-
286
- // Low-level: Recover single signature (matches Rust recover_65b_signature)
287
- const sig65 = recoverSignature65B(
288
- r, // r component (32 bytes hex)
289
- s, // s component (32 bytes hex)
290
- msgHash, // message hash that was signed
291
- publicKey // signer's public key (to verify recovery)
300
+ const committee = await podProvider.send('pod_getCommittee', {});
301
+ const aggregatedSignatures = extractAggregatedSignaturesWithValidators(
302
+ podReceipt,
303
+ committee.validators // [[index, compressedPubKey], ...]
292
304
  );
293
305
 
294
- // Utility: Derive address from public key
295
- const address = addressFromPublicKey(publicKey);
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);
314
+
315
+ // Utility: Derive address from uncompressed public key
316
+ const address = addressFromPublicKey(uncompressedPubKey);
317
+ ```
318
+
319
+ #### Pod Receipt Format
320
+
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
+ }
334
+ ```
335
+
336
+ #### Committee Format
337
+
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
+ }
296
348
  ```
297
349
 
298
350
  ## Events
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapforce/pod-bridge-sdk",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "SDK for interacting with Bridges between pod and other chains",
5
5
  "keywords": [
6
6
  "pod",