@thru/thru-sdk 0.1.38 → 0.2.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.
@@ -533,12 +533,27 @@ declare class Block {
533
533
  });
534
534
  static fromProto(proto: Block$1): Block;
535
535
  static fromWire(data: Uint8Array): Block;
536
+ private static tryParseWireWithHeaderSize;
536
537
  toWire(): Uint8Array;
537
538
  getTransactions(): Transaction[];
538
539
  private static extractTransactionBody;
540
+ private static extractWithHeaderSize;
541
+ /**
542
+ * Checks if the data looks like a valid transaction body.
543
+ * Uses heuristics to detect if we've correctly offset into the block data.
544
+ * Wire format: header (112 bytes) + body + signature (64 bytes at end)
545
+ * This is a lenient heuristic for format detection - version/flag validation
546
+ * happens in Transaction.parseWire.
547
+ */
548
+ private static looksLikeValidTransactionBody;
539
549
  private serializeHeader;
540
550
  private serializeFooter;
541
551
  private static parseHeader;
552
+ /**
553
+ * Parses a block header with a specific expected size.
554
+ * Handles both current (168 bytes with weight_slot) and legacy (160 bytes without weight_slot) formats.
555
+ */
556
+ private static parseHeaderWithSize;
542
557
  private static parseFooter;
543
558
  private static parseTransactionsFromBody;
544
559
  }
@@ -296,9 +296,10 @@ var SIGNATURE_SIZE2 = 64;
296
296
  var PUBKEY_SIZE2 = 32;
297
297
  var HASH_SIZE = 32;
298
298
  var BLOCK_HEADER_SIZE = 168;
299
+ var BLOCK_HEADER_SIZE_LEGACY = 160;
299
300
  var BLOCK_FOOTER_SIZE = 104;
300
301
  var BLOCK_VERSION_V1 = 1;
301
- var TXN_HEADER_SIZE = 112;
302
+ var TXN_HEADER_BODY_SIZE = 112;
302
303
  var TXN_VERSION_V1 = 1;
303
304
  var TXN_MAX_ACCOUNTS = 1024;
304
305
  var STATE_PROOF_HEADER_SIZE = 40;
@@ -354,11 +355,11 @@ var Transaction = class _Transaction {
354
355
  return transaction;
355
356
  }
356
357
  static parseWire(data, options = {}) {
357
- if (data.length < TXN_HEADER_SIZE + SIGNATURE_SIZE2) {
358
- throw new Error(`Transaction data too short: ${data.length} bytes (expected at least ${TXN_HEADER_SIZE + SIGNATURE_SIZE2})`);
359
- }
360
358
  const strict = options.strict ?? false;
361
359
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
360
+ if (data.length < TXN_HEADER_BODY_SIZE) {
361
+ throw new Error(`Transaction data too short: ${data.length} bytes (minimum ${TXN_HEADER_BODY_SIZE})`);
362
+ }
362
363
  let offset = 0;
363
364
  const version = view.getUint8(offset);
364
365
  offset += 1;
@@ -400,8 +401,8 @@ var Transaction = class _Transaction {
400
401
  _Transaction.ensureAvailable(data.length, offset, PUBKEY_SIZE2, "program account");
401
402
  const program = data.slice(offset, offset + PUBKEY_SIZE2);
402
403
  offset += PUBKEY_SIZE2;
403
- if (offset !== TXN_HEADER_SIZE) {
404
- throw new Error(`Transaction header parsing mismatch (expected offset ${TXN_HEADER_SIZE}, got ${offset})`);
404
+ if (offset !== TXN_HEADER_BODY_SIZE) {
405
+ throw new Error(`Transaction header parsing mismatch (expected offset ${TXN_HEADER_BODY_SIZE}, got ${offset})`);
405
406
  }
406
407
  const totalAccountCount = Number(readwriteAccountsCount + readonlyAccountsCount);
407
408
  if (strict && totalAccountCount > TXN_MAX_ACCOUNTS) {
@@ -412,7 +413,7 @@ var Transaction = class _Transaction {
412
413
  const accountsSize = totalAccountCount * PUBKEY_SIZE2;
413
414
  let expectedBodySize = accountsSize + instructionDataSize;
414
415
  if ((flags & TXN_FLAG_HAS_FEE_PAYER_PROOF) !== 0) {
415
- const proofOffset = TXN_HEADER_SIZE + accountsSize + instructionDataSize;
416
+ const proofOffset = TXN_HEADER_BODY_SIZE + accountsSize + instructionDataSize;
416
417
  _Transaction.ensureAvailable(data.length, proofOffset, STATE_PROOF_HEADER_SIZE, "state proof header");
417
418
  const proofView = new DataView(data.buffer, data.byteOffset + proofOffset, STATE_PROOF_HEADER_SIZE);
418
419
  const typeSlot = proofView.getBigUint64(0, true);
@@ -426,9 +427,9 @@ var Transaction = class _Transaction {
426
427
  expectedBodySize += ACCOUNT_META_FOOTPRINT;
427
428
  }
428
429
  }
429
- const txnSize = TXN_HEADER_SIZE + expectedBodySize + SIGNATURE_SIZE2;
430
+ const txnSize = TXN_HEADER_BODY_SIZE + expectedBodySize + SIGNATURE_SIZE2;
430
431
  _Transaction.ensureAvailable(data.length, 0, txnSize, "full transaction");
431
- const sigStart = TXN_HEADER_SIZE + expectedBodySize;
432
+ const sigStart = TXN_HEADER_BODY_SIZE + expectedBodySize;
432
433
  const readWriteAccounts = [];
433
434
  for (let i = 0; i < readwriteAccountsCount; i++) {
434
435
  _Transaction.ensureAvailable(sigStart, offset, PUBKEY_SIZE2, "read-write accounts");
@@ -461,7 +462,7 @@ var Transaction = class _Transaction {
461
462
  }
462
463
  if (offset !== sigStart) {
463
464
  throw new Error(
464
- `Transaction body has trailing bytes: expected ${offset + SIGNATURE_SIZE2} bytes but found ${txnSize}`
465
+ `Transaction body has trailing bytes: expected ${sigStart} bytes consumed but found ${offset}`
465
466
  );
466
467
  }
467
468
  const signatureBytes = data.slice(sigStart, sigStart + SIGNATURE_SIZE2);
@@ -495,7 +496,7 @@ var Transaction = class _Transaction {
495
496
  if (hasSignature) {
496
497
  transaction.setSignature(Signature.from(signatureBytes));
497
498
  }
498
- return { transaction, size: offset + SIGNATURE_SIZE2 };
499
+ return { transaction, size: txnSize };
499
500
  }
500
501
  static fromProto(proto) {
501
502
  if (!proto.header) {
@@ -524,9 +525,9 @@ var Transaction = class _Transaction {
524
525
  header.flags ?? DEFAULT_FLAGS
525
526
  );
526
527
  } catch (sectionErr) {
527
- if (body.length >= TXN_HEADER_SIZE) {
528
+ if (body.length >= TXN_HEADER_BODY_SIZE) {
528
529
  parsed = this.parseBodySections(
529
- body.slice(TXN_HEADER_SIZE),
530
+ body.slice(TXN_HEADER_BODY_SIZE),
530
531
  header.readwriteAccountsCount ?? 0,
531
532
  header.readonlyAccountsCount ?? 0,
532
533
  header.instructionDataSize ?? 0,
@@ -621,7 +622,7 @@ var Transaction = class _Transaction {
621
622
  return this.buildWirePayload(new Uint8Array(header), true);
622
623
  }
623
624
  createHeader() {
624
- const buffer = new ArrayBuffer(TXN_HEADER_SIZE);
625
+ const buffer = new ArrayBuffer(TXN_HEADER_BODY_SIZE);
625
626
  const headerBytes = new Uint8Array(buffer);
626
627
  const view = new DataView(buffer);
627
628
  let offset = 0;
@@ -1558,28 +1559,54 @@ var Block = class _Block {
1558
1559
  return block;
1559
1560
  }
1560
1561
  static fromWire(data) {
1561
- if (data.length < BLOCK_HEADER_SIZE) {
1562
- throw new Error(`Block data too short: ${data.length} bytes (expected at least ${BLOCK_HEADER_SIZE})`);
1562
+ const result = _Block.tryParseWireWithHeaderSize(data, BLOCK_HEADER_SIZE);
1563
+ if (result) {
1564
+ return result;
1565
+ }
1566
+ const legacyResult = _Block.tryParseWireWithHeaderSize(data, BLOCK_HEADER_SIZE_LEGACY);
1567
+ if (legacyResult) {
1568
+ return legacyResult;
1569
+ }
1570
+ throw new Error(`Block data too short: ${data.length} bytes (expected at least ${BLOCK_HEADER_SIZE})`);
1571
+ }
1572
+ static tryParseWireWithHeaderSize(data, headerSize) {
1573
+ if (data.length < headerSize) {
1574
+ return null;
1575
+ }
1576
+ const headerBytes = data.slice(0, headerSize);
1577
+ let header;
1578
+ let blockTimeNs;
1579
+ try {
1580
+ const parsed = this.parseHeaderWithSize(headerBytes, headerSize);
1581
+ header = parsed.header;
1582
+ blockTimeNs = parsed.blockTimeNs;
1583
+ } catch {
1584
+ return null;
1563
1585
  }
1564
- const headerBytes = data.slice(0, BLOCK_HEADER_SIZE);
1565
- const { header, blockTimeNs } = this.parseHeader(headerBytes);
1566
1586
  if (header.version !== BLOCK_VERSION_V1) {
1567
- throw new Error(`Unsupported block version: ${header.version}`);
1587
+ return null;
1568
1588
  }
1569
1589
  let finalHeader = header;
1570
1590
  let footer;
1571
1591
  let footerInfo;
1572
1592
  let body;
1573
- if (data.length >= BLOCK_HEADER_SIZE + BLOCK_FOOTER_SIZE) {
1593
+ if (data.length >= headerSize + BLOCK_FOOTER_SIZE) {
1574
1594
  const footerOffset = data.length - BLOCK_FOOTER_SIZE;
1575
1595
  const footerBytes = data.slice(footerOffset);
1576
- const parsedFooter = this.parseFooter(footerBytes);
1577
- footer = parsedFooter.footer;
1578
- footerInfo = { blockHash: parsedFooter.blockHash, attestorPayment: parsedFooter.attestorPayment };
1579
- finalHeader = header.withBlockHash(parsedFooter.blockHash);
1580
- body = footerOffset > BLOCK_HEADER_SIZE ? data.slice(BLOCK_HEADER_SIZE, footerOffset) : void 0;
1596
+ try {
1597
+ const parsedFooter = this.parseFooter(footerBytes);
1598
+ footer = parsedFooter.footer;
1599
+ footerInfo = { blockHash: parsedFooter.blockHash, attestorPayment: parsedFooter.attestorPayment };
1600
+ finalHeader = header.withBlockHash(parsedFooter.blockHash);
1601
+ } catch {
1602
+ return null;
1603
+ }
1604
+ body = footerOffset > headerSize ? data.slice(headerSize, footerOffset) : void 0;
1581
1605
  } else {
1582
- body = data.length > BLOCK_HEADER_SIZE ? data.slice(BLOCK_HEADER_SIZE) : void 0;
1606
+ body = data.length > headerSize ? data.slice(headerSize) : void 0;
1607
+ }
1608
+ if (body && body.length > 0 && !_Block.looksLikeValidTransactionBody(body)) {
1609
+ return null;
1583
1610
  }
1584
1611
  const block = new _Block({ header: finalHeader, footer, body });
1585
1612
  block.blockTimeNs = blockTimeNs;
@@ -1606,17 +1633,56 @@ var Block = class _Block {
1606
1633
  return _Block.parseTransactionsFromBody(this.body);
1607
1634
  }
1608
1635
  static extractTransactionBody(raw, hasFooter) {
1609
- if (!raw || raw.length <= BLOCK_HEADER_SIZE) {
1610
- return raw && raw.length > BLOCK_HEADER_SIZE ? raw.slice(BLOCK_HEADER_SIZE) : void 0;
1636
+ if (!raw || raw.length === 0) {
1637
+ return void 0;
1638
+ }
1639
+ const bodyWithCurrentHeader = _Block.extractWithHeaderSize(raw, hasFooter, BLOCK_HEADER_SIZE);
1640
+ if (bodyWithCurrentHeader && bodyWithCurrentHeader.length > 0 && _Block.looksLikeValidTransactionBody(bodyWithCurrentHeader)) {
1641
+ return bodyWithCurrentHeader;
1611
1642
  }
1612
- const footerSize = hasFooter && raw.length >= BLOCK_HEADER_SIZE + BLOCK_FOOTER_SIZE ? BLOCK_FOOTER_SIZE : 0;
1613
- const start = BLOCK_HEADER_SIZE;
1643
+ const bodyWithLegacyHeader = _Block.extractWithHeaderSize(raw, hasFooter, BLOCK_HEADER_SIZE_LEGACY);
1644
+ if (bodyWithLegacyHeader && bodyWithLegacyHeader.length > 0 && _Block.looksLikeValidTransactionBody(bodyWithLegacyHeader)) {
1645
+ return bodyWithLegacyHeader;
1646
+ }
1647
+ return void 0;
1648
+ }
1649
+ static extractWithHeaderSize(raw, hasFooter, headerSize) {
1650
+ if (raw.length <= headerSize) {
1651
+ return void 0;
1652
+ }
1653
+ const footerSize = hasFooter && raw.length >= headerSize + BLOCK_FOOTER_SIZE ? BLOCK_FOOTER_SIZE : 0;
1654
+ const start = headerSize;
1614
1655
  const end = Math.max(start, raw.length - footerSize);
1615
1656
  if (end <= start) {
1616
1657
  return void 0;
1617
1658
  }
1618
1659
  return raw.slice(start, end);
1619
1660
  }
1661
+ /**
1662
+ * Checks if the data looks like a valid transaction body.
1663
+ * Uses heuristics to detect if we've correctly offset into the block data.
1664
+ * Wire format: header (112 bytes) + body + signature (64 bytes at end)
1665
+ * This is a lenient heuristic for format detection - version/flag validation
1666
+ * happens in Transaction.parseWire.
1667
+ */
1668
+ static looksLikeValidTransactionBody(data) {
1669
+ const MIN_TXN_SIZE = TXN_HEADER_BODY_SIZE + SIGNATURE_SIZE2;
1670
+ if (data.length < MIN_TXN_SIZE) {
1671
+ return false;
1672
+ }
1673
+ const readwriteCount = data[2] | data[3] << 8;
1674
+ const readonlyCount = data[4] | data[5] << 8;
1675
+ const totalAccounts = readwriteCount + readonlyCount;
1676
+ if (totalAccounts > 1024) {
1677
+ return false;
1678
+ }
1679
+ const instrDataSize = data[6] | data[7] << 8;
1680
+ const expectedMinSize = TXN_HEADER_BODY_SIZE + totalAccounts * PUBKEY_SIZE2 + instrDataSize + SIGNATURE_SIZE2;
1681
+ if (data.length < expectedMinSize) {
1682
+ return false;
1683
+ }
1684
+ return true;
1685
+ }
1620
1686
  serializeHeader() {
1621
1687
  const buffer = new ArrayBuffer(BLOCK_HEADER_SIZE);
1622
1688
  const bytes = new Uint8Array(buffer);
@@ -1721,6 +1787,66 @@ var Block = class _Block {
1721
1787
  });
1722
1788
  return { header, blockTimeNs };
1723
1789
  }
1790
+ /**
1791
+ * Parses a block header with a specific expected size.
1792
+ * Handles both current (168 bytes with weight_slot) and legacy (160 bytes without weight_slot) formats.
1793
+ */
1794
+ static parseHeaderWithSize(bytes, expectedSize) {
1795
+ if (bytes.length !== expectedSize) {
1796
+ throw new Error(`Invalid block header size: ${bytes.length}, expected ${expectedSize}`);
1797
+ }
1798
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1799
+ let offset = 0;
1800
+ const signature = bytes.slice(offset, offset + SIGNATURE_SIZE2);
1801
+ offset += SIGNATURE_SIZE2;
1802
+ const version = view.getUint8(offset);
1803
+ offset += 1;
1804
+ offset += 5;
1805
+ const chainId = view.getUint16(offset, true);
1806
+ offset += 2;
1807
+ const producer = bytes.slice(offset, offset + PUBKEY_SIZE2);
1808
+ offset += PUBKEY_SIZE2;
1809
+ const bondAmountLockUp = view.getBigUint64(offset, true);
1810
+ offset += 8;
1811
+ const expiryTimestampNs = view.getBigUint64(offset, true);
1812
+ offset += 8;
1813
+ const startSlot = view.getBigUint64(offset, true);
1814
+ offset += 8;
1815
+ const expiryAfter = view.getUint32(offset, true);
1816
+ offset += 4;
1817
+ const maxBlockSize = view.getUint32(offset, true);
1818
+ offset += 4;
1819
+ const maxComputeUnits = view.getBigUint64(offset, true);
1820
+ offset += 8;
1821
+ const maxStateUnits = view.getUint32(offset, true);
1822
+ offset += 4;
1823
+ offset += 4;
1824
+ let weightSlot;
1825
+ let blockTimeNs;
1826
+ if (expectedSize === BLOCK_HEADER_SIZE) {
1827
+ weightSlot = view.getBigUint64(offset, true);
1828
+ offset += 8;
1829
+ blockTimeNs = view.getBigUint64(offset, true);
1830
+ } else {
1831
+ blockTimeNs = view.getBigUint64(offset, true);
1832
+ }
1833
+ const header = new BlockHeader({
1834
+ slot: startSlot,
1835
+ version,
1836
+ headerSignature: signature,
1837
+ producer,
1838
+ expiryTimestamp: nanosecondsToTimestamp(expiryTimestampNs),
1839
+ startSlot,
1840
+ expiryAfter,
1841
+ maxBlockSize,
1842
+ maxComputeUnits,
1843
+ maxStateUnits,
1844
+ bondAmountLockUp,
1845
+ weightSlot,
1846
+ chainId
1847
+ });
1848
+ return { header, blockTimeNs };
1849
+ }
1724
1850
  static parseFooter(bytes) {
1725
1851
  if (bytes.length !== BLOCK_FOOTER_SIZE) {
1726
1852
  throw new Error(`Invalid block footer size: ${bytes.length}`);
@@ -1746,9 +1872,16 @@ var Block = class _Block {
1746
1872
  let offset = 0;
1747
1873
  while (offset < body.length) {
1748
1874
  const slice = body.subarray(offset);
1749
- const { transaction, size } = Transaction.parseWire(slice);
1750
- transactions.push(transaction);
1751
- offset += size;
1875
+ if (slice.length < 176) {
1876
+ break;
1877
+ }
1878
+ try {
1879
+ const { transaction, size } = Transaction.parseWire(slice);
1880
+ transactions.push(transaction);
1881
+ offset += size;
1882
+ } catch {
1883
+ break;
1884
+ }
1752
1885
  }
1753
1886
  return transactions;
1754
1887
  }
@@ -2932,5 +3065,5 @@ var VersionInfo = class _VersionInfo {
2932
3065
  };
2933
3066
 
2934
3067
  export { Account, Block, ChainEvent, Filter, FilterParamValue, HeightSnapshot, PageRequest, PageResponse, Pubkey, Signature, SignatureDomain, StateProof, Transaction, TransactionBuilder, TransactionStatusSnapshot, VersionInfo, accounts_exports, batchSendTransactions, blocks_exports, buildAndSignTransaction, buildTransaction, chain_exports, collectStream, consensusStatusToString, consensus_exports, createAccount, createThruClientContext, currentOrHistoricalVersionContext, currentVersionContext, deriveAddress, deriveProgramAddress, events_exports, firstStreamValue, forEachStreamValue, fromPrivateKey, generateKeyPair, generateStateProof, getAccount, getBlock, getBlockHeight, getEvent, getRawAccount, getRawBlock, getRawTransaction, getTransaction, getTransactionStatus, height_exports, keys_exports, listAccounts, listBlocks, listEvents, listTransactions, listTransactionsForAccount, proofs_exports, sendTransaction, seqVersionContext, signWithDomain, slotVersionContext, streamAccountUpdates, streamBlocks, streamEvents, streamHeight, streamTransactions, streaming_exports, timestampVersionContext, trackTransaction, transactions_exports, verifyWithDomain, versionContext, withCallOptions };
2935
- //# sourceMappingURL=chunk-YIQKZL4H.js.map
2936
- //# sourceMappingURL=chunk-YIQKZL4H.js.map
3068
+ //# sourceMappingURL=chunk-BYQMY4EE.js.map
3069
+ //# sourceMappingURL=chunk-BYQMY4EE.js.map