@vultisig/walletcore-native 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,947 @@
1
+ /**
2
+ * @vultisig/walletcore-native
3
+ *
4
+ * Native WalletCore bridge for React Native / Expo.
5
+ * Provides a JS object matching the WalletCore API shape expected by the SDK.
6
+ */
7
+ import { keccak_256 } from '@noble/hashes/sha3'
8
+ import ExpoWalletCore from './ExpoWalletCoreModule'
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Types — matches the subset of @trustwallet/wallet-core's WalletCore interface
12
+ // that the SDK actually uses.
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export interface NativePublicKeyInstance {
16
+ readonly _handle: number
17
+ data(): Uint8Array
18
+ uncompressed(): NativePublicKeyInstance
19
+ compressed(): NativePublicKeyInstance
20
+ verify(signature: Uint8Array, message: Uint8Array): boolean
21
+ verifyAsDER(signature: Uint8Array, message: Uint8Array): boolean
22
+ delete(): void
23
+ }
24
+
25
+ export interface NativePrivateKeyInstance {
26
+ readonly _handle: number
27
+ data(): Uint8Array
28
+ getPublicKeySecp256k1(compressed: boolean): NativePublicKeyInstance
29
+ getPublicKeyEd25519(): NativePublicKeyInstance
30
+ delete(): void
31
+ }
32
+
33
+ export interface NativeHDWalletInstance {
34
+ readonly _handle: number
35
+ getMasterKey(curve: number): NativePrivateKeyInstance
36
+ getKeyForCoin(coinType: number): NativePrivateKeyInstance
37
+ getKeyDerivation(coinType: number, derivation: number): NativePrivateKeyInstance
38
+ getAddressDerivation(coinType: number, derivation: number): string
39
+ getKey(coinType: number, derivationPath: string): NativePrivateKeyInstance
40
+ getAddressForCoin(coinType: number): string
41
+ getExtendedPrivateKey(purpose: number, coinType: number, version: number): string
42
+ delete(): void
43
+ }
44
+
45
+ export interface NativeEthereumAbiFunctionInstance {
46
+ addParamUInt8(val: number, isOutput: boolean): number
47
+ addParamUInt16(val: number, isOutput: boolean): number
48
+ addParamUInt32(val: number, isOutput: boolean): number
49
+ addParamUInt64(val: number, isOutput: boolean): number
50
+ addParamUInt256(val: Uint8Array | Buffer, isOutput: boolean): number
51
+ addParamInt8(val: number, isOutput: boolean): number
52
+ addParamInt16(val: number, isOutput: boolean): number
53
+ addParamInt32(val: number, isOutput: boolean): number
54
+ addParamInt64(val: number, isOutput: boolean): number
55
+ addParamInt256(val: Uint8Array | Buffer, isOutput: boolean): number
56
+ addParamBool(val: boolean, isOutput: boolean): number
57
+ addParamString(val: string, isOutput: boolean): number
58
+ addParamAddress(val: Uint8Array | Buffer, isOutput: boolean): number
59
+ addParamBytes(val: Uint8Array | Buffer, isOutput: boolean): number
60
+ addParamBytesFix(size: number, val: Uint8Array | Buffer, isOutput: boolean): number
61
+ delete(): void
62
+ }
63
+
64
+ export interface NativeDataVectorInstance {
65
+ readonly items: string[]
66
+ add(data: Uint8Array): void
67
+ }
68
+
69
+ export interface NativeAnyAddressInstance {
70
+ description(): string
71
+ data(): Uint8Array
72
+ }
73
+
74
+ /** Subset of @trustwallet/wallet-core's WalletCore interface provided natively. */
75
+ export interface WalletCoreLike {
76
+ CoinType: Record<string, number>
77
+ PublicKeyType: Record<string, number>
78
+ Curve: Record<string, number>
79
+ Purpose: Record<string, number>
80
+ HDVersion: Record<string, number>
81
+
82
+ CoinTypeExt: {
83
+ derivationPath(coinType: number): string
84
+ deriveAddressFromPublicKey(coinType: number, publicKey: NativePublicKeyInstance): string
85
+ chainId(coinType: number): string
86
+ ss58Prefix(coinType: number): number
87
+ }
88
+
89
+ PublicKey: {
90
+ createWithData(data: Uint8Array | Buffer, type: number): NativePublicKeyInstance
91
+ }
92
+
93
+ AnyAddress: {
94
+ isValid(address: string, coinType: number): boolean
95
+ isValidBech32(address: string, coinType: number, hrp: string): boolean
96
+ isValidSS58(address: string, coinType: number, ss58Prefix: number): boolean
97
+ createWithString(address: string, coinType: number): NativeAnyAddressInstance
98
+ createBech32WithPublicKey(publicKey: NativePublicKeyInstance, coinType: number, hrp: string): NativeAnyAddressInstance
99
+ createBech32(address: string, coinType: number, hrp: string): NativeAnyAddressInstance
100
+ }
101
+
102
+ TransactionCompiler: {
103
+ preImageHashes(coinType: number, txInputData: Uint8Array): Uint8Array
104
+ compileWithSignatures(coinType: number, txInputData: Uint8Array, signatures: NativeDataVectorInstance, publicKeys: NativeDataVectorInstance): Uint8Array
105
+ }
106
+
107
+ DataVector: {
108
+ create(): NativeDataVectorInstance
109
+ }
110
+
111
+ HDWallet: {
112
+ createWithMnemonic(mnemonic: string, passphrase?: string): NativeHDWalletInstance
113
+ }
114
+
115
+ PrivateKey: {
116
+ create(): NativePrivateKeyInstance
117
+ }
118
+
119
+ AnySigner: {
120
+ plan(txInputData: Uint8Array, coinType: number): Uint8Array
121
+ }
122
+
123
+ HexCoding: {
124
+ decode(hex: string): Uint8Array
125
+ encode(data: Uint8Array): string
126
+ }
127
+
128
+ Bech32: {
129
+ encode(hrp: string, data: Uint8Array): string
130
+ }
131
+
132
+ BitcoinScript: {
133
+ buildPayToWitnessPubkeyHash(hash: Uint8Array): { data(): Uint8Array }
134
+ buildPayToPublicKeyHash(hash: Uint8Array): { data(): Uint8Array }
135
+ lockScriptForAddress(address: string, coinType: number): { data(): Uint8Array }
136
+ hashTypeForCoin(coinType: number): number
137
+ }
138
+
139
+ TONAddressConverter: {
140
+ toUserFriendly(address: string): string
141
+ }
142
+
143
+ EthereumAbiFunction: {
144
+ createWithString(name: string): NativeEthereumAbiFunctionInstance
145
+ }
146
+
147
+ EthereumAbi: {
148
+ encode(fn: NativeEthereumAbiFunctionInstance): Uint8Array
149
+ encodeTyped(messageJson: string): string
150
+ }
151
+
152
+ Mnemonic: {
153
+ isValid(mnemonic: string): boolean
154
+ }
155
+
156
+ SolanaAddress: {
157
+ createWithString(address: string): { defaultTokenAddress(tokenMintAddress: string): string }
158
+ }
159
+ }
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Helpers — base64 <-> Uint8Array
163
+ // ---------------------------------------------------------------------------
164
+
165
+ function toBase64(bytes: Uint8Array | Buffer): string {
166
+ if (Buffer.isBuffer(bytes)) {
167
+ return bytes.toString('base64')
168
+ }
169
+ let binary = ''
170
+ for (let i = 0; i < bytes.length; i++) {
171
+ binary += String.fromCharCode(bytes[i]!)
172
+ }
173
+ return btoa(binary)
174
+ }
175
+
176
+ function fromBase64(b64: string): Uint8Array {
177
+ const binary = atob(b64)
178
+ const bytes = new Uint8Array(binary.length)
179
+ for (let i = 0; i < binary.length; i++) {
180
+ bytes[i] = binary.charCodeAt(i)
181
+ }
182
+ return bytes
183
+ }
184
+
185
+ // ---------------------------------------------------------------------------
186
+ // CoinType enum — numeric values matching TrustWallet's CoinType
187
+ // Values sourced from TrustWallet wallet-core v4.2.9:
188
+ // https://github.com/trustwallet/wallet-core/blob/v4.2.9/include/TrustWalletCore/TWCoinType.h
189
+ // ---------------------------------------------------------------------------
190
+
191
+ const CoinTypeValues: Record<string, number> = {
192
+ bitcoin: 0,
193
+ litecoin: 2,
194
+ dogecoin: 3,
195
+ dash: 5,
196
+ ethereum: 60,
197
+ cosmos: 118,
198
+ zcash: 133,
199
+ ripple: 144, // xrp
200
+ xrp: 144,
201
+ bitcoinCash: 145,
202
+ tron: 195,
203
+ terra: 330,
204
+ polkadot: 354,
205
+ ton: 607,
206
+ solana: 501,
207
+ thorchain: 931,
208
+ sui: 784,
209
+ cardano: 1815,
210
+ smartChain: 20000714,
211
+ arbitrum: 10042221,
212
+ avalancheCChain: 10009000,
213
+ base: 8453,
214
+ polygon: 966,
215
+ optimism: 10000070,
216
+ cronosChain: 10000025,
217
+ blast: 81457,
218
+ zksync: 10000324,
219
+ osmosis: 10000118,
220
+ terraV2: 10000330,
221
+ noble: 18000118,
222
+ kujira: 70000118,
223
+ dydx: 22000118,
224
+ akash: 17000118,
225
+ mantle: 5000,
226
+ sei: 19000118,
227
+ }
228
+
229
+ // ---------------------------------------------------------------------------
230
+ // PublicKeyType enum
231
+ // ---------------------------------------------------------------------------
232
+
233
+ const PublicKeyTypeValues: Record<string, number> = {
234
+ secp256k1: 0,
235
+ secp256k1Extended: 1,
236
+ nist256p1: 2,
237
+ nist256p1Extended: 3,
238
+ ed25519: 4,
239
+ ed25519Blake2b: 5,
240
+ curve25519: 6,
241
+ ed25519Cardano: 7,
242
+ starkex: 8,
243
+ }
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // Curve enum
247
+ // ---------------------------------------------------------------------------
248
+
249
+ const CurveValues: Record<string, number> = {
250
+ secp256k1: 0,
251
+ ed25519: 1,
252
+ ed25519Blake2bNano: 2,
253
+ curve25519: 3,
254
+ nist256p1: 4,
255
+ ed25519ExtendedCardano: 5,
256
+ starkex: 6,
257
+ }
258
+
259
+ // ---------------------------------------------------------------------------
260
+ // Purpose enum
261
+ // ---------------------------------------------------------------------------
262
+
263
+ const PurposeValues: Record<string, number> = {
264
+ bip44: 44,
265
+ bip49: 49,
266
+ bip84: 84,
267
+ bip1852: 1852,
268
+ }
269
+
270
+ // ---------------------------------------------------------------------------
271
+ // HDVersion enum
272
+ // ---------------------------------------------------------------------------
273
+
274
+ const HDVersionValues: Record<string, number> = {
275
+ none: 0,
276
+ xpub: 1,
277
+ xprv: 2,
278
+ ypub: 3,
279
+ yprv: 4,
280
+ zpub: 5,
281
+ zprv: 6,
282
+ }
283
+
284
+ // ---------------------------------------------------------------------------
285
+ // NativePublicKey — wraps a native handle
286
+ //
287
+ // NOTE: This class does NOT use automatic cleanup via FinalizationRegistry.
288
+ // The native handle allocated on the C++ side must be released explicitly by
289
+ // calling delete() when the instance is no longer needed. Failure to do so
290
+ // will leak native memory. A FinalizationRegistry could be used to register
291
+ // a fallback cleanup callback, but garbage-collection timing is non-deterministic
292
+ // and should not be relied upon for timely resource reclamation.
293
+ // ---------------------------------------------------------------------------
294
+
295
+ class NativePublicKey implements NativePublicKeyInstance {
296
+ readonly _handle: number
297
+
298
+ constructor(handle: number) {
299
+ this._handle = handle
300
+ }
301
+
302
+ data(): Uint8Array {
303
+ return fromBase64(ExpoWalletCore.publicKeyData(this._handle))
304
+ }
305
+
306
+ uncompressed(): NativePublicKey {
307
+ return new NativePublicKey(ExpoWalletCore.publicKeyUncompressed(this._handle))
308
+ }
309
+
310
+ compressed(): NativePublicKey {
311
+ return new NativePublicKey(ExpoWalletCore.publicKeyCompressed(this._handle))
312
+ }
313
+
314
+ verify(signature: Uint8Array, message: Uint8Array): boolean {
315
+ return ExpoWalletCore.publicKeyVerify(this._handle, toBase64(signature), toBase64(message))
316
+ }
317
+
318
+ verifyAsDER(signature: Uint8Array, message: Uint8Array): boolean {
319
+ return ExpoWalletCore.publicKeyVerifyAsDER(this._handle, toBase64(signature), toBase64(message))
320
+ }
321
+
322
+ delete(): void {
323
+ ExpoWalletCore.freePublicKey(this._handle)
324
+ }
325
+ }
326
+
327
+ // ---------------------------------------------------------------------------
328
+ // NativePrivateKey — wraps a native handle
329
+ //
330
+ // NOTE: This class does NOT use automatic cleanup via FinalizationRegistry.
331
+ // The native handle allocated on the C++ side must be released explicitly by
332
+ // calling delete() when the instance is no longer needed. Failure to do so
333
+ // will leak native memory. A FinalizationRegistry could be used to register
334
+ // a fallback cleanup callback, but garbage-collection timing is non-deterministic
335
+ // and should not be relied upon for timely resource reclamation.
336
+ // ---------------------------------------------------------------------------
337
+
338
+ class NativePrivateKey implements NativePrivateKeyInstance {
339
+ readonly _handle: number
340
+
341
+ constructor(handle: number) {
342
+ this._handle = handle
343
+ }
344
+
345
+ data(): Uint8Array {
346
+ return fromBase64(ExpoWalletCore.privateKeyData(this._handle))
347
+ }
348
+
349
+ getPublicKeySecp256k1(compressed: boolean): NativePublicKey {
350
+ return new NativePublicKey(ExpoWalletCore.privateKeyGetPublicKeySecp256k1(this._handle, compressed))
351
+ }
352
+
353
+ getPublicKeyEd25519(): NativePublicKey {
354
+ return new NativePublicKey(ExpoWalletCore.privateKeyGetPublicKeyEd25519(this._handle))
355
+ }
356
+
357
+ delete(): void {
358
+ ExpoWalletCore.freePrivateKey(this._handle)
359
+ }
360
+ }
361
+
362
+ // ---------------------------------------------------------------------------
363
+ // NativeHDWallet — wraps a native handle
364
+ //
365
+ // NOTE: This class does NOT use automatic cleanup via FinalizationRegistry.
366
+ // The native handle allocated on the C++ side must be released explicitly by
367
+ // calling delete() when the instance is no longer needed. Failure to do so
368
+ // will leak native memory. A FinalizationRegistry could be used to register
369
+ // a fallback cleanup callback, but garbage-collection timing is non-deterministic
370
+ // and should not be relied upon for timely resource reclamation.
371
+ // ---------------------------------------------------------------------------
372
+
373
+ class NativeHDWallet implements NativeHDWalletInstance {
374
+ readonly _handle: number
375
+
376
+ constructor(handle: number) {
377
+ this._handle = handle
378
+ }
379
+
380
+ getMasterKey(curve: number): NativePrivateKey {
381
+ return new NativePrivateKey(ExpoWalletCore.hdWalletGetMasterKey(this._handle, curve))
382
+ }
383
+
384
+ getKeyForCoin(coinType: number): NativePrivateKey {
385
+ return new NativePrivateKey(ExpoWalletCore.hdWalletGetKeyForCoin(this._handle, coinType))
386
+ }
387
+
388
+ getKeyDerivation(coinType: number, derivation: number): NativePrivateKey {
389
+ return new NativePrivateKey(ExpoWalletCore.hdWalletGetKeyDerivation(this._handle, coinType, derivation))
390
+ }
391
+
392
+ getAddressDerivation(coinType: number, derivation: number): string {
393
+ return ExpoWalletCore.hdWalletGetAddressDerivation(this._handle, coinType, derivation)
394
+ }
395
+
396
+ getKey(coinType: number, derivationPath: string): NativePrivateKey {
397
+ return new NativePrivateKey(ExpoWalletCore.hdWalletGetKey(this._handle, coinType, derivationPath))
398
+ }
399
+
400
+ getAddressForCoin(coinType: number): string {
401
+ return ExpoWalletCore.hdWalletGetAddressForCoin(this._handle, coinType)
402
+ }
403
+
404
+ getExtendedPrivateKey(purpose: number, coinType: number, version: number): string {
405
+ return ExpoWalletCore.hdWalletGetExtendedPrivateKey(this._handle, purpose, coinType, version)
406
+ }
407
+
408
+ delete(): void {
409
+ ExpoWalletCore.freeHDWallet(this._handle)
410
+ }
411
+ }
412
+
413
+ // ---------------------------------------------------------------------------
414
+ // NativeDataVector — wraps signature/pubkey arrays
415
+ // ---------------------------------------------------------------------------
416
+
417
+ class NativeDataVector implements NativeDataVectorInstance {
418
+ readonly items: string[] = []
419
+
420
+ add(data: Uint8Array): void {
421
+ this.items.push(toBase64(data))
422
+ }
423
+ }
424
+
425
+ // ---------------------------------------------------------------------------
426
+ // NativeAnyAddress — wraps address results
427
+ // ---------------------------------------------------------------------------
428
+
429
+ class NativeAnyAddress implements NativeAnyAddressInstance {
430
+ private readonly _description: string
431
+ private readonly _data: string | null
432
+
433
+ constructor(description: string, data: string | null = null) {
434
+ this._description = description
435
+ this._data = data
436
+ }
437
+
438
+ description(): string {
439
+ return this._description
440
+ }
441
+
442
+ data(): Uint8Array {
443
+ return this._data ? fromBase64(this._data) : new Uint8Array(0)
444
+ }
445
+ }
446
+
447
+ // ---------------------------------------------------------------------------
448
+ // ABI encoding helpers — pure JS implementation of Solidity ABI encoding.
449
+ // Used by NativeEthereumAbiFunction to avoid bridging the full stateful
450
+ // EthereumAbiFunction class through native modules.
451
+ // ---------------------------------------------------------------------------
452
+
453
+ type AbiParam =
454
+ | { type: 'address'; value: Uint8Array }
455
+ | { type: 'uint256'; value: Uint8Array }
456
+ | { type: 'int256'; value: Uint8Array }
457
+ | { type: 'uint8'; value: number }
458
+ | { type: 'uint16'; value: number }
459
+ | { type: 'uint32'; value: number }
460
+ | { type: 'uint64'; value: number }
461
+ | { type: 'int8'; value: number }
462
+ | { type: 'int16'; value: number }
463
+ | { type: 'int32'; value: number }
464
+ | { type: 'int64'; value: number }
465
+ | { type: 'bool'; value: boolean }
466
+ | { type: 'string'; value: string }
467
+ | { type: 'bytes'; value: Uint8Array }
468
+ | { type: `bytes${number}`; value: Uint8Array }
469
+
470
+ /** Pad a Uint8Array to 32 bytes, left-aligned (for static types). */
471
+ function padLeft(data: Uint8Array, len = 32): Uint8Array {
472
+ const padded = new Uint8Array(len)
473
+ padded.set(data, len - data.length)
474
+ return padded
475
+ }
476
+
477
+ /** Pad a Uint8Array to a multiple of 32 bytes, right-aligned (for dynamic data). */
478
+ function padRight(data: Uint8Array): Uint8Array {
479
+ const paddedLen = Math.ceil(data.length / 32) * 32
480
+ const padded = new Uint8Array(paddedLen)
481
+ padded.set(data, 0)
482
+ return padded
483
+ }
484
+
485
+ function isDynamic(param: AbiParam): boolean {
486
+ return param.type === 'string' || param.type === 'bytes'
487
+ }
488
+
489
+ function abiTypeString(param: AbiParam): string {
490
+ return param.type
491
+ }
492
+
493
+ function uint256FromNumber(n: number): Uint8Array {
494
+ const buf = new Uint8Array(32)
495
+ // Write as big-endian — fit in last 8 bytes for safety
496
+ let val = BigInt(n)
497
+ for (let i = 31; i >= 0 && val > 0n; i--) {
498
+ buf[i] = Number(val & 0xffn)
499
+ val >>= 8n
500
+ }
501
+ return buf
502
+ }
503
+
504
+ function int256FromNumber(n: number): Uint8Array {
505
+ let val = BigInt(n)
506
+ if (val < 0n) {
507
+ // Two's complement for 256-bit
508
+ val = (1n << 256n) + val
509
+ }
510
+ const buf = new Uint8Array(32)
511
+ for (let i = 31; i >= 0; i--) {
512
+ buf[i] = Number(val & 0xffn)
513
+ val >>= 8n
514
+ }
515
+ return buf
516
+ }
517
+
518
+ /** Compute the 4-byte function selector: keccak256(signature)[:4]. */
519
+ function functionSelector(name: string, params: AbiParam[]): Uint8Array {
520
+ const sig = `${name}(${params.map(abiTypeString).join(',')})`
521
+ return keccak_256(new TextEncoder().encode(sig)).slice(0, 4)
522
+ }
523
+
524
+ /** ABI-encode a function call: selector + encoded parameters. */
525
+ function abiEncode(name: string, params: AbiParam[]): Uint8Array {
526
+ const selector = functionSelector(name, params)
527
+
528
+ // Head: for static types, the encoded value; for dynamic types, an offset pointer.
529
+ // Tail: dynamic type data appended after the head section.
530
+ const headParts: Uint8Array[] = []
531
+ const tailParts: Uint8Array[] = []
532
+
533
+ // The head section has 32 bytes per parameter.
534
+ let dynamicOffset = params.length * 32
535
+
536
+ for (const param of params) {
537
+ if (isDynamic(param)) {
538
+ // Head slot = offset to tail data
539
+ headParts.push(uint256FromNumber(dynamicOffset))
540
+
541
+ let encoded: Uint8Array
542
+ if (param.type === 'string') {
543
+ const strBytes = new TextEncoder().encode(param.value as string)
544
+ const lenSlot = uint256FromNumber(strBytes.length)
545
+ const paddedData = padRight(strBytes)
546
+ encoded = new Uint8Array(lenSlot.length + paddedData.length)
547
+ encoded.set(lenSlot, 0)
548
+ encoded.set(paddedData, lenSlot.length)
549
+ } else {
550
+ // bytes
551
+ const bytesVal = param.value as Uint8Array
552
+ const lenSlot = uint256FromNumber(bytesVal.length)
553
+ const paddedData = padRight(bytesVal)
554
+ encoded = new Uint8Array(lenSlot.length + paddedData.length)
555
+ encoded.set(lenSlot, 0)
556
+ encoded.set(paddedData, lenSlot.length)
557
+ }
558
+ tailParts.push(encoded)
559
+ dynamicOffset += encoded.length
560
+ } else {
561
+ // Static types — encode inline in head
562
+ let encoded: Uint8Array
563
+ switch (param.type) {
564
+ case 'address':
565
+ // Address is 20 bytes, left-padded to 32
566
+ encoded = padLeft(param.value as Uint8Array)
567
+ break
568
+ case 'uint256':
569
+ // Already big-endian bytes, pad to 32
570
+ encoded = padLeft(param.value as Uint8Array)
571
+ break
572
+ case 'int256':
573
+ encoded = padLeft(param.value as Uint8Array)
574
+ break
575
+ case 'bool':
576
+ encoded = uint256FromNumber(param.value ? 1 : 0)
577
+ break
578
+ case 'uint8':
579
+ case 'uint16':
580
+ case 'uint32':
581
+ case 'uint64':
582
+ encoded = uint256FromNumber(param.value as number)
583
+ break
584
+ case 'int8':
585
+ case 'int16':
586
+ case 'int32':
587
+ case 'int64':
588
+ encoded = int256FromNumber(param.value as number)
589
+ break
590
+ default:
591
+ if (param.type.startsWith('bytes')) {
592
+ // bytesN (fixed-size) — right-padded to 32
593
+ encoded = padRight(param.value as Uint8Array)
594
+ } else {
595
+ throw new Error(`Unsupported ABI param type: ${param.type}`)
596
+ }
597
+ }
598
+ headParts.push(encoded)
599
+ }
600
+ }
601
+
602
+ // Concatenate: selector + heads + tails
603
+ const totalLen =
604
+ selector.length +
605
+ headParts.reduce((sum, p) => sum + p.length, 0) +
606
+ tailParts.reduce((sum, p) => sum + p.length, 0)
607
+
608
+ const result = new Uint8Array(totalLen)
609
+ let offset = 0
610
+ result.set(selector, offset)
611
+ offset += selector.length
612
+ for (const part of headParts) {
613
+ result.set(part, offset)
614
+ offset += part.length
615
+ }
616
+ for (const part of tailParts) {
617
+ result.set(part, offset)
618
+ offset += part.length
619
+ }
620
+ return result
621
+ }
622
+
623
+ // ---------------------------------------------------------------------------
624
+ // NativeEthereumAbiFunction — pure JS ABI function encoder
625
+ // ---------------------------------------------------------------------------
626
+
627
+ class NativeEthereumAbiFunction implements NativeEthereumAbiFunctionInstance {
628
+ readonly name: string
629
+ private params: AbiParam[] = []
630
+
631
+ constructor(name: string) {
632
+ this.name = name
633
+ }
634
+
635
+ addParamUInt8(val: number, _isOutput: boolean): number {
636
+ this.params.push({ type: 'uint8', value: val })
637
+ return this.params.length - 1
638
+ }
639
+
640
+ addParamUInt16(val: number, _isOutput: boolean): number {
641
+ this.params.push({ type: 'uint16', value: val })
642
+ return this.params.length - 1
643
+ }
644
+
645
+ addParamUInt32(val: number, _isOutput: boolean): number {
646
+ this.params.push({ type: 'uint32', value: val })
647
+ return this.params.length - 1
648
+ }
649
+
650
+ addParamUInt64(val: number, _isOutput: boolean): number {
651
+ this.params.push({ type: 'uint64', value: val })
652
+ return this.params.length - 1
653
+ }
654
+
655
+ addParamUInt256(val: Uint8Array | Buffer, _isOutput: boolean): number {
656
+ this.params.push({ type: 'uint256', value: Uint8Array.from(val) })
657
+ return this.params.length - 1
658
+ }
659
+
660
+ addParamInt8(val: number, _isOutput: boolean): number {
661
+ this.params.push({ type: 'int8', value: val })
662
+ return this.params.length - 1
663
+ }
664
+
665
+ addParamInt16(val: number, _isOutput: boolean): number {
666
+ this.params.push({ type: 'int16', value: val })
667
+ return this.params.length - 1
668
+ }
669
+
670
+ addParamInt32(val: number, _isOutput: boolean): number {
671
+ this.params.push({ type: 'int32', value: val })
672
+ return this.params.length - 1
673
+ }
674
+
675
+ addParamInt64(val: number, _isOutput: boolean): number {
676
+ this.params.push({ type: 'int64', value: val })
677
+ return this.params.length - 1
678
+ }
679
+
680
+ addParamInt256(val: Uint8Array | Buffer, _isOutput: boolean): number {
681
+ this.params.push({ type: 'int256', value: Uint8Array.from(val) })
682
+ return this.params.length - 1
683
+ }
684
+
685
+ addParamBool(val: boolean, _isOutput: boolean): number {
686
+ this.params.push({ type: 'bool', value: val })
687
+ return this.params.length - 1
688
+ }
689
+
690
+ addParamString(val: string, _isOutput: boolean): number {
691
+ this.params.push({ type: 'string', value: val })
692
+ return this.params.length - 1
693
+ }
694
+
695
+ addParamAddress(val: Uint8Array | Buffer, _isOutput: boolean): number {
696
+ this.params.push({ type: 'address', value: Uint8Array.from(val) })
697
+ return this.params.length - 1
698
+ }
699
+
700
+ addParamBytes(val: Uint8Array | Buffer, _isOutput: boolean): number {
701
+ this.params.push({ type: 'bytes', value: Uint8Array.from(val) })
702
+ return this.params.length - 1
703
+ }
704
+
705
+ addParamBytesFix(size: number, val: Uint8Array | Buffer, _isOutput: boolean): number {
706
+ this.params.push({ type: `bytes${size}`, value: Uint8Array.from(val) })
707
+ return this.params.length - 1
708
+ }
709
+
710
+ /** Encode this function call as ABI calldata. */
711
+ encode(): Uint8Array {
712
+ return abiEncode(this.name, this.params)
713
+ }
714
+
715
+ delete(): void {
716
+ // No-op — pure JS, nothing to free.
717
+ }
718
+ }
719
+
720
+ // ---------------------------------------------------------------------------
721
+ // NativeWalletCore — the main facade
722
+ // ---------------------------------------------------------------------------
723
+
724
+ export class NativeWalletCore {
725
+ private static instance: WalletCoreLike | null = null
726
+
727
+ static getInstance(): WalletCoreLike {
728
+ if (NativeWalletCore.instance) {
729
+ return NativeWalletCore.instance
730
+ }
731
+
732
+ const wc: WalletCoreLike = {
733
+ // --- CoinType enum ---
734
+ CoinType: { ...CoinTypeValues },
735
+
736
+ // --- PublicKeyType enum ---
737
+ PublicKeyType: { ...PublicKeyTypeValues },
738
+
739
+ // --- Curve enum ---
740
+ Curve: { ...CurveValues },
741
+
742
+ // --- Purpose enum ---
743
+ Purpose: { ...PurposeValues },
744
+
745
+ // --- HDVersion enum ---
746
+ HDVersion: { ...HDVersionValues },
747
+
748
+ // --- CoinTypeExt ---
749
+ CoinTypeExt: {
750
+ derivationPath(coinType: number): string {
751
+ return ExpoWalletCore.derivationPath(coinType)
752
+ },
753
+ deriveAddressFromPublicKey(coinType: number, publicKey: NativePublicKey): string {
754
+ return ExpoWalletCore.deriveAddressFromPublicKey(coinType, publicKey._handle)
755
+ },
756
+ chainId(coinType: number): string {
757
+ return ExpoWalletCore.chainId(coinType)
758
+ },
759
+ ss58Prefix(coinType: number): number {
760
+ return ExpoWalletCore.ss58Prefix(coinType)
761
+ },
762
+ },
763
+
764
+ // --- PublicKey ---
765
+ PublicKey: {
766
+ createWithData(data: Uint8Array | Buffer, type: number): NativePublicKey {
767
+ const handle = ExpoWalletCore.publicKeyCreateWithData(toBase64(data), type)
768
+ return new NativePublicKey(handle)
769
+ },
770
+ },
771
+
772
+ // --- AnyAddress ---
773
+ AnyAddress: {
774
+ isValid(address: string, coinType: number): boolean {
775
+ return ExpoWalletCore.anyAddressIsValid(address, coinType)
776
+ },
777
+ isValidBech32(address: string, coinType: number, hrp: string): boolean {
778
+ return ExpoWalletCore.anyAddressIsValidBech32(address, coinType, hrp)
779
+ },
780
+ isValidSS58(address: string, coinType: number, ss58Prefix: number): boolean {
781
+ return ExpoWalletCore.anyAddressIsValidSS58(address, coinType, ss58Prefix)
782
+ },
783
+ createWithString(address: string, coinType: number): NativeAnyAddress {
784
+ const desc = ExpoWalletCore.anyAddressCreateWithString(address, coinType)
785
+ const dataB64 = ExpoWalletCore.anyAddressData(address, coinType)
786
+ return new NativeAnyAddress(desc, dataB64)
787
+ },
788
+ createBech32WithPublicKey(
789
+ publicKey: NativePublicKey,
790
+ coinType: number,
791
+ hrp: string
792
+ ): NativeAnyAddress {
793
+ const desc = ExpoWalletCore.anyAddressCreateBech32WithPublicKey(
794
+ publicKey._handle,
795
+ coinType,
796
+ hrp
797
+ )
798
+ return new NativeAnyAddress(desc)
799
+ },
800
+ createBech32(address: string, coinType: number, hrp: string): NativeAnyAddress {
801
+ const desc = ExpoWalletCore.anyAddressCreateBech32(address, coinType, hrp)
802
+ return new NativeAnyAddress(desc)
803
+ },
804
+ },
805
+
806
+ // --- TransactionCompiler ---
807
+ TransactionCompiler: {
808
+ preImageHashes(coinType: number, txInputData: Uint8Array): Uint8Array {
809
+ const result = ExpoWalletCore.preImageHashes(coinType, toBase64(txInputData))
810
+ return fromBase64(result)
811
+ },
812
+ compileWithSignatures(
813
+ coinType: number,
814
+ txInputData: Uint8Array,
815
+ signatures: NativeDataVector,
816
+ publicKeys: NativeDataVector
817
+ ): Uint8Array {
818
+ const result = ExpoWalletCore.compileWithSignatures(
819
+ coinType,
820
+ toBase64(txInputData),
821
+ signatures.items,
822
+ publicKeys.items
823
+ )
824
+ return fromBase64(result)
825
+ },
826
+ },
827
+
828
+ // --- DataVector ---
829
+ DataVector: {
830
+ create(): NativeDataVector {
831
+ return new NativeDataVector()
832
+ },
833
+ },
834
+
835
+ // --- HDWallet ---
836
+ HDWallet: {
837
+ createWithMnemonic(mnemonic: string, passphrase: string = ''): NativeHDWallet {
838
+ const handle = ExpoWalletCore.hdWalletCreate(mnemonic, passphrase)
839
+ return new NativeHDWallet(handle)
840
+ },
841
+ },
842
+
843
+ // --- PrivateKey ---
844
+ PrivateKey: {
845
+ create(): NativePrivateKey {
846
+ const handle = ExpoWalletCore.privateKeyCreate()
847
+ return new NativePrivateKey(handle)
848
+ },
849
+ },
850
+
851
+ // --- AnySigner ---
852
+ AnySigner: {
853
+ plan(txInputData: Uint8Array, coinType: number): Uint8Array {
854
+ const result = ExpoWalletCore.anySignerPlan(toBase64(txInputData), coinType)
855
+ return fromBase64(result)
856
+ },
857
+ },
858
+
859
+ // --- HexCoding ---
860
+ HexCoding: {
861
+ decode(hex: string): Uint8Array {
862
+ const result = ExpoWalletCore.hexDecode(hex)
863
+ return fromBase64(result)
864
+ },
865
+ encode(data: Uint8Array): string {
866
+ return ExpoWalletCore.hexEncode(toBase64(data))
867
+ },
868
+ },
869
+
870
+ // --- Bech32 ---
871
+ Bech32: {
872
+ encode(hrp: string, data: Uint8Array): string {
873
+ return ExpoWalletCore.bech32Encode(hrp, toBase64(data))
874
+ },
875
+ },
876
+
877
+ // --- BitcoinScript ---
878
+ BitcoinScript: {
879
+ buildPayToWitnessPubkeyHash(hash: Uint8Array): { data: () => Uint8Array } {
880
+ const result = ExpoWalletCore.bitcoinScriptBuildPayToWitnessPubkeyHash(toBase64(hash))
881
+ return { data: () => fromBase64(result) }
882
+ },
883
+ buildPayToPublicKeyHash(hash: Uint8Array): { data: () => Uint8Array } {
884
+ const result = ExpoWalletCore.bitcoinScriptBuildPayToPublicKeyHash(toBase64(hash))
885
+ return { data: () => fromBase64(result) }
886
+ },
887
+ lockScriptForAddress(address: string, coinType: number): { data: () => Uint8Array } {
888
+ const result = ExpoWalletCore.bitcoinScriptLockScriptForAddress(address, coinType)
889
+ return { data: () => fromBase64(result) }
890
+ },
891
+ hashTypeForCoin(coinType: number): number {
892
+ return ExpoWalletCore.bitcoinScriptHashTypeForCoin(coinType)
893
+ },
894
+ },
895
+
896
+ // --- TONAddressConverter ---
897
+ TONAddressConverter: {
898
+ toUserFriendly(address: string): string {
899
+ return ExpoWalletCore.tonAddressToUserFriendly(address)
900
+ },
901
+ },
902
+
903
+ // --- EthereumAbiFunction ---
904
+ EthereumAbiFunction: {
905
+ createWithString(name: string): NativeEthereumAbiFunction {
906
+ return new NativeEthereumAbiFunction(name)
907
+ },
908
+ },
909
+
910
+ // --- EthereumAbi ---
911
+ EthereumAbi: {
912
+ encode(fn: NativeEthereumAbiFunction): Uint8Array {
913
+ return fn.encode()
914
+ },
915
+ encodeTyped(messageJson: string): string {
916
+ return ExpoWalletCore.ethereumAbiEncodeTyped(messageJson)
917
+ },
918
+ },
919
+
920
+ // --- Mnemonic ---
921
+ Mnemonic: {
922
+ isValid(mnemonic: string): boolean {
923
+ return ExpoWalletCore.mnemonicIsValid(mnemonic)
924
+ },
925
+ },
926
+
927
+ // --- SolanaAddress ---
928
+ SolanaAddress: {
929
+ createWithString(address: string): {
930
+ defaultTokenAddress(tokenMintAddress: string): string
931
+ } {
932
+ return {
933
+ defaultTokenAddress(tokenMintAddress: string): string {
934
+ return ExpoWalletCore.solanaAddressDefaultTokenAddress(address, tokenMintAddress)
935
+ },
936
+ }
937
+ },
938
+ },
939
+ }
940
+
941
+ NativeWalletCore.instance = wc
942
+ return wc
943
+ }
944
+ }
945
+
946
+ // Re-export the raw native module for direct access
947
+ export { default as ExpoWalletCore } from './ExpoWalletCoreModule'