hd-wallet-wasm 2.1.1 → 2.1.2

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.
@@ -9,7 +9,7 @@
9
9
  * - Transaction building and signing
10
10
  *
11
11
  * @module hd-wallet-wasm
12
- * @version 2.1.1
12
+ * @version 2.1.2
13
13
  */
14
14
 
15
15
  // Import aligned API for batch operations
@@ -557,6 +557,59 @@ function decodeBech32Fallback(str) {
557
557
  return { hrp, data: out };
558
558
  }
559
559
 
560
+ /** %TypedArray%.prototype.fill, captured at load (uses the internal length). */
561
+ const TYPED_ARRAY_FILL = Object.getPrototypeOf(Uint8Array.prototype).fill;
562
+
563
+ // Internal-slot getters, captured at load: they work on objects of any realm
564
+ // and cannot be overridden by the object.
565
+ const slotGetter = (prototype, name) => Object.getOwnPropertyDescriptor(prototype, name)?.get;
566
+ const TYPED_ARRAY_SLOTS = ['buffer', 'byteOffset', 'byteLength']
567
+ .map((name) => slotGetter(Object.getPrototypeOf(Uint8Array.prototype), name));
568
+ const DATA_VIEW_SLOTS = ['buffer', 'byteOffset', 'byteLength'].map((name) => slotGetter(DataView.prototype, name));
569
+ const ARRAY_BUFFER_BYTE_LENGTH = slotGetter(ArrayBuffer.prototype, 'byteLength');
570
+ const ARRAY_BUFFER_DETACHED = slotGetter(ArrayBuffer.prototype, 'detached');
571
+ const SHARED_BUFFER_BYTE_LENGTH = typeof SharedArrayBuffer === 'function'
572
+ ? slotGetter(SharedArrayBuffer.prototype, 'byteLength')
573
+ : undefined;
574
+
575
+ /**
576
+ * The bytes under binary data of any kind and realm: { buffer, offset,
577
+ * length, detached }, or null for anything that is not binary data.
578
+ */
579
+ function binaryRegion(value) {
580
+ const read = (getters) => {
581
+ try {
582
+ const [buffer, offset, length] = getters.map((get) => get.call(value));
583
+ return { buffer, offset, length };
584
+ } catch {
585
+ return null;
586
+ }
587
+ };
588
+ let region = read(TYPED_ARRAY_SLOTS) ?? read(DATA_VIEW_SLOTS);
589
+ if (region === null) {
590
+ for (const byteLength of [ARRAY_BUFFER_BYTE_LENGTH, SHARED_BUFFER_BYTE_LENGTH]) {
591
+ if (!byteLength) continue;
592
+ try {
593
+ region = { buffer: value, offset: 0, length: byteLength.call(value) };
594
+ break;
595
+ } catch {
596
+ // Not that kind of buffer.
597
+ }
598
+ }
599
+ }
600
+ if (region === null) return null;
601
+ let detached = false;
602
+ try {
603
+ detached = ARRAY_BUFFER_DETACHED ? ARRAY_BUFFER_DETACHED.call(region.buffer) : false;
604
+ } catch {
605
+ detached = false; // A SharedArrayBuffer cannot be detached.
606
+ }
607
+ return { ...region, detached };
608
+ }
609
+
610
+ /** The largest Ethereum chain ID (EIP-2294). */
611
+ const ETH_MAX_CHAIN_ID = 9223372036854775771n;
612
+
560
613
  // =============================================================================
561
614
  // Memory Helpers
562
615
  // =============================================================================
@@ -565,19 +618,23 @@ function decodeBech32Fallback(str) {
565
618
  * Allocate memory and copy data
566
619
  * @param {WebAssembly.Module} wasm - WASM module
567
620
  * @param {Uint8Array} data - Data to copy
568
- * @returns {number} Pointer to allocated memory
621
+ * @returns {{ptr: number, len: number}} Heap pointer and the copied length
569
622
  */
570
623
  function allocAndCopy(wasm, data) {
571
624
  // Only binary data reaches the heap (a string or array would be coerced).
572
625
  V.bytes(data, 'data');
626
+ // One copy from the array's own bytes and length: the caller's `length`
627
+ // (a subclass getter, an own property) is never read again. Callers pass
628
+ // the returned `len` to native code.
629
+ const copy = new Uint8Array(data);
630
+ const len = copy.length;
573
631
  // Many C++ implementations assume pointers are non-null even when `len == 0`.
574
632
  // Allocate a 1-byte placeholder in that case to avoid passing NULL into WASM.
575
- const len = data.length;
576
- const allocLen = len === 0 ? 1 : len;
577
- const ptr = wasm._hd_alloc(allocLen);
633
+ const ptr = wasm._hd_alloc(len === 0 ? 1 : len);
578
634
  if (!ptr) throw new HDWalletError(ErrorCode.OUT_OF_MEMORY);
579
- if (len > 0) wasm.HEAPU8.set(data, ptr);
580
- return ptr;
635
+ wasm.HEAPU8.set(copy, ptr);
636
+ copy.fill(0);
637
+ return { ptr, len };
581
638
  }
582
639
 
583
640
  /**
@@ -714,17 +771,10 @@ function callDynamicStringResult(wasm, invoke, options = {}) {
714
771
  }
715
772
  }
716
773
 
774
+ // Binary data is a Uint8Array in every family: a DataView, an ArrayBuffer or
775
+ // another typed array is refused, as everywhere else.
717
776
  function toUint8Array(data, name = 'data') {
718
- if (data instanceof Uint8Array) {
719
- return data;
720
- }
721
- if (ArrayBuffer.isView(data)) {
722
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice();
723
- }
724
- if (data instanceof ArrayBuffer) {
725
- return new Uint8Array(data.slice(0));
726
- }
727
- throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, `${name} must be binary data`);
777
+ return V.bytes(data, name);
728
778
  }
729
779
 
730
780
  function curveToName(curve) {
@@ -981,7 +1031,7 @@ class HDKey {
981
1031
  publicKeyUncompressed() {
982
1032
  this._checkDestroyed();
983
1033
  const compressed = this.publicKey();
984
- const inPtr = allocAndCopy(this._wasm, compressed);
1034
+ const { ptr: inPtr, len: inLen } = allocAndCopy(this._wasm, compressed);
985
1035
  const outPtr = this._wasm._hd_alloc(65);
986
1036
  try {
987
1037
  const result = this._wasm._hd_curve_decompress_pubkey(inPtr, Curve.SECP256K1, outPtr, 65);
@@ -1133,11 +1183,11 @@ class HDKey {
1133
1183
  peerId() {
1134
1184
  this._checkDestroyed();
1135
1185
  const pubKey = this.publicKey();
1136
- const pubPtr = allocAndCopy(this._wasm, pubKey);
1186
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(this._wasm, pubKey);
1137
1187
  const outPtr = this._wasm._hd_alloc(64);
1138
1188
  try {
1139
1189
  const result = checkLengthWithin(
1140
- this._wasm._hd_libp2p_peer_id(pubPtr, pubKey.length, this.curve, outPtr, 64), 1, 64);
1190
+ this._wasm._hd_libp2p_peer_id(pubPtr, pubLen, this.curve, outPtr, 64), 1, 64);
1141
1191
  return readBytes(this._wasm, outPtr, result);
1142
1192
  } finally {
1143
1193
  this._wasm._hd_dealloc(pubPtr);
@@ -1152,10 +1202,10 @@ class HDKey {
1152
1202
  peerIdString() {
1153
1203
  this._checkDestroyed();
1154
1204
  const pubKey = this.publicKey();
1155
- const pubPtr = allocAndCopy(this._wasm, pubKey);
1205
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(this._wasm, pubKey);
1156
1206
  const outPtr = this._wasm._hd_alloc(128);
1157
1207
  try {
1158
- const result = this._wasm._hd_libp2p_peer_id_string(pubPtr, pubKey.length, this.curve, outPtr, 128);
1208
+ const result = this._wasm._hd_libp2p_peer_id_string(pubPtr, pubLen, this.curve, outPtr, 128);
1159
1209
  checkResult(result);
1160
1210
  return readString(this._wasm, outPtr);
1161
1211
  } finally {
@@ -1171,10 +1221,10 @@ class HDKey {
1171
1221
  ipnsHash() {
1172
1222
  this._checkDestroyed();
1173
1223
  const pubKey = this.publicKey();
1174
- const pubPtr = allocAndCopy(this._wasm, pubKey);
1224
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(this._wasm, pubKey);
1175
1225
  const outPtr = this._wasm._hd_alloc(128);
1176
1226
  try {
1177
- const result = this._wasm._hd_libp2p_ipns_hash(pubPtr, pubKey.length, this.curve, outPtr, 128);
1227
+ const result = this._wasm._hd_libp2p_ipns_hash(pubPtr, pubLen, this.curve, outPtr, 128);
1178
1228
  checkResult(result);
1179
1229
  return readString(this._wasm, outPtr);
1180
1230
  } finally {
@@ -1434,15 +1484,15 @@ function createModule(wasm) {
1434
1484
  fromEntropy(entropy, language = Language.ENGLISH) {
1435
1485
  V.bytes(entropy, 'entropy', ErrorCode.INVALID_ENTROPY_LENGTH);
1436
1486
  V.integer(language, 'language', 0, 0x7fffffff);
1437
- const entropyPtr = allocAndCopy(wasm, entropy);
1487
+ const { ptr: entropyPtr, len: entropyLen } = allocAndCopy(wasm, entropy);
1438
1488
  const outputPtr = wasm._hd_alloc(1024);
1439
1489
  try {
1440
- const result = wasm._hd_entropy_to_mnemonic(entropyPtr, entropy.length, language, outputPtr, 1024);
1490
+ const result = wasm._hd_entropy_to_mnemonic(entropyPtr, entropyLen, language, outputPtr, 1024);
1441
1491
  checkResult(result);
1442
1492
  return readString(wasm, outputPtr);
1443
1493
  } finally {
1444
1494
  // SECURITY FIX [HIGH-04]: Wipe sensitive data before deallocation
1445
- wasm._hd_secure_wipe(entropyPtr, entropy.length);
1495
+ wasm._hd_secure_wipe(entropyPtr, entropyLen);
1446
1496
  wasm._hd_dealloc(entropyPtr);
1447
1497
  // Wipe mnemonic output buffer
1448
1498
  wasm._hd_secure_wipe(outputPtr, 1024);
@@ -1530,15 +1580,15 @@ function createModule(wasm) {
1530
1580
  // BIP-32 allows 128-512 bits (16-64 bytes)
1531
1581
  V.bytesInRange(seed, 16, 64, 'Seed', ErrorCode.INVALID_SEED);
1532
1582
  V.curve(curve);
1533
- const seedPtr = allocAndCopy(wasm, seed);
1583
+ const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
1534
1584
  try {
1535
- const handle = wasm._hd_key_from_seed(seedPtr, seed.length, curve);
1585
+ const handle = wasm._hd_key_from_seed(seedPtr, seedLen, curve);
1536
1586
  if (!handle) {
1537
1587
  throw new HDWalletError(ErrorCode.INVALID_SEED);
1538
1588
  }
1539
1589
  return new HDKey(wasm, handle, 'm');
1540
1590
  } finally {
1541
- wasm._hd_secure_wipe(seedPtr, seed.length);
1591
+ wasm._hd_secure_wipe(seedPtr, seedLen);
1542
1592
  wasm._hd_dealloc(seedPtr);
1543
1593
  }
1544
1594
  },
@@ -1659,7 +1709,7 @@ function createModule(wasm) {
1659
1709
  publicKeyFromPrivate(privateKey, curve) {
1660
1710
  const pubFromPrivFn = requireWasmFunction(wasm, '_hd_curve_pubkey_from_privkey');
1661
1711
  V.privateKey(wasm, privateKey, curve);
1662
- const privPtr = allocAndCopy(wasm, privateKey);
1712
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
1663
1713
  const pubPtr = wasm._hd_alloc(65);
1664
1714
  try {
1665
1715
  // Returns the key length; a private key outside 0 < d < n fails.
@@ -1667,7 +1717,7 @@ function createModule(wasm) {
1667
1717
  checkLength(pubFromPrivFn(privPtr, curve, pubPtr, 65), expectedLen);
1668
1718
  return readBytes(wasm, pubPtr, expectedLen);
1669
1719
  } finally {
1670
- wasm._hd_secure_wipe(privPtr, privateKey.length);
1720
+ wasm._hd_secure_wipe(privPtr, privLen);
1671
1721
  wasm._hd_dealloc(privPtr);
1672
1722
  wasm._hd_dealloc(pubPtr);
1673
1723
  }
@@ -1702,7 +1752,7 @@ function createModule(wasm) {
1702
1752
  const decompressFn = requireWasmFunction(wasm, '_hd_curve_decompress_pubkey');
1703
1753
  // The export reads exactly 33 bytes: a valid compressed key only.
1704
1754
  V.publicKey(wasm, publicKey, curve, 'publicKey', [curve === Curve.P384 ? 49 : 33]);
1705
- const inPtr = allocAndCopy(wasm, publicKey);
1755
+ const { ptr: inPtr, len: inLen } = allocAndCopy(wasm, publicKey);
1706
1756
  const outPtr = wasm._hd_alloc(97);
1707
1757
  try {
1708
1758
  // Status export: 0 on success (secp256k1 only), -Error otherwise.
@@ -1732,17 +1782,17 @@ function createModule(wasm) {
1732
1782
  V.curve(curve, [Curve.SECP256K1, Curve.P256, Curve.P384, Curve.X25519]);
1733
1783
  V.privateKey(wasm, privateKey, curve);
1734
1784
  V.publicKey(wasm, publicKey, curve);
1735
- const privPtr = allocAndCopy(wasm, privateKey);
1736
- const pubPtr = allocAndCopy(wasm, publicKey);
1785
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
1786
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
1737
1787
  const secretSize = curve === Curve.P384 ? 48 : 32;
1738
1788
  const secretPtr = wasm._hd_alloc(secretSize);
1739
1789
  try {
1740
- const result = ecdhFn(curve, privPtr, privateKey.length, pubPtr, publicKey.length, secretPtr, secretSize);
1790
+ const result = ecdhFn(curve, privPtr, privLen, pubPtr, pubLen, secretPtr, secretSize);
1741
1791
  // Returns the secret length (the curve's field size); -1 on failure.
1742
1792
  if (result !== secretSize) throw new HDWalletError(ErrorCode.INTERNAL, 'ECDH key exchange failed');
1743
1793
  return readBytes(wasm, secretPtr, secretSize);
1744
1794
  } finally {
1745
- wasm._hd_secure_wipe(privPtr, privateKey.length);
1795
+ wasm._hd_secure_wipe(privPtr, privLen);
1746
1796
  wasm._hd_secure_wipe(secretPtr, secretSize);
1747
1797
  wasm._hd_dealloc(privPtr);
1748
1798
  wasm._hd_dealloc(pubPtr);
@@ -1766,12 +1816,12 @@ function createModule(wasm) {
1766
1816
  sign(message, privateKey) {
1767
1817
  V.bytes(message, 'message');
1768
1818
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
1769
- const msgPtr = allocAndCopy(wasm, message);
1770
- const keyPtr = allocAndCopy(wasm, privateKey);
1819
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
1820
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
1771
1821
  const sigPtr = wasm._hd_alloc(72);
1772
1822
  try {
1773
1823
  // Returns 64 (compact r||s); a private key outside 0 < d < n fails.
1774
- checkLength(wasm._hd_secp256k1_sign(msgPtr, message.length, keyPtr, sigPtr, 72), 64);
1824
+ checkLength(wasm._hd_secp256k1_sign(msgPtr, msgLen, keyPtr, sigPtr, 72), 64);
1775
1825
  return readBytes(wasm, sigPtr, 64);
1776
1826
  } finally {
1777
1827
  wasm._hd_secure_wipe(keyPtr, 32);
@@ -1793,14 +1843,14 @@ function createModule(wasm) {
1793
1843
  const signRecoverableFn = requireWasmFunction(wasm, '_hd_secp256k1_sign_recoverable');
1794
1844
  V.bytesOfLength(message, 32, 'message hash');
1795
1845
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
1796
- const msgPtr = allocAndCopy(wasm, message);
1797
- const keyPtr = allocAndCopy(wasm, privateKey);
1846
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
1847
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
1798
1848
  const sigPtr = wasm._hd_alloc(65);
1799
1849
  try {
1800
1850
  // C signature: int32_t hd_secp256k1_sign_recoverable(hash, hash_len, privkey, out_sig65)
1801
1851
  // Returns 65 on success (bytes written), -Error on failure.
1802
1852
  // out_sig65 = r[32] + s[32] + v[1], recovery ID is at byte[64].
1803
- checkLength(signRecoverableFn(msgPtr, message.length, keyPtr, sigPtr), 65);
1853
+ checkLength(signRecoverableFn(msgPtr, msgLen, keyPtr, sigPtr), 65);
1804
1854
  const recoveryId = wasm.HEAPU8[sigPtr + 64];
1805
1855
  if (recoveryId > 3) throw new HDWalletError(ErrorCode.INVALID_SIGNATURE);
1806
1856
  const signature = readBytes(wasm, sigPtr, 64);
@@ -1826,14 +1876,14 @@ function createModule(wasm) {
1826
1876
  V.bytes(signature, 'signature');
1827
1877
  V.bytes(publicKey, 'publicKey');
1828
1878
  // Compact r||s (64 bytes) or DER (8-72 bytes); a key of either SEC1 size.
1829
- if (signature.length < 8 || signature.length > 72 || !V.hasLength(publicKey, [33, 65])) {
1879
+ if (V.byteLength(signature) < 8 || V.byteLength(signature) > 72 || !V.hasLength(publicKey, [33, 65])) {
1830
1880
  return false;
1831
1881
  }
1832
- const msgPtr = allocAndCopy(wasm, message);
1833
- const sigPtr = allocAndCopy(wasm, signature);
1834
- const pubPtr = allocAndCopy(wasm, publicKey);
1882
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
1883
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
1884
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
1835
1885
  try {
1836
- const result = wasm._hd_secp256k1_verify(msgPtr, message.length, sigPtr, signature.length, pubPtr, publicKey.length);
1886
+ const result = wasm._hd_secp256k1_verify(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen);
1837
1887
  return result === 1;
1838
1888
  } finally {
1839
1889
  wasm._hd_dealloc(msgPtr);
@@ -1857,18 +1907,18 @@ function createModule(wasm) {
1857
1907
  if (!Number.isInteger(recoveryId) || recoveryId < 0 || recoveryId > 3) {
1858
1908
  throw new HDWalletError(ErrorCode.INVALID_SIGNATURE, 'recoveryId must be 0, 1, 2 or 3');
1859
1909
  }
1860
- const msgPtr = allocAndCopy(wasm, message);
1910
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
1861
1911
  // Build 65-byte sig buffer: r[32] + s[32] + v[1]
1862
1912
  // C signature: int32_t hd_secp256k1_recover(hash, hash_len, sig65, out_pubkey33)
1863
1913
  const sig65 = new Uint8Array(65);
1864
1914
  sig65.set(signature.subarray(0, 64));
1865
1915
  sig65[64] = recoveryId;
1866
- const sigPtr = allocAndCopy(wasm, sig65);
1916
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, sig65);
1867
1917
  const pubPtr = wasm._hd_alloc(33);
1868
1918
  try {
1869
1919
  // Returns 33 on success; a signature no key recovers from (ids 2
1870
1920
  // and 3 when r + n >= p included) fails, so this throws.
1871
- checkLength(recoverFn(msgPtr, message.length, sigPtr, pubPtr), 33);
1921
+ checkLength(recoverFn(msgPtr, msgLen, sigPtr, pubPtr), 33);
1872
1922
  return readBytes(wasm, pubPtr, 33);
1873
1923
  } finally {
1874
1924
  wasm._hd_dealloc(msgPtr);
@@ -1886,11 +1936,11 @@ function createModule(wasm) {
1886
1936
  ecdh(privateKey, publicKey) {
1887
1937
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
1888
1938
  V.publicKey(wasm, publicKey, Curve.SECP256K1);
1889
- const privPtr = allocAndCopy(wasm, privateKey);
1890
- const pubPtr = allocAndCopy(wasm, publicKey);
1939
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
1940
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
1891
1941
  const secretPtr = wasm._hd_alloc(32);
1892
1942
  try {
1893
- checkLength(wasm._hd_ecdh_secp256k1(privPtr, pubPtr, publicKey.length, secretPtr, 32), 32);
1943
+ checkLength(wasm._hd_ecdh_secp256k1(privPtr, pubPtr, pubLen, secretPtr, 32), 32);
1894
1944
  return readBytes(wasm, secretPtr, 32);
1895
1945
  } finally {
1896
1946
  wasm._hd_secure_wipe(privPtr, 32);
@@ -1913,7 +1963,7 @@ function createModule(wasm) {
1913
1963
  */
1914
1964
  publicKeyFromSeed(seed) {
1915
1965
  V.privateKey(wasm, seed, Curve.ED25519, 'seed');
1916
- const seedPtr = allocAndCopy(wasm, seed);
1966
+ const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
1917
1967
  const pubPtr = wasm._hd_alloc(32);
1918
1968
  try {
1919
1969
  checkResult(wasm._hd_ed25519_pubkey_from_seed(seedPtr, pubPtr, 32));
@@ -1934,11 +1984,11 @@ function createModule(wasm) {
1934
1984
  sign(message, privateKey) {
1935
1985
  V.bytes(message, 'message');
1936
1986
  V.privateKey(wasm, privateKey, Curve.ED25519);
1937
- const msgPtr = allocAndCopy(wasm, message);
1938
- const keyPtr = allocAndCopy(wasm, privateKey);
1987
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
1988
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
1939
1989
  const sigPtr = wasm._hd_alloc(64);
1940
1990
  try {
1941
- checkLength(wasm._hd_ed25519_sign(msgPtr, message.length, keyPtr, sigPtr, 64), 64);
1991
+ checkLength(wasm._hd_ed25519_sign(msgPtr, msgLen, keyPtr, sigPtr, 64), 64);
1942
1992
  return readBytes(wasm, sigPtr, 64);
1943
1993
  } finally {
1944
1994
  wasm._hd_secure_wipe(keyPtr, 32);
@@ -1959,13 +2009,13 @@ function createModule(wasm) {
1959
2009
  V.bytes(message, 'message');
1960
2010
  V.bytes(signature, 'signature');
1961
2011
  V.bytes(publicKey, 'publicKey');
1962
- if (signature.length !== 64 || publicKey.length !== 32) return false;
1963
- const msgPtr = allocAndCopy(wasm, message);
1964
- const sigPtr = allocAndCopy(wasm, signature);
1965
- const pubPtr = allocAndCopy(wasm, publicKey);
2012
+ if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 32) return false;
2013
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2014
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
2015
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
1966
2016
  try {
1967
2017
  // RFC 8032 strict: S < L, A and R canonical and not of small order.
1968
- const result = wasm._hd_ed25519_verify(msgPtr, message.length, sigPtr, signature.length, pubPtr, publicKey.length);
2018
+ const result = wasm._hd_ed25519_verify(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen);
1969
2019
  return result === 1;
1970
2020
  } finally {
1971
2021
  wasm._hd_dealloc(msgPtr);
@@ -1989,11 +2039,11 @@ function createModule(wasm) {
1989
2039
  sign(message, privateKey) {
1990
2040
  V.bytes(message, 'message');
1991
2041
  V.privateKey(wasm, privateKey, Curve.P256);
1992
- const msgPtr = allocAndCopy(wasm, message);
1993
- const keyPtr = allocAndCopy(wasm, privateKey);
2042
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2043
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
1994
2044
  const sigPtr = wasm._hd_alloc(72);
1995
2045
  try {
1996
- checkLength(wasm._hd_p256_sign(msgPtr, message.length, keyPtr, sigPtr, 72), 64);
2046
+ checkLength(wasm._hd_p256_sign(msgPtr, msgLen, keyPtr, sigPtr, 72), 64);
1997
2047
  return readBytes(wasm, sigPtr, 64);
1998
2048
  } finally {
1999
2049
  wasm._hd_secure_wipe(keyPtr, 32);
@@ -2015,13 +2065,13 @@ function createModule(wasm) {
2015
2065
  V.bytes(message, 'message');
2016
2066
  V.bytes(signature, 'signature');
2017
2067
  // The export reads exactly 64 signature bytes and takes no length.
2018
- if (signature.length !== 64 || !V.isPublicKey(wasm, publicKey, Curve.P256)) return false;
2019
- const msgPtr = allocAndCopy(wasm, message);
2020
- const sigPtr = allocAndCopy(wasm, signature);
2021
- const pubPtr = allocAndCopy(wasm, publicKey);
2068
+ if (V.byteLength(signature) !== 64 || !V.isPublicKey(wasm, publicKey, Curve.P256)) return false;
2069
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2070
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
2071
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2022
2072
  try {
2023
2073
  // C: hd_p256_verify(message, message_len, public_key, public_key_len, signature)
2024
- return verifyFn(msgPtr, message.length, pubPtr, publicKey.length, sigPtr) === 0;
2074
+ return verifyFn(msgPtr, msgLen, pubPtr, pubLen, sigPtr) === 0;
2025
2075
  } finally {
2026
2076
  wasm._hd_dealloc(msgPtr);
2027
2077
  wasm._hd_dealloc(sigPtr);
@@ -2032,11 +2082,11 @@ function createModule(wasm) {
2032
2082
  ecdh(privateKey, publicKey) {
2033
2083
  V.privateKey(wasm, privateKey, Curve.P256);
2034
2084
  V.publicKey(wasm, publicKey, Curve.P256);
2035
- const privPtr = allocAndCopy(wasm, privateKey);
2036
- const pubPtr = allocAndCopy(wasm, publicKey);
2085
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
2086
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2037
2087
  const secretPtr = wasm._hd_alloc(32);
2038
2088
  try {
2039
- checkLength(wasm._hd_ecdh_p256(privPtr, pubPtr, publicKey.length, secretPtr, 32), 32);
2089
+ checkLength(wasm._hd_ecdh_p256(privPtr, pubPtr, pubLen, secretPtr, 32), 32);
2040
2090
  return readBytes(wasm, secretPtr, 32);
2041
2091
  } finally {
2042
2092
  wasm._hd_secure_wipe(privPtr, 32);
@@ -2062,11 +2112,11 @@ function createModule(wasm) {
2062
2112
  sign(message, privateKey) {
2063
2113
  V.bytes(message, 'message');
2064
2114
  V.privateKey(wasm, privateKey, Curve.P384);
2065
- const msgPtr = allocAndCopy(wasm, message);
2066
- const keyPtr = allocAndCopy(wasm, privateKey);
2115
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2116
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2067
2117
  const sigPtr = wasm._hd_alloc(104);
2068
2118
  try {
2069
- checkLength(wasm._hd_p384_sign(msgPtr, message.length, keyPtr, sigPtr, 104), 96);
2119
+ checkLength(wasm._hd_p384_sign(msgPtr, msgLen, keyPtr, sigPtr, 104), 96);
2070
2120
  return readBytes(wasm, sigPtr, 96);
2071
2121
  } finally {
2072
2122
  wasm._hd_secure_wipe(keyPtr, 48);
@@ -2088,13 +2138,13 @@ function createModule(wasm) {
2088
2138
  V.bytes(message, 'message');
2089
2139
  V.bytes(signature, 'signature');
2090
2140
  // The export reads exactly 96 signature bytes and takes no length.
2091
- if (signature.length !== 96 || !V.isPublicKey(wasm, publicKey, Curve.P384)) return false;
2092
- const msgPtr = allocAndCopy(wasm, message);
2093
- const sigPtr = allocAndCopy(wasm, signature);
2094
- const pubPtr = allocAndCopy(wasm, publicKey);
2141
+ if (V.byteLength(signature) !== 96 || !V.isPublicKey(wasm, publicKey, Curve.P384)) return false;
2142
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2143
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
2144
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2095
2145
  try {
2096
2146
  // C: hd_p384_verify(message, message_len, public_key, public_key_len, signature)
2097
- return verifyFn(msgPtr, message.length, pubPtr, publicKey.length, sigPtr) === 0;
2147
+ return verifyFn(msgPtr, msgLen, pubPtr, pubLen, sigPtr) === 0;
2098
2148
  } finally {
2099
2149
  wasm._hd_dealloc(msgPtr);
2100
2150
  wasm._hd_dealloc(sigPtr);
@@ -2105,11 +2155,11 @@ function createModule(wasm) {
2105
2155
  ecdh(privateKey, publicKey) {
2106
2156
  V.privateKey(wasm, privateKey, Curve.P384);
2107
2157
  V.publicKey(wasm, publicKey, Curve.P384);
2108
- const privPtr = allocAndCopy(wasm, privateKey);
2109
- const pubPtr = allocAndCopy(wasm, publicKey);
2158
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
2159
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2110
2160
  const secretPtr = wasm._hd_alloc(48);
2111
2161
  try {
2112
- checkLength(wasm._hd_ecdh_p384(privPtr, pubPtr, publicKey.length, secretPtr, 48), 48);
2162
+ checkLength(wasm._hd_ecdh_p384(privPtr, pubPtr, pubLen, secretPtr, 48), 48);
2113
2163
  return readBytes(wasm, secretPtr, 48);
2114
2164
  } finally {
2115
2165
  wasm._hd_secure_wipe(privPtr, 48);
@@ -2132,7 +2182,7 @@ function createModule(wasm) {
2132
2182
  */
2133
2183
  publicKey(privateKey) {
2134
2184
  V.privateKey(wasm, privateKey, Curve.X25519);
2135
- const privPtr = allocAndCopy(wasm, privateKey);
2185
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
2136
2186
  const pubPtr = wasm._hd_alloc(32);
2137
2187
  try {
2138
2188
  checkResult(wasm._hd_x25519_pubkey(privPtr, pubPtr, 32));
@@ -2153,8 +2203,8 @@ function createModule(wasm) {
2153
2203
  ecdh(privateKey, publicKey) {
2154
2204
  V.privateKey(wasm, privateKey, Curve.X25519);
2155
2205
  V.publicKey(wasm, publicKey, Curve.X25519);
2156
- const privPtr = allocAndCopy(wasm, privateKey);
2157
- const pubPtr = allocAndCopy(wasm, publicKey);
2206
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, privateKey);
2207
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2158
2208
  const secretPtr = wasm._hd_alloc(32);
2159
2209
  try {
2160
2210
  checkResult(wasm._hd_ecdh_x25519(privPtr, pubPtr, secretPtr, 32));
@@ -2233,10 +2283,10 @@ function createModule(wasm) {
2233
2283
  const fn = requireWasmFunction(wasm, '_hd_coin_address_from_pubkey');
2234
2284
  publicKey = coinPublicKey(coinType, publicKey);
2235
2285
  V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
2236
- const pubPtr = allocAndCopy(wasm, publicKey);
2286
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2237
2287
  const outPtr = wasm._hd_alloc(128);
2238
2288
  try {
2239
- const result = fn(coinType, network, pubPtr, publicKey.length, outPtr, 128);
2289
+ const result = fn(coinType, network, pubPtr, pubLen, outPtr, 128);
2240
2290
  checkResult(result);
2241
2291
  return readString(wasm, outPtr);
2242
2292
  } finally {
@@ -2276,20 +2326,20 @@ function createModule(wasm) {
2276
2326
  const fn = requireWasmFunction(wasm, '_hd_coin_sign_message');
2277
2327
  V.bytes(message, 'message');
2278
2328
  V.privateKey(wasm, privateKey, coinCurve(coinType));
2279
- const msgPtr = allocAndCopy(wasm, message);
2280
- const keyPtr = allocAndCopy(wasm, privateKey);
2329
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2330
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2281
2331
  const sigLenPtr = wasm._hd_alloc(4);
2282
2332
  // Pre-allocate enough for the largest possible signature (65 bytes for ECDSA recoverable)
2283
2333
  const maxSigLen = 128;
2284
2334
  wasm.setValue(sigLenPtr, maxSigLen, 'i32');
2285
2335
  const sigPtr = wasm._hd_alloc(maxSigLen);
2286
2336
  try {
2287
- const result = fn(coinType, msgPtr, message.length, keyPtr, sigPtr, sigLenPtr);
2337
+ const result = fn(coinType, msgPtr, msgLen, keyPtr, sigPtr, sigLenPtr);
2288
2338
  checkResult(result);
2289
2339
  const actualLen = checkLengthWithin(wasm.getValue(sigLenPtr, 'i32'), 1, maxSigLen);
2290
2340
  return readBytes(wasm, sigPtr, actualLen);
2291
2341
  } finally {
2292
- wasm._hd_secure_wipe(keyPtr, privateKey.length);
2342
+ wasm._hd_secure_wipe(keyPtr, keyLen);
2293
2343
  wasm._hd_dealloc(msgPtr);
2294
2344
  wasm._hd_dealloc(keyPtr);
2295
2345
  wasm._hd_dealloc(sigPtr);
@@ -2311,13 +2361,13 @@ function createModule(wasm) {
2311
2361
  V.bytes(message, 'message');
2312
2362
  V.bytes(signature, 'signature');
2313
2363
  V.bytes(publicKey, 'publicKey');
2314
- const msgPtr = allocAndCopy(wasm, message);
2315
- const sigPtr = allocAndCopy(wasm, signature);
2316
- const pubPtr = allocAndCopy(wasm, publicKey);
2364
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
2365
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
2366
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2317
2367
  try {
2318
2368
  // The export takes every length: any failure (a malformed signature
2319
2369
  // or key included) is "not verified".
2320
- return fn(coinType, msgPtr, message.length, sigPtr, signature.length, pubPtr, publicKey.length) === 1;
2370
+ return fn(coinType, msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
2321
2371
  } finally {
2322
2372
  wasm._hd_dealloc(msgPtr);
2323
2373
  wasm._hd_dealloc(sigPtr);
@@ -2370,7 +2420,7 @@ function createModule(wasm) {
2370
2420
  default:
2371
2421
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Invalid address type');
2372
2422
  }
2373
- const pubPtr = allocAndCopy(wasm, publicKey);
2423
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2374
2424
  const outPtr = wasm._hd_alloc(128);
2375
2425
  try {
2376
2426
  let fnName;
@@ -2394,7 +2444,7 @@ function createModule(wasm) {
2394
2444
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Invalid address type');
2395
2445
  }
2396
2446
  const addressFn = requireWasmFunction(wasm, fnName);
2397
- const result = addressFn(pubPtr, publicKey.length, network, outPtr, 128);
2447
+ const result = addressFn(pubPtr, pubLen, network, outPtr, 128);
2398
2448
  checkResult(result);
2399
2449
  return readString(wasm, outPtr);
2400
2450
  } finally {
@@ -2462,7 +2512,7 @@ function createModule(wasm) {
2462
2512
  const toWifFn = requireWasmFunction(wasm, '_hd_btc_to_wif');
2463
2513
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
2464
2514
  V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
2465
- const keyPtr = allocAndCopy(wasm, privateKey);
2515
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2466
2516
  const outPtr = wasm._hd_alloc(64);
2467
2517
  try {
2468
2518
  const result = toWifFn(keyPtr, compressed ? 1 : 0, network, outPtr, 64);
@@ -2500,7 +2550,7 @@ function createModule(wasm) {
2500
2550
  V.string(message, 'message');
2501
2551
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
2502
2552
  const msgPtr = allocString(wasm, message);
2503
- const keyPtr = allocAndCopy(wasm, privateKey);
2553
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2504
2554
  const sigPtr = wasm._hd_alloc(65);
2505
2555
  try {
2506
2556
  const result = signFn(msgPtr, keyPtr, compressed ? 1 : 0, sigPtr, 65);
@@ -2528,12 +2578,12 @@ function createModule(wasm) {
2528
2578
  } catch {
2529
2579
  return false;
2530
2580
  }
2531
- if (sigBytes.length !== 65) return false;
2581
+ if (V.byteLength(sigBytes) !== 65) return false;
2532
2582
  const msgPtr = allocString(wasm, message);
2533
- const sigPtr = allocAndCopy(wasm, sigBytes);
2583
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, sigBytes);
2534
2584
  const addrPtr = allocString(wasm, address);
2535
2585
  try {
2536
- const result = verifyFn(msgPtr, sigPtr, sigBytes.length, addrPtr, network);
2586
+ const result = verifyFn(msgPtr, sigPtr, sigLen, addrPtr, network);
2537
2587
  return result === 1;
2538
2588
  } finally {
2539
2589
  wasm._hd_dealloc(msgPtr);
@@ -2556,26 +2606,28 @@ function createModule(wasm) {
2556
2606
  if (input === null || typeof input !== 'object') {
2557
2607
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'input must be an object');
2558
2608
  }
2609
+ // Each field is read once; only these values are used below.
2610
+ const { txid, vout, amount: amountArg, scriptType: scriptTypeArg, sequence: sequenceArg, pubkey } = input;
2559
2611
  // The txid: 32 bytes as 64 hex digits, in the given byte order.
2560
- const txidBytes = typeof input.txid === 'string' && input.txid.length === 64
2561
- ? hexOrNull(input.txid)
2612
+ const txidBytes = typeof txid === 'string' && txid.length === 64
2613
+ ? hexOrNull(txid)
2562
2614
  : null;
2563
2615
  if (txidBytes === null) {
2564
2616
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'txid must be 64 hex digits');
2565
2617
  }
2566
- V.uint32(input.vout, 'vout');
2567
- const amount = V.unsignedBigInt(input.amount, 'amount', 64);
2568
- const scriptType = input.scriptType || 0;
2618
+ V.uint32(vout, 'vout');
2619
+ const amount = V.unsignedBigInt(amountArg, 'amount', 64);
2620
+ const scriptType = scriptTypeArg || 0;
2569
2621
  V.integer(scriptType, 'scriptType', 0, BitcoinScriptType.MULTISIG);
2570
- const sequence = input.sequence || 0xffffffff;
2622
+ const sequence = sequenceArg || 0xffffffff;
2571
2623
  V.uint32(sequence, 'sequence');
2572
2624
  // The export reads a 33-byte compressed key.
2573
- V.publicKey(wasm, input.pubkey, Curve.SECP256K1, 'pubkey', [33]);
2574
- const txidPtr = allocAndCopy(wasm, txidBytes);
2575
- const pubPtr = allocAndCopy(wasm, input.pubkey);
2625
+ V.publicKey(wasm, pubkey, Curve.SECP256K1, 'pubkey', [33]);
2626
+ const { ptr: txidPtr } = allocAndCopy(wasm, txidBytes);
2627
+ const { ptr: pubPtr } = allocAndCopy(wasm, pubkey);
2576
2628
  try {
2577
2629
  const result = addInputFn(
2578
- this._handle, txidPtr, input.vout,
2630
+ this._handle, txidPtr, vout,
2579
2631
  amount, scriptType,
2580
2632
  pubPtr, sequence
2581
2633
  );
@@ -2591,6 +2643,9 @@ function createModule(wasm) {
2591
2643
  addOutput(address, amount, network = Network.MAINNET) {
2592
2644
  V.string(address, 'address');
2593
2645
  amount = V.unsignedBigInt(amount, 'amount', 64);
2646
+ // The address must be of this network: a mainnet transaction never
2647
+ // pays a testnet address, or the reverse. Regtest is not supported.
2648
+ V.oneOf(network, [Network.MAINNET, Network.TESTNET], 'network');
2594
2649
  // Use address-based output helper: decode address to script internally
2595
2650
  const addP2wpkhFn = getWasmFunction(wasm, '_hd_btc_tx_add_p2wpkh_output');
2596
2651
  const addP2pkhFn = getWasmFunction(wasm, '_hd_btc_tx_add_p2pkh_output');
@@ -2610,8 +2665,14 @@ function createModule(wasm) {
2610
2665
  checkResult(decResult);
2611
2666
  const addrType = wasm.getValue(typePtr, 'i32');
2612
2667
  const hashLen = checkLengthWithin(wasm.getValue(hashLenPtr, 'i32'), 1, 32);
2613
- // The P2WPKH/P2PKH helpers read a 20-byte key hash.
2614
- if ((addrType === 2 || addrType === 0) && hashLen !== 20) {
2668
+ if (wasm.getValue(networkPtr, 'i32') !== network) {
2669
+ throw new HDWalletError(ErrorCode.INVALID_ADDRESS, 'address is not of this transaction\'s network');
2670
+ }
2671
+ // The native decoder types an address by witness version and
2672
+ // program length together; each type has one payload length
2673
+ // (the P2WPKH/P2PKH helpers read a 20-byte key hash).
2674
+ const payloadLength = [20, 20, 20, 32, 32][addrType];
2675
+ if (payloadLength === undefined || hashLen !== payloadLength) {
2615
2676
  throw new HDWalletError(ErrorCode.INVALID_ADDRESS);
2616
2677
  }
2617
2678
  // P2WPKH = 2, P2PKH = 0
@@ -2640,9 +2701,9 @@ function createModule(wasm) {
2640
2701
  } else {
2641
2702
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Unsupported address type');
2642
2703
  }
2643
- const scriptPtr = allocAndCopy(wasm, script);
2704
+ const { ptr: scriptPtr, len: scriptLen } = allocAndCopy(wasm, script);
2644
2705
  try {
2645
- const result = addOutputFn(this._handle, BigInt(amount), scriptPtr, script.length);
2706
+ const result = addOutputFn(this._handle, BigInt(amount), scriptPtr, scriptLen);
2646
2707
  checkResult(result);
2647
2708
  } finally {
2648
2709
  wasm._hd_dealloc(scriptPtr);
@@ -2662,7 +2723,7 @@ function createModule(wasm) {
2662
2723
  sign(privateKey) {
2663
2724
  const signFn = requireWasmFunction(wasm, '_hd_btc_tx_sign');
2664
2725
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
2665
- const keyPtr = allocAndCopy(wasm, privateKey);
2726
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2666
2727
  try {
2667
2728
  // Sign all inputs
2668
2729
  for (let i = 0; i < this._inputCount; i++) {
@@ -2750,9 +2811,9 @@ function createModule(wasm) {
2750
2811
  parse(data) {
2751
2812
  const parseFn = requireWasmFunction(wasm, '_hd_btc_tx_parse');
2752
2813
  V.bytes(data, 'data');
2753
- const dataPtr = allocAndCopy(wasm, data);
2814
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
2754
2815
  try {
2755
- const handle = parseFn(dataPtr, data.length);
2816
+ const handle = parseFn(dataPtr, dataLen);
2756
2817
  if (!handle) throw new HDWalletError(ErrorCode.INVALID_TRANSACTION);
2757
2818
  return this._makeTxObj(handle);
2758
2819
  } finally {
@@ -2785,7 +2846,7 @@ function createModule(wasm) {
2785
2846
  getAddress(publicKey) {
2786
2847
  const getAddressFn = requireWasmFunction(wasm, '_hd_eth_address');
2787
2848
  V.bytesOfLength(publicKey, [33, 64, 65], 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
2788
- if (publicKey.length === 64) {
2849
+ if (V.byteLength(publicKey) === 64) {
2789
2850
  // Raw x || y: valid when 04 || x || y is a point.
2790
2851
  const sec1 = new Uint8Array(65);
2791
2852
  sec1[0] = 0x04;
@@ -2794,10 +2855,10 @@ function createModule(wasm) {
2794
2855
  } else {
2795
2856
  V.publicKey(wasm, publicKey, Curve.SECP256K1);
2796
2857
  }
2797
- const pubPtr = allocAndCopy(wasm, publicKey);
2858
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
2798
2859
  const outPtr = wasm._hd_alloc(64);
2799
2860
  try {
2800
- const result = getAddressFn(pubPtr, publicKey.length, outPtr, 64);
2861
+ const result = getAddressFn(pubPtr, pubLen, outPtr, 64);
2801
2862
  checkResult(result);
2802
2863
  return readString(wasm, outPtr);
2803
2864
  } finally {
@@ -2852,11 +2913,11 @@ function createModule(wasm) {
2852
2913
  const signFn = requireWasmFunction(wasm, '_hd_eth_sign_message');
2853
2914
  const msgBytes = ethereumMessageBytes(message);
2854
2915
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
2855
- const msgPtr = allocAndCopy(wasm, msgBytes);
2856
- const keyPtr = allocAndCopy(wasm, privateKey);
2916
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, msgBytes);
2917
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2857
2918
  const sigPtr = wasm._hd_alloc(65);
2858
2919
  try {
2859
- const result = signFn(msgPtr, msgBytes.length, keyPtr, sigPtr, 65);
2920
+ const result = signFn(msgPtr, msgLen, keyPtr, sigPtr, 65);
2860
2921
  checkResult(result);
2861
2922
  const sigBytes = readBytes(wasm, sigPtr, 65);
2862
2923
  return '0x' + encodeHexFallback(sigBytes);
@@ -2888,10 +2949,10 @@ function createModule(wasm) {
2888
2949
  // keccak256 via WASM
2889
2950
  function k256(data) {
2890
2951
  const d = data instanceof Uint8Array ? data : new TextEncoder().encode(data);
2891
- const dPtr = allocAndCopy(wasm, d);
2952
+ const { ptr: dPtr, len: dLen } = allocAndCopy(wasm, d);
2892
2953
  const hPtr = wasm._hd_alloc(32);
2893
2954
  try {
2894
- checkLength(wasm._hd_hash_keccak256(dPtr, d.length, hPtr, 32), 32);
2955
+ checkLength(wasm._hd_hash_keccak256(dPtr, dLen, hPtr, 32), 32);
2895
2956
  return readBytes(wasm, hPtr, 32);
2896
2957
  } finally {
2897
2958
  wasm._hd_dealloc(dPtr);
@@ -2983,8 +3044,8 @@ function createModule(wasm) {
2983
3044
  const namePtr = allocString(wasm, domain.name || '');
2984
3045
  const versionPtr = allocString(wasm, domain.version || '');
2985
3046
  const contractPtr = allocString(wasm, domain.verifyingContract || '');
2986
- const structHashPtr = allocAndCopy(wasm, structHash);
2987
- const keyPtr = allocAndCopy(wasm, privateKey);
3047
+ const { ptr: structHashPtr, len: structHashLen } = allocAndCopy(wasm, structHash);
3048
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
2988
3049
  const sigPtr = wasm._hd_alloc(65);
2989
3050
  try {
2990
3051
  const result = signTypedDataFn(namePtr, versionPtr, BigInt(domain.chainId || 0), contractPtr, structHashPtr, keyPtr, sigPtr, 65);
@@ -3017,14 +3078,14 @@ function createModule(wasm) {
3017
3078
  const msgBytes = ethereumMessageBytes(message);
3018
3079
  V.string(signature, 'signature');
3019
3080
  const sigBytes = hexOrNull(signature);
3020
- if (sigBytes === null || sigBytes.length !== 65 || ![0, 1, 27, 28].includes(sigBytes[64])) {
3081
+ if (sigBytes === null || V.byteLength(sigBytes) !== 65 || ![0, 1, 27, 28].includes(sigBytes[64])) {
3021
3082
  throw new HDWalletError(ErrorCode.INVALID_SIGNATURE, 'signature must be 65 bytes r || s || v with v 0, 1, 27 or 28');
3022
3083
  }
3023
- const msgPtr = allocAndCopy(wasm, msgBytes);
3024
- const sigPtr = allocAndCopy(wasm, sigBytes);
3084
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, msgBytes);
3085
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, sigBytes);
3025
3086
  const addrPtr = wasm._hd_alloc(64);
3026
3087
  try {
3027
- const result = recoverFn(msgPtr, msgBytes.length, sigPtr, sigBytes.length, addrPtr, 64);
3088
+ const result = recoverFn(msgPtr, msgLen, sigPtr, sigLen, addrPtr, 64);
3028
3089
  checkResult(result);
3029
3090
  return readString(wasm, addrPtr);
3030
3091
  } finally {
@@ -3047,10 +3108,10 @@ function createModule(wasm) {
3047
3108
  hashMessage(message) {
3048
3109
  const hashFn = requireWasmFunction(wasm, '_hd_eth_hash_message');
3049
3110
  const msgBytes = ethereumMessageBytes(message);
3050
- const msgPtr = allocAndCopy(wasm, msgBytes);
3111
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, msgBytes);
3051
3112
  const hashPtr = wasm._hd_alloc(32);
3052
3113
  try {
3053
- const result = hashFn(msgPtr, msgBytes.length, hashPtr);
3114
+ const result = hashFn(msgPtr, msgLen, hashPtr);
3054
3115
  checkResult(result);
3055
3116
  return readBytes(wasm, hashPtr, 32);
3056
3117
  } finally {
@@ -3067,7 +3128,7 @@ function createModule(wasm) {
3067
3128
  sign(privateKey) {
3068
3129
  const signFn = requireWasmFunction(wasm, '_hd_eth_tx_sign');
3069
3130
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
3070
- const keyPtr = allocAndCopy(wasm, privateKey);
3131
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3071
3132
  try {
3072
3133
  const result = signFn(this._handle, keyPtr);
3073
3134
  checkResult(result);
@@ -3116,31 +3177,54 @@ function createModule(wasm) {
3116
3177
  };
3117
3178
  },
3118
3179
 
3119
- // Validate the fields every transaction type shares. The native setters
3120
- // take uint64 (a 256-bit value as 32 bytes); a larger or negative number
3121
- // would wrap modulo 2^64, so it is refused here.
3122
- _commonFields(params) {
3180
+ // Read each field of `params` once (a getter or a Uint8Array subclass
3181
+ // could answer differently on a second read: a longer `length` put heap
3182
+ // bytes into the signed data) and check it. The nonce and the chain ID
3183
+ // have no defaults: a missing nonce signed nonce 0, a missing chain ID
3184
+ // chain 1. The native setters take uint64 (a 256-bit value as 32 bytes);
3185
+ // a larger or negative number would wrap modulo 2^64, so it is refused.
3186
+ _readParams(params) {
3123
3187
  if (params === null || typeof params !== 'object') {
3124
3188
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'params must be an object');
3125
3189
  }
3126
- if (params.to !== undefined && params.to !== null && params.to !== '') V.string(params.to, 'to');
3127
- if (params.data !== undefined && params.data !== null) V.bytes(params.data, 'data');
3128
- return {
3129
- nonce: V.unsignedBigInt(params.nonce || 0, 'nonce', 64),
3130
- gasLimit: params.gasLimit === undefined ? undefined : V.unsignedBigInt(params.gasLimit, 'gasLimit', 64),
3131
- value: params.value === undefined ? undefined : V.unsignedBigInt(params.value, 'value', 256),
3190
+ const {
3191
+ nonce, chainId, gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas, to, value, data,
3192
+ } = params;
3193
+ if (nonce === undefined || nonce === null) {
3194
+ throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'nonce is required');
3195
+ }
3196
+ if (chainId === undefined || chainId === null) {
3197
+ throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'chainId is required');
3198
+ }
3199
+ const fields = {
3200
+ nonce: V.unsignedBigInt(nonce, 'nonce', 64),
3201
+ chainId: V.unsignedBigInt(chainId, 'chainId', 64),
3202
+ gasLimit: gasLimit === undefined ? undefined : V.unsignedBigInt(gasLimit, 'gasLimit', 64),
3203
+ gasPrice: gasPrice === undefined ? undefined : V.unsignedBigInt(gasPrice, 'gasPrice', 64),
3204
+ maxFeePerGas,
3205
+ maxPriorityFeePerGas,
3206
+ to: to === undefined || to === null || to === '' ? undefined : V.string(to, 'to'),
3207
+ value: value === undefined ? undefined : V.unsignedBigInt(value, 'value', 256),
3208
+ // A copy made from the array's own bytes and length.
3209
+ data: data === undefined || data === null ? undefined : new Uint8Array(V.bytes(data, 'data')),
3132
3210
  };
3211
+ // EIP-2294: 1 <= chainId <= floor(MAX_UINT64 / 2) - 36, so a legacy
3212
+ // v = 2 * chainId + 35 + recovery id fits 64 bits.
3213
+ if (fields.chainId === 0n || fields.chainId > ETH_MAX_CHAIN_ID) {
3214
+ throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, `chainId must be from 1 to ${ETH_MAX_CHAIN_ID}`);
3215
+ }
3216
+ return fields;
3133
3217
  },
3134
3218
 
3135
- _setCommonFields(handle, params, fields) {
3219
+ _setCommonFields(handle, fields) {
3136
3220
  requireWasmFunction(wasm, '_hd_eth_tx_set_nonce')(handle, fields.nonce);
3137
3221
  if (fields.gasLimit !== undefined) {
3138
3222
  requireWasmFunction(wasm, '_hd_eth_tx_set_gas_limit')(handle, fields.gasLimit);
3139
3223
  }
3140
3224
 
3141
- if (params.to) {
3225
+ if (fields.to !== undefined) {
3142
3226
  const setToFn = requireWasmFunction(wasm, '_hd_eth_tx_set_to_hex');
3143
- const toPtr = allocString(wasm, params.to);
3227
+ const toPtr = allocString(wasm, fields.to);
3144
3228
  try {
3145
3229
  // An address that does not parse fails here. Without a recipient
3146
3230
  // the transaction does not serialize or sign (it would otherwise
@@ -3156,7 +3240,7 @@ function createModule(wasm) {
3156
3240
  const setValueFn = requireWasmFunction(wasm, '_hd_eth_tx_set_value_256');
3157
3241
  const valueBytes = new Uint8Array(32);
3158
3242
  for (let i = 31, v = fields.value; i >= 0 && v > 0n; i--, v >>= 8n) valueBytes[i] = Number(v & 0xffn);
3159
- const valuePtr = allocAndCopy(wasm, valueBytes);
3243
+ const { ptr: valuePtr, len: valueLen } = allocAndCopy(wasm, valueBytes);
3160
3244
  try {
3161
3245
  setValueFn(handle, valuePtr);
3162
3246
  } finally {
@@ -3164,31 +3248,32 @@ function createModule(wasm) {
3164
3248
  }
3165
3249
  }
3166
3250
 
3167
- if (params.data) {
3251
+ if (fields.data !== undefined) {
3168
3252
  const setDataFn = requireWasmFunction(wasm, '_hd_eth_tx_set_data');
3169
- const dataPtr = allocAndCopy(wasm, params.data);
3253
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, fields.data);
3170
3254
  try {
3171
- setDataFn(handle, dataPtr, params.data.length);
3255
+ setDataFn(handle, dataPtr, dataLen);
3172
3256
  } finally {
3173
3257
  wasm._hd_dealloc(dataPtr);
3174
3258
  }
3175
3259
  }
3176
3260
  },
3177
3261
 
3262
+ // EIP-1559 transaction. nonce, chainId (>= 1), gasLimit, maxFeePerGas
3263
+ // and maxPriorityFeePerGas are required.
3178
3264
  createEIP1559(params) {
3179
3265
  const createFn = requireWasmFunction(wasm, '_hd_eth_tx_create_eip1559');
3180
- const fields = this._commonFields(params);
3266
+ const fields = this._readParams(params);
3181
3267
  if (fields.gasLimit === undefined) {
3182
3268
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'gasLimit is required');
3183
3269
  }
3184
- const chainId = V.unsignedBigInt(params.chainId, 'chainId', 64);
3185
- const maxPriorityFeePerGas = V.unsignedBigInt(params.maxPriorityFeePerGas, 'maxPriorityFeePerGas', 64);
3186
- const maxFeePerGas = V.unsignedBigInt(params.maxFeePerGas, 'maxFeePerGas', 64);
3187
- const handle = createFn(chainId, maxPriorityFeePerGas, maxFeePerGas);
3270
+ const maxPriorityFeePerGas = V.unsignedBigInt(fields.maxPriorityFeePerGas, 'maxPriorityFeePerGas', 64);
3271
+ const maxFeePerGas = V.unsignedBigInt(fields.maxFeePerGas, 'maxFeePerGas', 64);
3272
+ const handle = createFn(fields.chainId, maxPriorityFeePerGas, maxFeePerGas);
3188
3273
  if (!handle) throw new HDWalletError(ErrorCode.NOT_SUPPORTED);
3189
3274
 
3190
3275
  try {
3191
- this._setCommonFields(handle, params, fields);
3276
+ this._setCommonFields(handle, fields);
3192
3277
  } catch (error) {
3193
3278
  requireWasmFunction(wasm, '_hd_eth_tx_destroy')(handle);
3194
3279
  throw error;
@@ -3197,19 +3282,18 @@ function createModule(wasm) {
3197
3282
  return this._makeTxObj(handle);
3198
3283
  },
3199
3284
 
3200
- // Legacy (EIP-155) transaction. Before 2.1.1 every field but chainId was
3201
- // ignored, so the declared to/value/gasPrice never reached the signed bytes.
3285
+ // Legacy (EIP-155) transaction. nonce and chainId (>= 1) are required.
3286
+ // Before 2.1.1 every field but chainId was ignored, so the declared
3287
+ // to/value/gasPrice never reached the signed bytes.
3202
3288
  create(params = {}) {
3203
3289
  const createFn = requireWasmFunction(wasm, '_hd_eth_tx_create');
3204
- const fields = this._commonFields(params);
3205
- const chainId = V.unsignedBigInt(params.chainId || 1, 'chainId', 64);
3206
- const gasPrice = params.gasPrice === undefined ? undefined : V.unsignedBigInt(params.gasPrice, 'gasPrice', 64);
3207
- const handle = createFn(chainId);
3290
+ const fields = this._readParams(params);
3291
+ const handle = createFn(fields.chainId);
3208
3292
  if (!handle) throw new HDWalletError(ErrorCode.NOT_SUPPORTED);
3209
3293
 
3210
3294
  try {
3211
- if (gasPrice !== undefined) requireWasmFunction(wasm, '_hd_eth_tx_set_gas_price')(handle, gasPrice);
3212
- this._setCommonFields(handle, params, fields);
3295
+ if (fields.gasPrice !== undefined) requireWasmFunction(wasm, '_hd_eth_tx_set_gas_price')(handle, fields.gasPrice);
3296
+ this._setCommonFields(handle, fields);
3213
3297
  } catch (error) {
3214
3298
  requireWasmFunction(wasm, '_hd_eth_tx_destroy')(handle);
3215
3299
  throw error;
@@ -3221,9 +3305,9 @@ function createModule(wasm) {
3221
3305
  parse(data) {
3222
3306
  const parseFn = requireWasmFunction(wasm, '_hd_eth_tx_parse');
3223
3307
  V.bytes(data, 'data');
3224
- const dataPtr = allocAndCopy(wasm, data);
3308
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
3225
3309
  try {
3226
- const handle = parseFn(dataPtr, data.length);
3310
+ const handle = parseFn(dataPtr, dataLen);
3227
3311
  if (!handle) throw new HDWalletError(ErrorCode.INVALID_TRANSACTION);
3228
3312
  return this._makeTxObj(handle);
3229
3313
  } finally {
@@ -3246,11 +3330,11 @@ function createModule(wasm) {
3246
3330
  const getAddressFn = requireWasmFunction(wasm, '_hd_cosmos_address');
3247
3331
  V.publicKey(wasm, publicKey, Curve.SECP256K1, 'publicKey', [33]);
3248
3332
  V.string(prefix, 'prefix');
3249
- const pubPtr = allocAndCopy(wasm, publicKey);
3333
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
3250
3334
  const prefixPtr = allocString(wasm, prefix);
3251
3335
  const outPtr = wasm._hd_alloc(128);
3252
3336
  try {
3253
- const result = getAddressFn(pubPtr, publicKey.length, prefixPtr, outPtr, 128);
3337
+ const result = getAddressFn(pubPtr, pubLen, prefixPtr, outPtr, 128);
3254
3338
  checkResult(result);
3255
3339
  return readString(wasm, outPtr);
3256
3340
  } finally {
@@ -3301,11 +3385,11 @@ function createModule(wasm) {
3301
3385
  const jsonStr = JSON.stringify(doc);
3302
3386
  const enc = new TextEncoder();
3303
3387
  const docBytes = enc.encode(jsonStr);
3304
- const docPtr = allocAndCopy(wasm, docBytes);
3305
- const keyPtr = allocAndCopy(wasm, privateKey);
3388
+ const { ptr: docPtr, len: docLen } = allocAndCopy(wasm, docBytes);
3389
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3306
3390
  const sigPtr = wasm._hd_alloc(64);
3307
3391
  try {
3308
- const result = signAminoFn(docPtr, docBytes.length, keyPtr, sigPtr, 64);
3392
+ const result = signAminoFn(docPtr, docLen, keyPtr, sigPtr, 64);
3309
3393
  checkResult(result);
3310
3394
  return readBytes(wasm, sigPtr, 64);
3311
3395
  } finally {
@@ -3324,11 +3408,11 @@ function createModule(wasm) {
3324
3408
  const [signDocBytes, privateKey] = args;
3325
3409
  V.bytes(signDocBytes, 'signDocBytes');
3326
3410
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
3327
- const docPtr = allocAndCopy(wasm, signDocBytes);
3328
- const keyPtr = allocAndCopy(wasm, privateKey);
3411
+ const { ptr: docPtr, len: docLen } = allocAndCopy(wasm, signDocBytes);
3412
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3329
3413
  const sigPtr = wasm._hd_alloc(64);
3330
3414
  try {
3331
- const result = signDirectFn(docPtr, signDocBytes.length, keyPtr, sigPtr, 64);
3415
+ const result = signDirectFn(docPtr, docLen, keyPtr, sigPtr, 64);
3332
3416
  checkResult(result);
3333
3417
  return readBytes(wasm, sigPtr, 64);
3334
3418
  } finally {
@@ -3342,9 +3426,10 @@ function createModule(wasm) {
3342
3426
  if (args.length !== 5) {
3343
3427
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'signDirect takes (signDocBytes, privateKey) or (bodyBytes, authInfoBytes, chainId, accountNumber, privateKey)');
3344
3428
  }
3345
- const [bodyBytes, authInfoBytes, chainId, accountNumber, privateKey] = args;
3346
- V.bytes(bodyBytes, 'bodyBytes');
3347
- V.bytes(authInfoBytes, 'authInfoBytes');
3429
+ const [bodyArg, authInfoArg, chainId, accountNumber, privateKey] = args;
3430
+ // Copies read once: the lengths below size the SignDoc.
3431
+ const bodyBytes = V.snapshot(bodyArg, 'bodyBytes');
3432
+ const authInfoBytes = V.snapshot(authInfoArg, 'authInfoBytes');
3348
3433
  V.string(chainId, 'chainId');
3349
3434
  V.unsignedBigInt(accountNumber, 'accountNumber', 64);
3350
3435
  V.privateKey(wasm, privateKey, Curve.SECP256K1);
@@ -3366,11 +3451,11 @@ function createModule(wasm) {
3366
3451
  for (let i = 0; i < 8; i++) {
3367
3452
  signDoc[off + i] = Number((accNum >> BigInt(i * 8)) & 0xFFn);
3368
3453
  }
3369
- const docPtr = allocAndCopy(wasm, signDoc);
3370
- const keyPtr = allocAndCopy(wasm, privateKey);
3454
+ const { ptr: docPtr, len: docLen } = allocAndCopy(wasm, signDoc);
3455
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3371
3456
  const sigPtr = wasm._hd_alloc(64);
3372
3457
  try {
3373
- const result = signDirectFn(docPtr, signDoc.length, keyPtr, sigPtr, 64);
3458
+ const result = signDirectFn(docPtr, docLen, keyPtr, sigPtr, 64);
3374
3459
  checkResult(result);
3375
3460
  return readBytes(wasm, sigPtr, 64);
3376
3461
  } finally {
@@ -3387,12 +3472,12 @@ function createModule(wasm) {
3387
3472
  V.bytes(message, 'message');
3388
3473
  V.bytes(signature, 'signature');
3389
3474
  V.bytes(publicKey, 'publicKey');
3390
- if (signature.length !== 64 || publicKey.length !== 33) return false;
3391
- const msgPtr = allocAndCopy(wasm, message);
3392
- const sigPtr = allocAndCopy(wasm, signature);
3393
- const pubPtr = allocAndCopy(wasm, publicKey);
3475
+ if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 33) return false;
3476
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
3477
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
3478
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
3394
3479
  try {
3395
- return verifyFn(msgPtr, message.length, sigPtr, signature.length, pubPtr, publicKey.length) === 1;
3480
+ return verifyFn(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
3396
3481
  } finally {
3397
3482
  wasm._hd_dealloc(msgPtr);
3398
3483
  wasm._hd_dealloc(sigPtr);
@@ -3418,10 +3503,10 @@ function createModule(wasm) {
3418
3503
  const getAddressFn = requireWasmFunction(wasm, '_hd_sol_address');
3419
3504
  // Any 32 bytes: Solana addresses include off-curve program addresses.
3420
3505
  V.bytesOfLength(publicKey, 32, 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
3421
- const pubPtr = allocAndCopy(wasm, publicKey);
3506
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
3422
3507
  const outPtr = wasm._hd_alloc(64);
3423
3508
  try {
3424
- const result = getAddressFn(pubPtr, publicKey.length, outPtr, 64);
3509
+ const result = getAddressFn(pubPtr, pubLen, outPtr, 64);
3425
3510
  checkResult(result);
3426
3511
  return readString(wasm, outPtr);
3427
3512
  } finally {
@@ -3445,11 +3530,11 @@ function createModule(wasm) {
3445
3530
  const signFn = requireWasmFunction(wasm, '_hd_sol_sign_message');
3446
3531
  V.bytes(message, 'message');
3447
3532
  V.privateKey(wasm, privateKey, Curve.ED25519);
3448
- const msgPtr = allocAndCopy(wasm, message);
3449
- const keyPtr = allocAndCopy(wasm, privateKey);
3533
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
3534
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3450
3535
  const sigPtr = wasm._hd_alloc(64);
3451
3536
  try {
3452
- const result = signFn(msgPtr, message.length, keyPtr, sigPtr, 64);
3537
+ const result = signFn(msgPtr, msgLen, keyPtr, sigPtr, 64);
3453
3538
  checkResult(result);
3454
3539
  return readBytes(wasm, sigPtr, 64);
3455
3540
  } finally {
@@ -3465,12 +3550,12 @@ function createModule(wasm) {
3465
3550
  V.bytes(message, 'message');
3466
3551
  V.bytes(signature, 'signature');
3467
3552
  V.bytes(publicKey, 'publicKey');
3468
- if (signature.length !== 64 || publicKey.length !== 32) return false;
3469
- const msgPtr = allocAndCopy(wasm, message);
3470
- const sigPtr = allocAndCopy(wasm, signature);
3471
- const pubPtr = allocAndCopy(wasm, publicKey);
3553
+ if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 32) return false;
3554
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
3555
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
3556
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
3472
3557
  try {
3473
- return verifyFn(msgPtr, message.length, sigPtr, signature.length, pubPtr, publicKey.length) === 1;
3558
+ return verifyFn(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
3474
3559
  } finally {
3475
3560
  wasm._hd_dealloc(msgPtr);
3476
3561
  wasm._hd_dealloc(sigPtr);
@@ -3495,7 +3580,7 @@ function createModule(wasm) {
3495
3580
  derivePubkey(privateKey) {
3496
3581
  const fn = requireWasmFunction(wasm, '_hd_sol_derive_pubkey');
3497
3582
  V.privateKey(wasm, privateKey, Curve.ED25519);
3498
- const keyPtr = allocAndCopy(wasm, privateKey);
3583
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3499
3584
  const outPtr = wasm._hd_alloc(32);
3500
3585
  try {
3501
3586
  const result = fn(keyPtr, outPtr, 32);
@@ -3513,25 +3598,31 @@ function createModule(wasm) {
3513
3598
  // The export reads a 32-byte program id; Solana allows up to 16 seeds
3514
3599
  // of up to 32 bytes.
3515
3600
  V.bytesOfLength(programId, 32, 'programId', ErrorCode.INVALID_PUBLIC_KEY);
3516
- if (!Array.isArray(seeds) || seeds.length > 16) {
3601
+ // The array and each seed are read once (an array or a Proxy could
3602
+ // answer a second read differently): the copies' lengths go to the heap.
3603
+ const count = Array.isArray(seeds) ? seeds.length : -1;
3604
+ if (!Number.isInteger(count) || count < 0 || count > 16) {
3517
3605
  throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'seeds must be an array of at most 16 seeds');
3518
3606
  }
3519
- for (const seed of seeds) V.bytesInRange(seed, 0, 32, 'seed');
3520
- const progPtr = allocAndCopy(wasm, programId);
3607
+ const seedCopies = [];
3608
+ for (let i = 0; i < count; i++) {
3609
+ seedCopies.push(V.snapshot(V.bytesInRange(seeds[i], 0, 32, 'seed'), 'seed'));
3610
+ }
3611
+ const { ptr: progPtr } = allocAndCopy(wasm, programId);
3521
3612
  let totalLen = 0;
3522
- for (const s of seeds) totalLen += s.length;
3613
+ for (const s of seedCopies) totalLen += s.length;
3523
3614
  const flatSeeds = new Uint8Array(totalLen);
3524
3615
  let off = 0;
3525
- for (const s of seeds) { flatSeeds.set(s, off); off += s.length; }
3526
- const seedsPtr = totalLen > 0 ? allocAndCopy(wasm, flatSeeds) : 0;
3527
- const seedLensPtr = wasm._hd_alloc(seeds.length * 4);
3528
- for (let i = 0; i < seeds.length; i++) {
3529
- wasm.setValue(seedLensPtr + i * 4, seeds[i].length, 'i32');
3616
+ for (const s of seedCopies) { flatSeeds.set(s, off); off += s.length; }
3617
+ const { ptr: seedsPtr } = totalLen > 0 ? allocAndCopy(wasm, flatSeeds) : { ptr: 0 };
3618
+ const seedLensPtr = wasm._hd_alloc(seedCopies.length * 4);
3619
+ for (let i = 0; i < seedCopies.length; i++) {
3620
+ wasm.setValue(seedLensPtr + i * 4, seedCopies[i].length, 'i32');
3530
3621
  }
3531
3622
  const addrPtr = wasm._hd_alloc(64);
3532
3623
  const bumpPtr = wasm._hd_alloc(1);
3533
3624
  try {
3534
- const result = fn(progPtr, seedsPtr || 0, totalLen, seedLensPtr, seeds.length, addrPtr, 64, bumpPtr);
3625
+ const result = fn(progPtr, seedsPtr || 0, totalLen, seedLensPtr, seedCopies.length, addrPtr, 64, bumpPtr);
3535
3626
  checkResult(result);
3536
3627
  return {
3537
3628
  address: readString(wasm, addrPtr),
@@ -3576,10 +3667,10 @@ function createModule(wasm) {
3576
3667
  const getAddressFn = requireWasmFunction(wasm, '_hd_dot_address');
3577
3668
  V.bytesOfLength(publicKey, 32, 'publicKey', ErrorCode.INVALID_PUBLIC_KEY);
3578
3669
  V.integer(ss58Prefix, 'ss58Prefix', 0, 16383);
3579
- const pubPtr = allocAndCopy(wasm, publicKey);
3670
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
3580
3671
  const outPtr = wasm._hd_alloc(64);
3581
3672
  try {
3582
- const result = getAddressFn(pubPtr, publicKey.length, ss58Prefix, outPtr, 64);
3673
+ const result = getAddressFn(pubPtr, pubLen, ss58Prefix, outPtr, 64);
3583
3674
  checkResult(result);
3584
3675
  return readString(wasm, outPtr);
3585
3676
  } finally {
@@ -3637,7 +3728,7 @@ function createModule(wasm) {
3637
3728
  derivePubkey(privateKey) {
3638
3729
  const fn = requireWasmFunction(wasm, '_hd_dot_derive_pubkey');
3639
3730
  V.privateKey(wasm, privateKey, Curve.ED25519);
3640
- const keyPtr = allocAndCopy(wasm, privateKey);
3731
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3641
3732
  const outPtr = wasm._hd_alloc(32);
3642
3733
  try {
3643
3734
  const result = fn(keyPtr, outPtr, 32);
@@ -3654,11 +3745,11 @@ function createModule(wasm) {
3654
3745
  const signFn = requireWasmFunction(wasm, '_hd_dot_sign_message');
3655
3746
  V.bytes(message, 'message');
3656
3747
  V.privateKey(wasm, privateKey, Curve.ED25519);
3657
- const msgPtr = allocAndCopy(wasm, message);
3658
- const keyPtr = allocAndCopy(wasm, privateKey);
3748
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
3749
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, privateKey);
3659
3750
  const sigPtr = wasm._hd_alloc(64);
3660
3751
  try {
3661
- const result = signFn(msgPtr, message.length, keyPtr, sigPtr, 64);
3752
+ const result = signFn(msgPtr, msgLen, keyPtr, sigPtr, 64);
3662
3753
  checkResult(result);
3663
3754
  return readBytes(wasm, sigPtr, 64);
3664
3755
  } finally {
@@ -3674,12 +3765,12 @@ function createModule(wasm) {
3674
3765
  V.bytes(message, 'message');
3675
3766
  V.bytes(signature, 'signature');
3676
3767
  V.bytes(publicKey, 'publicKey');
3677
- if (signature.length !== 64 || publicKey.length !== 32) return false;
3678
- const msgPtr = allocAndCopy(wasm, message);
3679
- const sigPtr = allocAndCopy(wasm, signature);
3680
- const pubPtr = allocAndCopy(wasm, publicKey);
3768
+ if (V.byteLength(signature) !== 64 || V.byteLength(publicKey) !== 32) return false;
3769
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
3770
+ const { ptr: sigPtr, len: sigLen } = allocAndCopy(wasm, signature);
3771
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
3681
3772
  try {
3682
- return verifyFn(msgPtr, message.length, sigPtr, signature.length, pubPtr, publicKey.length) === 1;
3773
+ return verifyFn(msgPtr, msgLen, sigPtr, sigLen, pubPtr, pubLen) === 1;
3683
3774
  } finally {
3684
3775
  wasm._hd_dealloc(msgPtr);
3685
3776
  wasm._hd_dealloc(sigPtr);
@@ -3775,10 +3866,10 @@ function createModule(wasm) {
3775
3866
 
3776
3867
  async signTransaction(path, transaction) {
3777
3868
  const pathPtr = allocString(wasm, path);
3778
- const txPtr = allocAndCopy(wasm, transaction);
3869
+ const { ptr: txPtr, len: txLen } = allocAndCopy(wasm, transaction);
3779
3870
  const sigPtr = wasm._hd_alloc(128);
3780
3871
  try {
3781
- const result = wasm._hd_hw_sign_transaction(this._handle, pathPtr, txPtr, transaction.length, sigPtr, 128);
3872
+ const result = wasm._hd_hw_sign_transaction(this._handle, pathPtr, txPtr, txLen, sigPtr, 128);
3782
3873
  checkResult(result);
3783
3874
  return readBytes(wasm, sigPtr, 64);
3784
3875
  } finally {
@@ -3843,13 +3934,13 @@ function createModule(wasm) {
3843
3934
  addWallet(seed, name) {
3844
3935
  const addWalletFn = getWasmFunction(wasm, '_hd_keyring_add_wallet');
3845
3936
  if (!addWalletFn) return '';
3846
- const seedPtr = allocAndCopy(wasm, seed);
3937
+ const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
3847
3938
  const namePtr = name ? allocString(wasm, name) : 0;
3848
3939
  try {
3849
- const ptr = addWalletFn(this._handle, seedPtr, seed.length, namePtr);
3940
+ const ptr = addWalletFn(this._handle, seedPtr, seedLen, namePtr);
3850
3941
  return readString(wasm, ptr);
3851
3942
  } finally {
3852
- wasm._hd_secure_wipe(seedPtr, seed.length);
3943
+ wasm._hd_secure_wipe(seedPtr, seedLen);
3853
3944
  wasm._hd_dealloc(seedPtr);
3854
3945
  if (namePtr) wasm._hd_dealloc(namePtr);
3855
3946
  }
@@ -3887,10 +3978,10 @@ function createModule(wasm) {
3887
3978
  const signTransactionFn = requireWasmFunction(wasm, '_hd_keyring_sign_transaction', ErrorCode.NOT_SUPPORTED, 'Keyring is not available in the WASM build');
3888
3979
  const idPtr = allocString(wasm, walletId);
3889
3980
  const pathPtr = allocString(wasm, path);
3890
- const txPtr = allocAndCopy(wasm, transaction);
3981
+ const { ptr: txPtr, len: txLen } = allocAndCopy(wasm, transaction);
3891
3982
  const sigPtr = wasm._hd_alloc(128);
3892
3983
  try {
3893
- const result = signTransactionFn(this._handle, idPtr, pathPtr, txPtr, transaction.length, sigPtr, 128);
3984
+ const result = signTransactionFn(this._handle, idPtr, pathPtr, txPtr, txLen, sigPtr, 128);
3894
3985
  checkResult(result);
3895
3986
  return readBytes(wasm, sigPtr, 64);
3896
3987
  } finally {
@@ -3905,10 +3996,10 @@ function createModule(wasm) {
3905
3996
  const signMessageFn = requireWasmFunction(wasm, '_hd_keyring_sign_message', ErrorCode.NOT_SUPPORTED, 'Keyring is not available in the WASM build');
3906
3997
  const idPtr = allocString(wasm, walletId);
3907
3998
  const pathPtr = allocString(wasm, path);
3908
- const msgPtr = allocAndCopy(wasm, message);
3999
+ const { ptr: msgPtr, len: msgLen } = allocAndCopy(wasm, message);
3909
4000
  const sigPtr = wasm._hd_alloc(128);
3910
4001
  try {
3911
- const result = signMessageFn(this._handle, idPtr, pathPtr, msgPtr, message.length, sigPtr, 128);
4002
+ const result = signMessageFn(this._handle, idPtr, pathPtr, msgPtr, msgLen, sigPtr, 128);
3912
4003
  checkResult(result);
3913
4004
  return readBytes(wasm, sigPtr, 64);
3914
4005
  } finally {
@@ -3943,10 +4034,10 @@ function createModule(wasm) {
3943
4034
  const utils = {
3944
4035
  // Hashing
3945
4036
  sha256(data) {
3946
- const dataPtr = allocAndCopy(wasm, data);
4037
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
3947
4038
  const hashPtr = wasm._hd_alloc(32);
3948
4039
  try {
3949
- checkLength(wasm._hd_hash_sha256(dataPtr, data.length, hashPtr, 32), 32);
4040
+ checkLength(wasm._hd_hash_sha256(dataPtr, dataLen, hashPtr, 32), 32);
3950
4041
  return readBytes(wasm, hashPtr, 32);
3951
4042
  } finally {
3952
4043
  wasm._hd_dealloc(dataPtr);
@@ -3955,10 +4046,10 @@ function createModule(wasm) {
3955
4046
  },
3956
4047
 
3957
4048
  sha512(data) {
3958
- const dataPtr = allocAndCopy(wasm, data);
4049
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
3959
4050
  const hashPtr = wasm._hd_alloc(64);
3960
4051
  try {
3961
- checkLength(wasm._hd_hash_sha512(dataPtr, data.length, hashPtr, 64), 64);
4052
+ checkLength(wasm._hd_hash_sha512(dataPtr, dataLen, hashPtr, 64), 64);
3962
4053
  return readBytes(wasm, hashPtr, 64);
3963
4054
  } finally {
3964
4055
  wasm._hd_dealloc(dataPtr);
@@ -3967,10 +4058,10 @@ function createModule(wasm) {
3967
4058
  },
3968
4059
 
3969
4060
  keccak256(data) {
3970
- const dataPtr = allocAndCopy(wasm, data);
4061
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
3971
4062
  const hashPtr = wasm._hd_alloc(32);
3972
4063
  try {
3973
- checkLength(wasm._hd_hash_keccak256(dataPtr, data.length, hashPtr, 32), 32);
4064
+ checkLength(wasm._hd_hash_keccak256(dataPtr, dataLen, hashPtr, 32), 32);
3974
4065
  return readBytes(wasm, hashPtr, 32);
3975
4066
  } finally {
3976
4067
  wasm._hd_dealloc(dataPtr);
@@ -3979,10 +4070,10 @@ function createModule(wasm) {
3979
4070
  },
3980
4071
 
3981
4072
  ripemd160(data) {
3982
- const dataPtr = allocAndCopy(wasm, data);
4073
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
3983
4074
  const hashPtr = wasm._hd_alloc(20);
3984
4075
  try {
3985
- checkLength(wasm._hd_hash_ripemd160(dataPtr, data.length, hashPtr, 20), 20);
4076
+ checkLength(wasm._hd_hash_ripemd160(dataPtr, dataLen, hashPtr, 20), 20);
3986
4077
  return readBytes(wasm, hashPtr, 20);
3987
4078
  } finally {
3988
4079
  wasm._hd_dealloc(dataPtr);
@@ -3991,10 +4082,10 @@ function createModule(wasm) {
3991
4082
  },
3992
4083
 
3993
4084
  hash160(data) {
3994
- const dataPtr = allocAndCopy(wasm, data);
4085
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
3995
4086
  const hashPtr = wasm._hd_alloc(20);
3996
4087
  try {
3997
- checkLength(wasm._hd_hash_hash160(dataPtr, data.length, hashPtr, 20), 20);
4088
+ checkLength(wasm._hd_hash_hash160(dataPtr, dataLen, hashPtr, 20), 20);
3998
4089
  return readBytes(wasm, hashPtr, 20);
3999
4090
  } finally {
4000
4091
  wasm._hd_dealloc(dataPtr);
@@ -4005,10 +4096,10 @@ function createModule(wasm) {
4005
4096
  blake2b(data, outputLength = 32) {
4006
4097
  // The export checks the size against its 64-byte buffer.
4007
4098
  V.integer(outputLength, 'outputLength', 0, 0x7fffffff);
4008
- const dataPtr = allocAndCopy(wasm, data);
4099
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
4009
4100
  const hashPtr = wasm._hd_alloc(64);
4010
4101
  try {
4011
- checkLength(wasm._hd_hash_blake2b(dataPtr, data.length, hashPtr, 64, outputLength), outputLength);
4102
+ checkLength(wasm._hd_hash_blake2b(dataPtr, dataLen, hashPtr, 64, outputLength), outputLength);
4012
4103
  return readBytes(wasm, hashPtr, outputLength);
4013
4104
  } finally {
4014
4105
  wasm._hd_dealloc(dataPtr);
@@ -4019,10 +4110,10 @@ function createModule(wasm) {
4019
4110
  blake2s(data, outputLength = 32) {
4020
4111
  // The export checks the size against its 32-byte buffer.
4021
4112
  V.integer(outputLength, 'outputLength', 0, 0x7fffffff);
4022
- const dataPtr = allocAndCopy(wasm, data);
4113
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
4023
4114
  const hashPtr = wasm._hd_alloc(32);
4024
4115
  try {
4025
- checkLength(wasm._hd_hash_blake2s(dataPtr, data.length, hashPtr, 32, outputLength), outputLength);
4116
+ checkLength(wasm._hd_hash_blake2s(dataPtr, dataLen, hashPtr, 32, outputLength), outputLength);
4026
4117
  return readBytes(wasm, hashPtr, outputLength);
4027
4118
  } finally {
4028
4119
  wasm._hd_dealloc(dataPtr);
@@ -4037,15 +4128,15 @@ function createModule(wasm) {
4037
4128
  V.bytes(info, 'info');
4038
4129
  // The export refuses more than 255 blocks of output.
4039
4130
  V.integer(length, 'length', 1, 0x3fffffff);
4040
- const ikmPtr = allocAndCopy(wasm, ikm);
4041
- const saltPtr = allocAndCopy(wasm, salt);
4042
- const infoPtr = allocAndCopy(wasm, info);
4131
+ const { ptr: ikmPtr, len: ikmLen } = allocAndCopy(wasm, ikm);
4132
+ const { ptr: saltPtr, len: saltLen } = allocAndCopy(wasm, salt);
4133
+ const { ptr: infoPtr, len: infoLen } = allocAndCopy(wasm, info);
4043
4134
  const outPtr = wasm._hd_alloc(length);
4044
4135
  try {
4045
- checkLength(wasm._hd_kdf_hkdf(ikmPtr, ikm.length, saltPtr, salt.length, infoPtr, info.length, outPtr, length), length);
4136
+ checkLength(wasm._hd_kdf_hkdf(ikmPtr, ikmLen, saltPtr, saltLen, infoPtr, infoLen, outPtr, length), length);
4046
4137
  return readBytes(wasm, outPtr, length);
4047
4138
  } finally {
4048
- wasm._hd_secure_wipe(ikmPtr, ikm.length);
4139
+ wasm._hd_secure_wipe(ikmPtr, ikmLen);
4049
4140
  wasm._hd_dealloc(ikmPtr);
4050
4141
  wasm._hd_dealloc(saltPtr);
4051
4142
  wasm._hd_dealloc(infoPtr);
@@ -4069,30 +4160,30 @@ function createModule(wasm) {
4069
4160
  V.bytesOfLength(iv, 12, 'IV');
4070
4161
  V.bytes(aad, 'aad');
4071
4162
 
4072
- const keyPtr = allocAndCopy(wasm, key);
4073
- const ptPtr = allocAndCopy(wasm, plaintext);
4074
- const ivPtr = allocAndCopy(wasm, iv);
4075
- const aadPtr = aad.length > 0 ? allocAndCopy(wasm, aad) : 0;
4076
- const ctPtr = wasm._hd_alloc(plaintext.length);
4163
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
4164
+ const { ptr: ptPtr, len: ptLen } = allocAndCopy(wasm, plaintext);
4165
+ const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
4166
+ const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
4167
+ const ctPtr = wasm._hd_alloc(ptLen);
4077
4168
  const tagPtr = wasm._hd_alloc(16);
4078
4169
 
4079
4170
  try {
4080
4171
  const result = wasm._hd_aes_gcm_encrypt(
4081
- keyPtr, key.length,
4082
- ptPtr, plaintext.length,
4083
- ivPtr, iv.length,
4084
- aadPtr, aad.length,
4172
+ keyPtr, keyLen,
4173
+ ptPtr, ptLen,
4174
+ ivPtr, ivLen,
4175
+ aadPtr, aadLen,
4085
4176
  ctPtr, tagPtr
4086
4177
  );
4087
- checkLength(result, plaintext.length);
4178
+ checkLength(result, ptLen);
4088
4179
 
4089
4180
  return {
4090
- ciphertext: readBytes(wasm, ctPtr, plaintext.length),
4181
+ ciphertext: readBytes(wasm, ctPtr, ptLen),
4091
4182
  tag: readBytes(wasm, tagPtr, 16)
4092
4183
  };
4093
4184
  } finally {
4094
- wasm._hd_secure_wipe(keyPtr, key.length);
4095
- wasm._hd_secure_wipe(ptPtr, plaintext.length);
4185
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4186
+ wasm._hd_secure_wipe(ptPtr, ptLen);
4096
4187
  wasm._hd_dealloc(keyPtr);
4097
4188
  wasm._hd_dealloc(ptPtr);
4098
4189
  wasm._hd_dealloc(ivPtr);
@@ -4119,22 +4210,22 @@ function createModule(wasm) {
4119
4210
  V.bytesOfLength(iv, 12, 'IV');
4120
4211
  V.bytes(aad, 'aad');
4121
4212
 
4122
- const keyPtr = allocAndCopy(wasm, key);
4123
- const ctPtr = allocAndCopy(wasm, ciphertext);
4124
- const ivPtr = allocAndCopy(wasm, iv);
4125
- const tagPtr = allocAndCopy(wasm, tag);
4126
- const aadPtr = aad.length > 0 ? allocAndCopy(wasm, aad) : 0;
4127
- const ptPtr = wasm._hd_alloc(ciphertext.length);
4213
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
4214
+ const { ptr: ctPtr, len: ctLen } = allocAndCopy(wasm, ciphertext);
4215
+ const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
4216
+ const { ptr: tagPtr, len: tagLen } = allocAndCopy(wasm, tag);
4217
+ const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
4218
+ const ptPtr = wasm._hd_alloc(ctLen);
4128
4219
 
4129
4220
  try {
4130
4221
  const result = wasm._hd_aes_gcm_decrypt(
4131
- keyPtr, key.length,
4132
- ctPtr, ciphertext.length,
4133
- ivPtr, iv.length,
4134
- aadPtr, aad.length,
4222
+ keyPtr, keyLen,
4223
+ ctPtr, ctLen,
4224
+ ivPtr, ivLen,
4225
+ aadPtr, aadLen,
4135
4226
  tagPtr, ptPtr
4136
4227
  );
4137
- if (result !== ciphertext.length) {
4228
+ if (result !== ctLen) {
4138
4229
  const code = nativeErrorCode(result);
4139
4230
  throw new HDWalletError(
4140
4231
  code,
@@ -4142,15 +4233,15 @@ function createModule(wasm) {
4142
4233
  );
4143
4234
  }
4144
4235
 
4145
- return readBytes(wasm, ptPtr, ciphertext.length);
4236
+ return readBytes(wasm, ptPtr, ctLen);
4146
4237
  } finally {
4147
- wasm._hd_secure_wipe(keyPtr, key.length);
4238
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4148
4239
  wasm._hd_dealloc(keyPtr);
4149
4240
  wasm._hd_dealloc(ctPtr);
4150
4241
  wasm._hd_dealloc(ivPtr);
4151
4242
  wasm._hd_dealloc(tagPtr);
4152
4243
  if (aadPtr) wasm._hd_dealloc(aadPtr);
4153
- wasm._hd_secure_wipe(ptPtr, ciphertext.length);
4244
+ wasm._hd_secure_wipe(ptPtr, ctLen);
4154
4245
  wasm._hd_dealloc(ptPtr);
4155
4246
  }
4156
4247
  }
@@ -4186,14 +4277,14 @@ function createModule(wasm) {
4186
4277
  V.bytes(salt, 'salt');
4187
4278
  V.integer(iterations, 'iterations', 1, 0x7fffffff);
4188
4279
  V.integer(length, 'length', 1, 0x3fffffff);
4189
- const pwdPtr = allocAndCopy(wasm, password);
4190
- const saltPtr = allocAndCopy(wasm, salt);
4280
+ const { ptr: pwdPtr, len: pwdLen } = allocAndCopy(wasm, password);
4281
+ const { ptr: saltPtr, len: saltLen } = allocAndCopy(wasm, salt);
4191
4282
  const outPtr = wasm._hd_alloc(length);
4192
4283
  try {
4193
- checkLength(wasm._hd_kdf_pbkdf2(pwdPtr, password.length, saltPtr, salt.length, iterations, outPtr, length), length);
4284
+ checkLength(wasm._hd_kdf_pbkdf2(pwdPtr, pwdLen, saltPtr, saltLen, iterations, outPtr, length), length);
4194
4285
  return readBytes(wasm, outPtr, length);
4195
4286
  } finally {
4196
- wasm._hd_secure_wipe(pwdPtr, password.length);
4287
+ wasm._hd_secure_wipe(pwdPtr, pwdLen);
4197
4288
  wasm._hd_dealloc(pwdPtr);
4198
4289
  wasm._hd_dealloc(saltPtr);
4199
4290
  wasm._hd_dealloc(outPtr);
@@ -4207,14 +4298,14 @@ function createModule(wasm) {
4207
4298
  V.integer(r, 'r', 1, 0x7fffffff);
4208
4299
  V.integer(p, 'p', 1, 0x7fffffff);
4209
4300
  V.integer(length, 'length', 1, 0x3fffffff);
4210
- const pwdPtr = allocAndCopy(wasm, password);
4211
- const saltPtr = allocAndCopy(wasm, salt);
4301
+ const { ptr: pwdPtr, len: pwdLen } = allocAndCopy(wasm, password);
4302
+ const { ptr: saltPtr, len: saltLen } = allocAndCopy(wasm, salt);
4212
4303
  const outPtr = wasm._hd_alloc(length);
4213
4304
  try {
4214
- checkLength(wasm._hd_kdf_scrypt(pwdPtr, password.length, saltPtr, salt.length, BigInt(n), r, p, outPtr, length), length);
4305
+ checkLength(wasm._hd_kdf_scrypt(pwdPtr, pwdLen, saltPtr, saltLen, BigInt(n), r, p, outPtr, length), length);
4215
4306
  return readBytes(wasm, outPtr, length);
4216
4307
  } finally {
4217
- wasm._hd_secure_wipe(pwdPtr, password.length);
4308
+ wasm._hd_secure_wipe(pwdPtr, pwdLen);
4218
4309
  wasm._hd_dealloc(pwdPtr);
4219
4310
  wasm._hd_dealloc(saltPtr);
4220
4311
  wasm._hd_dealloc(outPtr);
@@ -4228,9 +4319,9 @@ function createModule(wasm) {
4228
4319
  if (!encodeBase58Fn) {
4229
4320
  return encodeBase58Fallback(data);
4230
4321
  }
4231
- const dataPtr = allocAndCopy(wasm, data);
4322
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
4232
4323
  try {
4233
- const ptr = encodeBase58Fn(dataPtr, data.length);
4324
+ const ptr = encodeBase58Fn(dataPtr, dataLen);
4234
4325
  return readString(wasm, ptr);
4235
4326
  } finally {
4236
4327
  wasm._hd_dealloc(dataPtr);
@@ -4264,14 +4355,14 @@ function createModule(wasm) {
4264
4355
  const encodeBase58CheckFn = getWasmFunction(wasm, '_hd_encode_base58check');
4265
4356
  if (!encodeBase58CheckFn) {
4266
4357
  const checksum = this.sha256(this.sha256(data)).slice(0, 4);
4267
- const payload = new Uint8Array(data.length + checksum.length);
4358
+ const payload = new Uint8Array(V.byteLength(data) + checksum.length);
4268
4359
  payload.set(data, 0);
4269
- payload.set(checksum, data.length);
4360
+ payload.set(checksum, V.byteLength(data));
4270
4361
  return encodeBase58Fallback(payload);
4271
4362
  }
4272
- const dataPtr = allocAndCopy(wasm, data);
4363
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
4273
4364
  try {
4274
- const ptr = encodeBase58CheckFn(dataPtr, data.length);
4365
+ const ptr = encodeBase58CheckFn(dataPtr, dataLen);
4275
4366
  return readString(wasm, ptr);
4276
4367
  } finally {
4277
4368
  wasm._hd_dealloc(dataPtr);
@@ -4320,9 +4411,9 @@ function createModule(wasm) {
4320
4411
  return encodeBech32Fallback(hrp, data);
4321
4412
  }
4322
4413
  const hrpPtr = allocString(wasm, hrp);
4323
- const dataPtr = allocAndCopy(wasm, data);
4414
+ const { ptr: dataPtr, len: dataLen } = allocAndCopy(wasm, data);
4324
4415
  try {
4325
- const ptr = encodeBech32Fn(hrpPtr, dataPtr, data.length);
4416
+ const ptr = encodeBech32Fn(hrpPtr, dataPtr, dataLen);
4326
4417
  return readString(wasm, ptr);
4327
4418
  } finally {
4328
4419
  wasm._hd_dealloc(hrpPtr);
@@ -4374,13 +4465,22 @@ function createModule(wasm) {
4374
4465
  },
4375
4466
 
4376
4467
  // Memory
4468
+ /**
4469
+ * Zero, in place, the bytes under any typed array, DataView, ArrayBuffer
4470
+ * or SharedArrayBuffer (any realm), read from its internal slots, never
4471
+ * from overridable properties. Throws INVALID_ARGUMENT for anything else
4472
+ * and for a detached buffer (its bytes are no longer here to wipe).
4473
+ */
4377
4474
  secureWipe(data) {
4378
- if (data instanceof Uint8Array) {
4379
- // Wipe in-place
4380
- for (let i = 0; i < data.length; i++) {
4381
- data[i] = 0;
4382
- }
4475
+ const region = binaryRegion(data);
4476
+ if (region === null) {
4477
+ throw new HDWalletError(ErrorCode.INVALID_ARGUMENT,
4478
+ 'secureWipe takes a typed array, DataView, ArrayBuffer or SharedArrayBuffer');
4479
+ }
4480
+ if (region.detached) {
4481
+ throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'secureWipe: the buffer is detached');
4383
4482
  }
4483
+ TYPED_ARRAY_FILL.call(new Uint8Array(region.buffer, region.offset, region.length), 0);
4384
4484
  }
4385
4485
  };
4386
4486
 
@@ -4411,15 +4511,15 @@ function createModule(wasm) {
4411
4511
  exportPrivateKeyPem(curve, privateKey) {
4412
4512
  const fn = requireWasmFunction(wasm, '_hd_x509_export_private_key_pem');
4413
4513
  const keyBytes = V.privateKey(wasm, toUint8Array(privateKey, 'privateKey'), curve);
4414
- const keyPtr = allocAndCopy(wasm, keyBytes);
4514
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, keyBytes);
4415
4515
  try {
4416
4516
  return callDynamicStringResult(
4417
4517
  wasm,
4418
- (outPtr, outLenPtr) => fn(curve, keyPtr, keyBytes.length, outPtr, outLenPtr),
4518
+ (outPtr, outLenPtr) => fn(curve, keyPtr, keyLen, outPtr, outLenPtr),
4419
4519
  { wipeOutput: true }
4420
4520
  );
4421
4521
  } finally {
4422
- wasm._hd_secure_wipe(keyPtr, keyBytes.length);
4522
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4423
4523
  wasm._hd_dealloc(keyPtr);
4424
4524
  }
4425
4525
  },
@@ -4448,7 +4548,7 @@ function createModule(wasm) {
4448
4548
  const optionsLen = wasm.lengthBytesUTF8(optionsSpec) + 1;
4449
4549
  const keyBytes = V.privateKey(wasm, toUint8Array(certificatePrivateKey, 'certificatePrivateKey'),
4450
4550
  certificateCurve, 'certificatePrivateKey');
4451
- const keyPtr = allocAndCopy(wasm, keyBytes);
4551
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, keyBytes);
4452
4552
 
4453
4553
  try {
4454
4554
  const bytes = callDynamicBytesResult(
@@ -4457,7 +4557,7 @@ function createModule(wasm) {
4457
4557
  optionsPtr,
4458
4558
  certificateCurve,
4459
4559
  keyPtr,
4460
- keyBytes.length,
4560
+ keyLen,
4461
4561
  outputEncoding,
4462
4562
  outPtr,
4463
4563
  outLenPtr
@@ -4466,7 +4566,7 @@ function createModule(wasm) {
4466
4566
  return decodeX509CertificateOutput(bytes, outputEncoding);
4467
4567
  } finally {
4468
4568
  wasm._hd_secure_wipe(optionsPtr, optionsLen);
4469
- wasm._hd_secure_wipe(keyPtr, keyBytes.length);
4569
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4470
4570
  wasm._hd_dealloc(optionsPtr);
4471
4571
  wasm._hd_dealloc(keyPtr);
4472
4572
  }
@@ -4489,16 +4589,16 @@ function createModule(wasm) {
4489
4589
  const optionsLen = wasm.lengthBytesUTF8(optionsSpec) + 1;
4490
4590
  const issuerKeyBytes = V.privateKey(wasm, toUint8Array(issuerPrivateKey, 'issuerPrivateKey'),
4491
4591
  issuerCurve, 'issuerPrivateKey');
4492
- const issuerKeyPtr = allocAndCopy(wasm, issuerKeyBytes);
4592
+ const { ptr: issuerKeyPtr, len: issuerKeyLen } = allocAndCopy(wasm, issuerKeyBytes);
4493
4593
  const issuerCertBytes = normalizeX509CertificateInput(
4494
4594
  issuerCertificate,
4495
4595
  issuerCertificateEncoding,
4496
4596
  'issuerCertificate'
4497
4597
  );
4498
- const issuerCertPtr = allocAndCopy(wasm, issuerCertBytes);
4598
+ const { ptr: issuerCertPtr, len: issuerCertLen } = allocAndCopy(wasm, issuerCertBytes);
4499
4599
  const subjectKeyBytes = V.privateKey(wasm, toUint8Array(subjectPrivateKey, 'subjectPrivateKey'),
4500
4600
  subjectCurve, 'subjectPrivateKey');
4501
- const subjectKeyPtr = allocAndCopy(wasm, subjectKeyBytes);
4601
+ const { ptr: subjectKeyPtr, len: subjectKeyLen } = allocAndCopy(wasm, subjectKeyBytes);
4502
4602
 
4503
4603
  try {
4504
4604
  const bytes = callDynamicBytesResult(
@@ -4507,13 +4607,13 @@ function createModule(wasm) {
4507
4607
  optionsPtr,
4508
4608
  issuerCurve,
4509
4609
  issuerKeyPtr,
4510
- issuerKeyBytes.length,
4610
+ issuerKeyLen,
4511
4611
  issuerCertPtr,
4512
- issuerCertBytes.length,
4612
+ issuerCertLen,
4513
4613
  issuerCertificateEncoding,
4514
4614
  subjectCurve,
4515
4615
  subjectKeyPtr,
4516
- subjectKeyBytes.length,
4616
+ subjectKeyLen,
4517
4617
  outputEncoding,
4518
4618
  outPtr,
4519
4619
  outLenPtr
@@ -4522,8 +4622,8 @@ function createModule(wasm) {
4522
4622
  return decodeX509CertificateOutput(bytes, outputEncoding);
4523
4623
  } finally {
4524
4624
  wasm._hd_secure_wipe(optionsPtr, optionsLen);
4525
- wasm._hd_secure_wipe(issuerKeyPtr, issuerKeyBytes.length);
4526
- wasm._hd_secure_wipe(subjectKeyPtr, subjectKeyBytes.length);
4625
+ wasm._hd_secure_wipe(issuerKeyPtr, issuerKeyLen);
4626
+ wasm._hd_secure_wipe(subjectKeyPtr, subjectKeyLen);
4527
4627
  wasm._hd_dealloc(optionsPtr);
4528
4628
  wasm._hd_dealloc(issuerKeyPtr);
4529
4629
  wasm._hd_dealloc(issuerCertPtr);
@@ -4536,14 +4636,14 @@ function createModule(wasm) {
4536
4636
  assertX509CertificateEncoding(outputEncoding, 'outputEncoding');
4537
4637
  const fn = requireWasmFunction(wasm, '_hd_x509_convert_certificate');
4538
4638
  const certBytes = normalizeX509CertificateInput(certificate, inputEncoding);
4539
- const certPtr = allocAndCopy(wasm, certBytes);
4639
+ const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
4540
4640
 
4541
4641
  try {
4542
4642
  const bytes = callDynamicBytesResult(
4543
4643
  wasm,
4544
4644
  (outPtr, outLenPtr) => fn(
4545
4645
  certPtr,
4546
- certBytes.length,
4646
+ certLen,
4547
4647
  inputEncoding,
4548
4648
  outputEncoding,
4549
4649
  outPtr,
@@ -4560,13 +4660,13 @@ function createModule(wasm) {
4560
4660
  assertX509CertificateEncoding(inputEncoding, 'inputEncoding');
4561
4661
  const fn = requireWasmFunction(wasm, '_hd_x509_parse_certificate_json');
4562
4662
  const certBytes = normalizeX509CertificateInput(certificate, inputEncoding);
4563
- const certPtr = allocAndCopy(wasm, certBytes);
4663
+ const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
4564
4664
 
4565
4665
  try {
4566
4666
  return normalizeParsedCertificateJson(JSON.parse(
4567
4667
  callDynamicStringResult(
4568
4668
  wasm,
4569
- (outPtr, outLenPtr) => fn(certPtr, certBytes.length, inputEncoding, outPtr, outLenPtr)
4669
+ (outPtr, outLenPtr) => fn(certPtr, certLen, inputEncoding, outPtr, outLenPtr)
4570
4670
  )
4571
4671
  ));
4572
4672
  } finally {
@@ -4578,10 +4678,10 @@ function createModule(wasm) {
4578
4678
  assertX509CertificateEncoding(inputEncoding, 'inputEncoding');
4579
4679
  const fn = requireWasmFunction(wasm, '_hd_x509_verify_wallet_attestation');
4580
4680
  const certBytes = normalizeX509CertificateInput(certificate, inputEncoding);
4581
- const certPtr = allocAndCopy(wasm, certBytes);
4681
+ const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
4582
4682
 
4583
4683
  try {
4584
- const result = fn(certPtr, certBytes.length, inputEncoding);
4684
+ const result = fn(certPtr, certLen, inputEncoding);
4585
4685
  if (result < 0) {
4586
4686
  throw new HDWalletError(result);
4587
4687
  }
@@ -4603,38 +4703,38 @@ function createModule(wasm) {
4603
4703
  assertX509CertificateEncoding(certificateEncoding, 'certificateEncoding');
4604
4704
  const fn = requireWasmFunction(wasm, '_hd_x509_export_pkcs12');
4605
4705
  const certBytes = normalizeX509CertificateInput(certificate, certificateEncoding);
4606
- const certPtr = allocAndCopy(wasm, certBytes);
4706
+ const { ptr: certPtr, len: certLen } = allocAndCopy(wasm, certBytes);
4607
4707
  const keyBytes = V.privateKey(wasm, toUint8Array(privateKey, 'privateKey'), privateKeyCurve);
4608
4708
  V.string(password, 'password');
4609
4709
  V.string(friendlyName, 'friendlyName');
4610
- const keyPtr = allocAndCopy(wasm, keyBytes);
4710
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, keyBytes);
4611
4711
  const passwordPtr = allocString(wasm, password);
4612
4712
  const passwordLen = wasm.lengthBytesUTF8(password) + 1;
4613
4713
  const friendlyNamePtr = allocString(wasm, friendlyName);
4614
4714
  const chainBytes = normalizePemInput(chainPem, 'chainPem');
4615
- const chainPtr = chainBytes.length > 0 ? allocAndCopy(wasm, chainBytes) : 0;
4715
+ const { ptr: chainPtr, len: chainLen } = V.byteLength(chainBytes) > 0 ? allocAndCopy(wasm, chainBytes) : { ptr: 0, len: 0 };
4616
4716
 
4617
4717
  try {
4618
4718
  return callDynamicBytesResult(
4619
4719
  wasm,
4620
4720
  (outPtr, outLenPtr) => fn(
4621
4721
  certPtr,
4622
- certBytes.length,
4722
+ certLen,
4623
4723
  certificateEncoding,
4624
4724
  privateKeyCurve,
4625
4725
  keyPtr,
4626
- keyBytes.length,
4726
+ keyLen,
4627
4727
  passwordPtr,
4628
4728
  friendlyNamePtr,
4629
4729
  chainPtr,
4630
- chainBytes.length,
4730
+ chainLen,
4631
4731
  outPtr,
4632
4732
  outLenPtr
4633
4733
  ),
4634
4734
  { wipeOutput: true }
4635
4735
  );
4636
4736
  } finally {
4637
- wasm._hd_secure_wipe(keyPtr, keyBytes.length);
4737
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4638
4738
  wasm._hd_secure_wipe(passwordPtr, passwordLen);
4639
4739
  wasm._hd_dealloc(certPtr);
4640
4740
  wasm._hd_dealloc(keyPtr);
@@ -4650,7 +4750,7 @@ function createModule(wasm) {
4650
4750
  const fn = requireWasmFunction(wasm, '_hd_x509_import_pkcs12_json');
4651
4751
  const bundleBytes = toUint8Array(pkcs12Bundle, 'pkcs12Bundle');
4652
4752
  V.string(password, 'password');
4653
- const bundlePtr = allocAndCopy(wasm, bundleBytes);
4753
+ const { ptr: bundlePtr, len: bundleLen } = allocAndCopy(wasm, bundleBytes);
4654
4754
  const passwordPtr = allocString(wasm, password);
4655
4755
  const passwordLen = wasm.lengthBytesUTF8(password) + 1;
4656
4756
 
@@ -4658,12 +4758,12 @@ function createModule(wasm) {
4658
4758
  return normalizePkcs12ImportJson(JSON.parse(
4659
4759
  callDynamicStringResult(
4660
4760
  wasm,
4661
- (outPtr, outLenPtr) => fn(bundlePtr, bundleBytes.length, passwordPtr, outPtr, outLenPtr),
4761
+ (outPtr, outLenPtr) => fn(bundlePtr, bundleLen, passwordPtr, outPtr, outLenPtr),
4662
4762
  { wipeOutput: true }
4663
4763
  )
4664
4764
  ));
4665
4765
  } finally {
4666
- wasm._hd_secure_wipe(bundlePtr, bundleBytes.length);
4766
+ wasm._hd_secure_wipe(bundlePtr, bundleLen);
4667
4767
  wasm._hd_secure_wipe(passwordPtr, passwordLen);
4668
4768
  wasm._hd_dealloc(bundlePtr);
4669
4769
  wasm._hd_dealloc(passwordPtr);
@@ -4698,18 +4798,18 @@ function createModule(wasm) {
4698
4798
  if (!Number.isInteger(overhead) || overhead <= 0) {
4699
4799
  throw new HDWalletError(ErrorCode.NOT_SUPPORTED, 'ECIES is not supported on this curve');
4700
4800
  }
4701
- const outSize = plaintext.length + overhead;
4702
- const pubPtr = allocAndCopy(wasm, recipientPublicKey);
4703
- const ptPtr = allocAndCopy(wasm, plaintext);
4704
- const aadPtr = aad.length > 0 ? allocAndCopy(wasm, aad) : 0;
4801
+ const outSize = V.byteLength(plaintext) + overhead;
4802
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, recipientPublicKey);
4803
+ const { ptr: ptPtr, len: ptLen } = allocAndCopy(wasm, plaintext);
4804
+ const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
4705
4805
  const outPtr = wasm._hd_alloc(outSize);
4706
4806
  try {
4707
- const result = checkLength(encryptFn(curveId, pubPtr, recipientPublicKey.length,
4708
- ptPtr, plaintext.length, aadPtr, aad.length, outPtr, outSize), outSize);
4807
+ const result = checkLength(encryptFn(curveId, pubPtr, pubLen,
4808
+ ptPtr, ptLen, aadPtr, aadLen, outPtr, outSize), outSize);
4709
4809
  return readBytes(wasm, outPtr, result);
4710
4810
  } finally {
4711
4811
  wasm._hd_dealloc(pubPtr);
4712
- wasm._hd_secure_wipe(ptPtr, plaintext.length);
4812
+ wasm._hd_secure_wipe(ptPtr, ptLen);
4713
4813
  wasm._hd_dealloc(ptPtr);
4714
4814
  if (aadPtr) wasm._hd_dealloc(aadPtr);
4715
4815
  wasm._hd_dealloc(outPtr);
@@ -4722,20 +4822,20 @@ function createModule(wasm) {
4722
4822
  V.bytes(ciphertext, 'ciphertext');
4723
4823
  V.bytes(aad, 'aad');
4724
4824
  const decryptFn = requireWasmFunction(wasm, '_hd_ecies_decrypt');
4725
- const privPtr = allocAndCopy(wasm, recipientPrivateKey);
4726
- const ctPtr = allocAndCopy(wasm, ciphertext);
4727
- const aadPtr = aad.length > 0 ? allocAndCopy(wasm, aad) : 0;
4728
- const outPtr = wasm._hd_alloc(ciphertext.length);
4825
+ const { ptr: privPtr, len: privLen } = allocAndCopy(wasm, recipientPrivateKey);
4826
+ const { ptr: ctPtr, len: ctLen } = allocAndCopy(wasm, ciphertext);
4827
+ const { ptr: aadPtr, len: aadLen } = V.byteLength(aad) > 0 ? allocAndCopy(wasm, aad) : { ptr: 0, len: 0 };
4828
+ const outPtr = wasm._hd_alloc(ctLen);
4729
4829
  try {
4730
- const result = checkLengthWithin(decryptFn(curveId, privPtr, recipientPrivateKey.length,
4731
- ctPtr, ciphertext.length, aadPtr, aad.length, outPtr, ciphertext.length), 0, ciphertext.length);
4830
+ const result = checkLengthWithin(decryptFn(curveId, privPtr, privLen,
4831
+ ctPtr, ctLen, aadPtr, aadLen, outPtr, ctLen), 0, ctLen);
4732
4832
  return readBytes(wasm, outPtr, result);
4733
4833
  } finally {
4734
- wasm._hd_secure_wipe(privPtr, recipientPrivateKey.length);
4834
+ wasm._hd_secure_wipe(privPtr, privLen);
4735
4835
  wasm._hd_dealloc(privPtr);
4736
4836
  wasm._hd_dealloc(ctPtr);
4737
4837
  if (aadPtr) wasm._hd_dealloc(aadPtr);
4738
- wasm._hd_secure_wipe(outPtr, ciphertext.length);
4838
+ wasm._hd_secure_wipe(outPtr, ctLen);
4739
4839
  wasm._hd_dealloc(outPtr);
4740
4840
  }
4741
4841
  }
@@ -4751,16 +4851,16 @@ function createModule(wasm) {
4751
4851
  V.bytesOfLength(key, [16, 24, 32], 'key');
4752
4852
  V.bytes(plaintext, 'plaintext');
4753
4853
  V.bytesOfLength(iv, 16, 'iv');
4754
- const keyPtr = allocAndCopy(wasm, key);
4755
- const ptPtr = allocAndCopy(wasm, plaintext);
4756
- const ivPtr = allocAndCopy(wasm, iv);
4757
- const outPtr = wasm._hd_alloc(plaintext.length);
4854
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
4855
+ const { ptr: ptPtr, len: ptLen } = allocAndCopy(wasm, plaintext);
4856
+ const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
4857
+ const outPtr = wasm._hd_alloc(ptLen);
4758
4858
  try {
4759
- checkLength(encryptFn(keyPtr, key.length, ptPtr, plaintext.length, ivPtr, iv.length, outPtr, plaintext.length), plaintext.length);
4760
- return readBytes(wasm, outPtr, plaintext.length);
4859
+ checkLength(encryptFn(keyPtr, keyLen, ptPtr, ptLen, ivPtr, ivLen, outPtr, ptLen), ptLen);
4860
+ return readBytes(wasm, outPtr, ptLen);
4761
4861
  } finally {
4762
- wasm._hd_secure_wipe(keyPtr, key.length);
4763
- wasm._hd_secure_wipe(ptPtr, plaintext.length);
4862
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4863
+ wasm._hd_secure_wipe(ptPtr, ptLen);
4764
4864
  wasm._hd_dealloc(keyPtr);
4765
4865
  wasm._hd_dealloc(ptPtr);
4766
4866
  wasm._hd_dealloc(ivPtr);
@@ -4773,19 +4873,19 @@ function createModule(wasm) {
4773
4873
  V.bytesOfLength(key, [16, 24, 32], 'key');
4774
4874
  V.bytes(ciphertext, 'ciphertext');
4775
4875
  V.bytesOfLength(iv, 16, 'iv');
4776
- const keyPtr = allocAndCopy(wasm, key);
4777
- const ctPtr = allocAndCopy(wasm, ciphertext);
4778
- const ivPtr = allocAndCopy(wasm, iv);
4779
- const outPtr = wasm._hd_alloc(ciphertext.length);
4876
+ const { ptr: keyPtr, len: keyLen } = allocAndCopy(wasm, key);
4877
+ const { ptr: ctPtr, len: ctLen } = allocAndCopy(wasm, ciphertext);
4878
+ const { ptr: ivPtr, len: ivLen } = allocAndCopy(wasm, iv);
4879
+ const outPtr = wasm._hd_alloc(ctLen);
4780
4880
  try {
4781
- checkLength(decryptFn(keyPtr, key.length, ctPtr, ciphertext.length, ivPtr, iv.length, outPtr, ciphertext.length), ciphertext.length);
4782
- return readBytes(wasm, outPtr, ciphertext.length);
4881
+ checkLength(decryptFn(keyPtr, keyLen, ctPtr, ctLen, ivPtr, ivLen, outPtr, ctLen), ctLen);
4882
+ return readBytes(wasm, outPtr, ctLen);
4783
4883
  } finally {
4784
- wasm._hd_secure_wipe(keyPtr, key.length);
4884
+ wasm._hd_secure_wipe(keyPtr, keyLen);
4785
4885
  wasm._hd_dealloc(keyPtr);
4786
4886
  wasm._hd_dealloc(ctPtr);
4787
4887
  wasm._hd_dealloc(ivPtr);
4788
- wasm._hd_secure_wipe(outPtr, ciphertext.length);
4888
+ wasm._hd_secure_wipe(outPtr, ctLen);
4789
4889
  wasm._hd_dealloc(outPtr);
4790
4890
  }
4791
4891
  }
@@ -4800,19 +4900,19 @@ function createModule(wasm) {
4800
4900
  const deriveFn = requireWasmFunction(wasm, '_hd_slip10_ed25519_derive_path');
4801
4901
  V.bytesOfLength(seed, 64, 'seed', ErrorCode.INVALID_SEED);
4802
4902
  V.string(path, 'path');
4803
- const seedPtr = allocAndCopy(wasm, seed);
4903
+ const { ptr: seedPtr, len: seedLen } = allocAndCopy(wasm, seed);
4804
4904
  const pathPtr = allocString(wasm, path);
4805
4905
  const keyPtr = wasm._hd_alloc(32);
4806
4906
  const chainCodePtr = wasm._hd_alloc(32);
4807
4907
  try {
4808
- const result = deriveFn(seedPtr, seed.length, pathPtr, keyPtr, chainCodePtr);
4908
+ const result = deriveFn(seedPtr, seedLen, pathPtr, keyPtr, chainCodePtr);
4809
4909
  checkResult(result);
4810
4910
  return {
4811
4911
  privateKey: readBytes(wasm, keyPtr, 32),
4812
4912
  chainCode: readBytes(wasm, chainCodePtr, 32)
4813
4913
  };
4814
4914
  } finally {
4815
- wasm._hd_secure_wipe(seedPtr, seed.length);
4915
+ wasm._hd_secure_wipe(seedPtr, seedLen);
4816
4916
  wasm._hd_secure_wipe(keyPtr, 32);
4817
4917
  wasm._hd_dealloc(seedPtr);
4818
4918
  wasm._hd_dealloc(pathPtr);
@@ -4838,11 +4938,11 @@ function createModule(wasm) {
4838
4938
  V.curve(curve, [Curve.SECP256K1, Curve.ED25519, Curve.P256]);
4839
4939
  V.publicKey(wasm, publicKey, curve, 'publicKey',
4840
4940
  curve === Curve.SECP256K1 ? [33] : V.publicKeyLengths(curve));
4841
- const pubPtr = allocAndCopy(wasm, publicKey);
4941
+ const { ptr: pubPtr, len: pubLen } = allocAndCopy(wasm, publicKey);
4842
4942
  const outPtr = wasm._hd_alloc(64);
4843
4943
  try {
4844
4944
  const result = checkLengthWithin(
4845
- wasm._hd_libp2p_peer_id(pubPtr, publicKey.length, curve, outPtr, 64), 1, 64);
4945
+ wasm._hd_libp2p_peer_id(pubPtr, pubLen, curve, outPtr, 64), 1, 64);
4846
4946
  return readBytes(wasm, outPtr, result);
4847
4947
  } finally {
4848
4948
  wasm._hd_dealloc(pubPtr);
@@ -4872,16 +4972,16 @@ function createModule(wasm) {
4872
4972
  // pre-computed peerID bytes and just do the CID wrapping + encoding here.
4873
4973
  // We still use WASM for base36 encoding.
4874
4974
  // A peer ID multihash: identity or SHA-256, at most 64 bytes.
4875
- V.bytesInRange(peerIdBytes, 2, 64, 'peerIdBytes');
4975
+ const peerId = V.snapshot(V.bytesInRange(peerIdBytes, 2, 64, 'peerIdBytes'), 'peerIdBytes');
4876
4976
  const outPtr = wasm._hd_alloc(128);
4877
4977
  // Build CID in JS (trivial: 2-byte prefix + multihash)
4878
- const cid = new Uint8Array(2 + peerIdBytes.length);
4978
+ const cid = new Uint8Array(2 + peerId.length);
4879
4979
  cid[0] = 0x01; // CIDv1
4880
4980
  cid[1] = 0x72; // libp2p-key codec
4881
- cid.set(peerIdBytes, 2);
4882
- const cidPtr = allocAndCopy(wasm, cid);
4981
+ cid.set(peerId, 2);
4982
+ const { ptr: cidPtr, len: cidLen } = allocAndCopy(wasm, cid);
4883
4983
  try {
4884
- const result = wasm._hd_encode_base36lower(cidPtr, cid.length, outPtr, 128);
4984
+ const result = wasm._hd_encode_base36lower(cidPtr, cidLen, outPtr, 128);
4885
4985
  checkResult(result);
4886
4986
  return 'k' + readString(wasm, outPtr);
4887
4987
  } finally {
@@ -4896,15 +4996,15 @@ function createModule(wasm) {
4896
4996
  * @returns {string}
4897
4997
  */
4898
4998
  ipnsHashBase32(peerIdBytes) {
4899
- V.bytesInRange(peerIdBytes, 2, 64, 'peerIdBytes');
4900
- const cid = new Uint8Array(2 + peerIdBytes.length);
4999
+ const peerId = V.snapshot(V.bytesInRange(peerIdBytes, 2, 64, 'peerIdBytes'), 'peerIdBytes');
5000
+ const cid = new Uint8Array(2 + peerId.length);
4901
5001
  cid[0] = 0x01; // CIDv1
4902
5002
  cid[1] = 0x72; // libp2p-key codec
4903
- cid.set(peerIdBytes, 2);
4904
- const cidPtr = allocAndCopy(wasm, cid);
5003
+ cid.set(peerId, 2);
5004
+ const { ptr: cidPtr, len: cidLen } = allocAndCopy(wasm, cid);
4905
5005
  const outPtr = wasm._hd_alloc(128);
4906
5006
  try {
4907
- const result = wasm._hd_encode_base32lower(cidPtr, cid.length, outPtr, 128);
5007
+ const result = wasm._hd_encode_base32lower(cidPtr, cidLen, outPtr, 128);
4908
5008
  checkResult(result);
4909
5009
  return 'b' + readString(wasm, outPtr);
4910
5010
  } finally {
@@ -4995,11 +5095,11 @@ function createModule(wasm) {
4995
5095
  // Entropy
4996
5096
  injectEntropy(entropy) {
4997
5097
  V.bytesInRange(entropy, 1, 0x100000, 'entropy');
4998
- const entropyPtr = allocAndCopy(wasm, entropy);
5098
+ const { ptr: entropyPtr, len: entropyLen } = allocAndCopy(wasm, entropy);
4999
5099
  try {
5000
- wasm._hd_inject_entropy(entropyPtr, entropy.length);
5100
+ wasm._hd_inject_entropy(entropyPtr, entropyLen);
5001
5101
  } finally {
5002
- wasm._hd_secure_wipe(entropyPtr, entropy.length);
5102
+ wasm._hd_secure_wipe(entropyPtr, entropyLen);
5003
5103
  wasm._hd_dealloc(entropyPtr);
5004
5104
  }
5005
5105
  },