@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.
@@ -0,0 +1,547 @@
1
+ import ExpoModulesCore
2
+ import WalletCore
3
+
4
+ // MARK: - Handle Management
5
+
6
+ // NOTE: These module-level variables are accessed from the Expo module dispatch
7
+ // thread. Expo modules serialize function handler calls onto a single background
8
+ // serial queue, so concurrent mutation is not expected. If this assumption ever
9
+ // changes (e.g. concurrent AsyncFunction calls are added), access to nextHandle
10
+ // and the maps below must be protected with a DispatchQueue or a lock.
11
+ private var nextHandle: Int = 1
12
+ private var publicKeys: [Int: PublicKey] = [:]
13
+ private var privateKeys: [Int: PrivateKey] = [:]
14
+ private var hdWallets: [Int: HDWallet] = [:]
15
+
16
+ private func storePublicKey(_ pk: PublicKey) -> Int {
17
+ let h = nextHandle; nextHandle += 1
18
+ publicKeys[h] = pk
19
+ return h
20
+ }
21
+
22
+ private func storePrivateKey(_ pk: PrivateKey) -> Int {
23
+ let h = nextHandle; nextHandle += 1
24
+ privateKeys[h] = pk
25
+ return h
26
+ }
27
+
28
+ private func storeHDWallet(_ w: HDWallet) -> Int {
29
+ let h = nextHandle; nextHandle += 1
30
+ hdWallets[h] = w
31
+ return h
32
+ }
33
+
34
+ // MARK: - CoinType mapping
35
+
36
+ private func coinTypeFromValue(_ value: Int) throws -> CoinType {
37
+ guard let result = CoinType(rawValue: UInt32(value)) else {
38
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid CoinType raw value \(value)"])
39
+ }
40
+ return result
41
+ }
42
+
43
+ private func publicKeyTypeFromValue(_ value: Int) throws -> PublicKeyType {
44
+ guard let result = PublicKeyType(rawValue: UInt32(value)) else {
45
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PublicKeyType raw value \(value)"])
46
+ }
47
+ return result
48
+ }
49
+
50
+ private func curveFromValue(_ value: Int) throws -> Curve {
51
+ guard let result = Curve(rawValue: UInt32(value)) else {
52
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid Curve raw value \(value)"])
53
+ }
54
+ return result
55
+ }
56
+
57
+ private func purposeFromValue(_ value: Int) throws -> Purpose {
58
+ guard let result = Purpose(rawValue: UInt32(value)) else {
59
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid Purpose raw value \(value)"])
60
+ }
61
+ return result
62
+ }
63
+
64
+ private func hdVersionFromValue(_ value: Int) throws -> HDVersion {
65
+ guard let result = HDVersion(rawValue: UInt32(value)) else {
66
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDVersion raw value \(value)"])
67
+ }
68
+ return result
69
+ }
70
+
71
+ // MARK: - Module
72
+
73
+ public class ExpoWalletCoreModule: Module {
74
+ public func definition() -> ModuleDefinition {
75
+ Name("ExpoWalletCore")
76
+
77
+ // =====================================================================
78
+ // CoinType
79
+ // =====================================================================
80
+
81
+ Function("coinTypeValue") { (name: String) -> Int in
82
+ // Map string name to CoinType raw value
83
+ let mapping: [String: CoinType] = [
84
+ "bitcoin": .bitcoin, "litecoin": .litecoin, "dogecoin": .dogecoin,
85
+ "dash": .dash, "ethereum": .ethereum, "cosmos": .cosmos,
86
+ "zcash": .zcash, "ripple": .xrp, "xrp": .xrp,
87
+ "bitcoinCash": .bitcoinCash, "tron": .tron,
88
+ "polkadot": .polkadot, "ton": .ton, "solana": .solana,
89
+ "thorchain": .thorchain, "sui": .sui, "cardano": .cardano,
90
+ "smartChain": .smartChain, "arbitrum": .arbitrum,
91
+ "avalancheCChain": .avalancheCChain, "base": .base,
92
+ "polygon": .polygon, "optimism": .optimism,
93
+ "cronosChain": .cronosChain, "blast": .blast,
94
+ "zksync": .zksync, "osmosis": .osmosis,
95
+ "terraV2": .terraV2, "terra": .terra,
96
+ "noble": .noble, "kujira": .kujira,
97
+ "dydx": .dydx, "akash": .akash, "mantle": .mantle, "sei": .sei,
98
+ ]
99
+ return Int(mapping[name]?.rawValue ?? 0)
100
+ }
101
+
102
+ // =====================================================================
103
+ // CoinTypeExt
104
+ // =====================================================================
105
+
106
+ Function("derivationPath") { (coinType: Int) -> String in
107
+ let ct = try coinTypeFromValue(coinType)
108
+ return ct.derivationPath()
109
+ }
110
+
111
+ Function("deriveAddressFromPublicKey") { (coinType: Int, publicKeyHandle: Int) -> String in
112
+ guard let pk = publicKeys[publicKeyHandle] else {
113
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PublicKey handle"])
114
+ }
115
+ let ct = try coinTypeFromValue(coinType)
116
+ return ct.deriveAddressFromPublicKey(publicKey: pk)
117
+ }
118
+
119
+ Function("chainId") { (coinType: Int) -> String in
120
+ let ct = try coinTypeFromValue(coinType)
121
+ return ct.chainId
122
+ }
123
+
124
+ Function("ss58Prefix") { (coinType: Int) -> Int in
125
+ let ct = try coinTypeFromValue(coinType)
126
+ return Int(ct.ss58Prefix)
127
+ }
128
+
129
+ // =====================================================================
130
+ // PublicKey
131
+ // =====================================================================
132
+
133
+ Function("publicKeyCreateWithData") { (dataBase64: String, typeValue: Int) -> Int in
134
+ guard let data = Data(base64Encoded: dataBase64) else {
135
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data"])
136
+ }
137
+ let pkType = try publicKeyTypeFromValue(typeValue)
138
+ guard let pk = PublicKey(data: data, type: pkType) else {
139
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create PublicKey"])
140
+ }
141
+ return storePublicKey(pk)
142
+ }
143
+
144
+ Function("publicKeyData") { (handle: Int) -> String in
145
+ guard let pk = publicKeys[handle] else {
146
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PublicKey handle"])
147
+ }
148
+ return pk.data.base64EncodedString()
149
+ }
150
+
151
+ Function("publicKeyUncompressed") { (handle: Int) -> Int in
152
+ guard let pk = publicKeys[handle] else {
153
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PublicKey handle"])
154
+ }
155
+ return storePublicKey(pk.uncompressed)
156
+ }
157
+
158
+ Function("publicKeyCompressed") { (handle: Int) -> Int in
159
+ guard let pk = publicKeys[handle] else {
160
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PublicKey handle"])
161
+ }
162
+ return storePublicKey(pk.compressed)
163
+ }
164
+
165
+ Function("publicKeyVerify") { (handle: Int, signatureBase64: String, messageBase64: String) -> Bool in
166
+ guard let pk = publicKeys[handle],
167
+ let sig = Data(base64Encoded: signatureBase64),
168
+ let msg = Data(base64Encoded: messageBase64) else { return false }
169
+ return pk.verify(signature: sig, message: msg)
170
+ }
171
+
172
+ Function("publicKeyVerifyAsDER") { (handle: Int, signatureBase64: String, messageBase64: String) -> Bool in
173
+ guard let pk = publicKeys[handle],
174
+ let sig = Data(base64Encoded: signatureBase64),
175
+ let msg = Data(base64Encoded: messageBase64) else { return false }
176
+ return pk.verifyAsDER(signature: sig, message: msg)
177
+ }
178
+
179
+ Function("freePublicKey") { (handle: Int) in
180
+ publicKeys.removeValue(forKey: handle)
181
+ }
182
+
183
+ // =====================================================================
184
+ // AnyAddress
185
+ // =====================================================================
186
+
187
+ Function("anyAddressIsValid") { (address: String, coinType: Int) -> Bool in
188
+ let ct = try coinTypeFromValue(coinType)
189
+ return AnyAddress.isValid(string: address, coin: ct)
190
+ }
191
+
192
+ Function("anyAddressIsValidBech32") { (address: String, coinType: Int, hrp: String) -> Bool in
193
+ let ct = try coinTypeFromValue(coinType)
194
+ return AnyAddress.isValidBech32(string: address, coin: ct, hrp: hrp)
195
+ }
196
+
197
+ Function("anyAddressIsValidSS58") { (address: String, coinType: Int, ss58Prefix: Int) -> Bool in
198
+ let ct = try coinTypeFromValue(coinType)
199
+ return AnyAddress.isValidSS58(string: address, coin: ct, ss58Prefix: UInt32(ss58Prefix))
200
+ }
201
+
202
+ Function("anyAddressCreateWithString") { (address: String, coinType: Int) -> String in
203
+ let ct = try coinTypeFromValue(coinType)
204
+ guard let addr = AnyAddress(string: address, coin: ct) else {
205
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid address"])
206
+ }
207
+ return addr.description
208
+ }
209
+
210
+ Function("anyAddressCreateBech32WithPublicKey") { (publicKeyHandle: Int, coinType: Int, hrp: String) -> String in
211
+ guard let pk = publicKeys[publicKeyHandle] else {
212
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PublicKey handle"])
213
+ }
214
+ let ct = try coinTypeFromValue(coinType)
215
+ let addr = AnyAddress(publicKey: pk, coin: ct, hrp: hrp)
216
+ return addr.description
217
+ }
218
+
219
+ Function("anyAddressCreateBech32") { (address: String, coinType: Int, hrp: String) -> String in
220
+ let ct = try coinTypeFromValue(coinType)
221
+ guard let addr = AnyAddress(string: address, coin: ct, hrp: hrp) else {
222
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid bech32 address"])
223
+ }
224
+ return addr.description
225
+ }
226
+
227
+ Function("anyAddressData") { (address: String, coinType: Int) -> String in
228
+ let ct = try coinTypeFromValue(coinType)
229
+ guard let addr = AnyAddress(string: address, coin: ct) else {
230
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid address"])
231
+ }
232
+ return addr.data.base64EncodedString()
233
+ }
234
+
235
+ // =====================================================================
236
+ // TransactionCompiler
237
+ // =====================================================================
238
+
239
+ Function("preImageHashes") { (coinType: Int, txInputDataBase64: String) -> String in
240
+ guard let txData = Data(base64Encoded: txInputDataBase64) else {
241
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data"])
242
+ }
243
+ let ct = try coinTypeFromValue(coinType)
244
+ let result = TransactionCompiler.preImageHashes(coinType: ct, txInputData: txData)
245
+ return result.base64EncodedString()
246
+ }
247
+
248
+ Function("compileWithSignatures") { (coinType: Int, txInputDataBase64: String, signaturesBase64: [String], publicKeysBase64: [String]) -> String in
249
+ guard let txData = Data(base64Encoded: txInputDataBase64) else {
250
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data"])
251
+ }
252
+ let ct = try coinTypeFromValue(coinType)
253
+
254
+ let signatures = DataVector()
255
+ for (i, sigB64) in signaturesBase64.enumerated() {
256
+ guard let sigData = Data(base64Encoded: sigB64) else {
257
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 signature at index \(i)"])
258
+ }
259
+ signatures.add(data: sigData)
260
+ }
261
+
262
+ let pubkeys = DataVector()
263
+ for (i, pkB64) in publicKeysBase64.enumerated() {
264
+ guard let pkData = Data(base64Encoded: pkB64) else {
265
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 public key at index \(i)"])
266
+ }
267
+ pubkeys.add(data: pkData)
268
+ }
269
+
270
+ let result = TransactionCompiler.compileWithSignatures(
271
+ coinType: ct,
272
+ txInputData: txData,
273
+ signatures: signatures,
274
+ publicKeys: pubkeys
275
+ )
276
+ return result.base64EncodedString()
277
+ }
278
+
279
+ // =====================================================================
280
+ // AnySigner
281
+ // =====================================================================
282
+
283
+ Function("anySignerPlan") { (txInputDataBase64: String, coinType: Int) -> String in
284
+ guard let txData = Data(base64Encoded: txInputDataBase64) else {
285
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data"])
286
+ }
287
+ let ct = try coinTypeFromValue(coinType)
288
+ let result = AnySigner.nativePlan(data: txData, coin: ct)
289
+ return result.base64EncodedString()
290
+ }
291
+
292
+ // =====================================================================
293
+ // HDWallet
294
+ // =====================================================================
295
+
296
+ Function("hdWalletCreate") { (mnemonic: String, passphrase: String) -> Int in
297
+ guard let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) else {
298
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid mnemonic"])
299
+ }
300
+ return storeHDWallet(wallet)
301
+ }
302
+
303
+ Function("hdWalletGetMasterKey") { (handle: Int, curveValue: Int) -> Int in
304
+ guard let wallet = hdWallets[handle] else {
305
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
306
+ }
307
+ let curve = try curveFromValue(curveValue)
308
+ let key = wallet.getMasterKey(curve: curve)
309
+ return storePrivateKey(key)
310
+ }
311
+
312
+ Function("hdWalletGetKeyForCoin") { (handle: Int, coinType: Int) -> Int in
313
+ guard let wallet = hdWallets[handle] else {
314
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
315
+ }
316
+ let ct = try coinTypeFromValue(coinType)
317
+ let key = wallet.getKeyForCoin(coin: ct)
318
+ return storePrivateKey(key)
319
+ }
320
+
321
+ Function("hdWalletGetKeyDerivation") { (handle: Int, coinType: Int, derivationValue: Int) -> Int in
322
+ guard let wallet = hdWallets[handle] else {
323
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
324
+ }
325
+ let ct = try coinTypeFromValue(coinType)
326
+ let derivation = Derivation(rawValue: UInt32(derivationValue)) ?? .default
327
+ let key = wallet.getKeyDerivation(coin: ct, derivation: derivation)
328
+ return storePrivateKey(key)
329
+ }
330
+
331
+ Function("hdWalletGetAddressDerivation") { (handle: Int, coinType: Int, derivationValue: Int) -> String in
332
+ guard let wallet = hdWallets[handle] else {
333
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
334
+ }
335
+ let ct = try coinTypeFromValue(coinType)
336
+ let derivation = Derivation(rawValue: UInt32(derivationValue)) ?? .default
337
+ return wallet.getAddressDerivation(coin: ct, derivation: derivation)
338
+ }
339
+
340
+ Function("hdWalletGetKey") { (handle: Int, coinType: Int, derivationPath: String) -> Int in
341
+ guard let wallet = hdWallets[handle] else {
342
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
343
+ }
344
+ let ct = try coinTypeFromValue(coinType)
345
+ let key = wallet.getKey(coin: ct, derivationPath: derivationPath)
346
+ return storePrivateKey(key)
347
+ }
348
+
349
+ Function("hdWalletGetAddressForCoin") { (handle: Int, coinType: Int) -> String in
350
+ guard let wallet = hdWallets[handle] else {
351
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
352
+ }
353
+ let ct = try coinTypeFromValue(coinType)
354
+ return wallet.getAddressForCoin(coin: ct)
355
+ }
356
+
357
+ Function("hdWalletGetExtendedPrivateKey") { (handle: Int, purposeValue: Int, coinType: Int, versionValue: Int) -> String in
358
+ guard let wallet = hdWallets[handle] else {
359
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
360
+ }
361
+ let purpose = try purposeFromValue(purposeValue)
362
+ let ct = try coinTypeFromValue(coinType)
363
+ let version = try hdVersionFromValue(versionValue)
364
+ return wallet.getExtendedPrivateKey(purpose: purpose, coin: ct, version: version)
365
+ }
366
+
367
+ Function("freeHDWallet") { (handle: Int) in
368
+ hdWallets.removeValue(forKey: handle)
369
+ }
370
+
371
+ // =====================================================================
372
+ // PrivateKey
373
+ // =====================================================================
374
+
375
+ Function("privateKeyCreate") { () -> Int in
376
+ let key = PrivateKey()
377
+ return storePrivateKey(key)
378
+ }
379
+
380
+ Function("privateKeyData") { (handle: Int) -> String in
381
+ guard let key = privateKeys[handle] else {
382
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PrivateKey handle"])
383
+ }
384
+ return key.data.base64EncodedString()
385
+ }
386
+
387
+ Function("privateKeyGetPublicKeySecp256k1") { (handle: Int, compressed: Bool) -> Int in
388
+ guard let key = privateKeys[handle] else {
389
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PrivateKey handle"])
390
+ }
391
+ let pk = key.getPublicKeySecp256k1(compressed: compressed)
392
+ return storePublicKey(pk)
393
+ }
394
+
395
+ Function("privateKeyGetPublicKeyEd25519") { (handle: Int) -> Int in
396
+ guard let key = privateKeys[handle] else {
397
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid PrivateKey handle"])
398
+ }
399
+ let pk = key.getPublicKeyEd25519()
400
+ return storePublicKey(pk)
401
+ }
402
+
403
+ Function("freePrivateKey") { (handle: Int) in
404
+ privateKeys.removeValue(forKey: handle)
405
+ }
406
+
407
+ // =====================================================================
408
+ // HexCoding
409
+ // =====================================================================
410
+
411
+ Function("hexDecode") { (hex: String) -> String in
412
+ guard hex.count % 2 == 0 else {
413
+ throw NSError(
414
+ domain: "ExpoWalletCore",
415
+ code: -1,
416
+ userInfo: [NSLocalizedDescriptionKey: "Hex string must have even length, got \(hex.count)"]
417
+ )
418
+ }
419
+ let allowed = CharacterSet(charactersIn: "0123456789abcdefABCDEF")
420
+ guard hex.unicodeScalars.allSatisfy({ allowed.contains($0) }) else {
421
+ throw NSError(
422
+ domain: "ExpoWalletCore",
423
+ code: -1,
424
+ userInfo: [NSLocalizedDescriptionKey: "Hex string contains non-hex characters"]
425
+ )
426
+ }
427
+ var data = Data()
428
+ var index = hex.startIndex
429
+ while index < hex.endIndex {
430
+ let nextIndex = hex.index(index, offsetBy: 2)
431
+ let byteString = hex[index..<nextIndex]
432
+ guard let byte = UInt8(byteString, radix: 16) else {
433
+ throw NSError(
434
+ domain: "ExpoWalletCore",
435
+ code: -1,
436
+ userInfo: [NSLocalizedDescriptionKey: "Hex string contains non-hex characters"]
437
+ )
438
+ }
439
+ data.append(byte)
440
+ index = nextIndex
441
+ }
442
+ return data.base64EncodedString()
443
+ }
444
+
445
+ Function("hexEncode") { (dataBase64: String) -> String in
446
+ guard let data = Data(base64Encoded: dataBase64) else {
447
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data for hexEncode"])
448
+ }
449
+ return data.map { String(format: "%02x", $0) }.joined()
450
+ }
451
+
452
+ // =====================================================================
453
+ // Bech32
454
+ // =====================================================================
455
+
456
+ Function("bech32Encode") { (hrp: String, dataBase64: String) -> String in
457
+ guard let data = Data(base64Encoded: dataBase64) else {
458
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data for bech32Encode"])
459
+ }
460
+ return Bech32.encode(hrp: hrp, data: data)
461
+ }
462
+
463
+ // =====================================================================
464
+ // BitcoinScript
465
+ // =====================================================================
466
+
467
+ Function("bitcoinScriptBuildPayToWitnessPubkeyHash") { (hashBase64: String) -> String in
468
+ guard let hash = Data(base64Encoded: hashBase64) else {
469
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data for bitcoinScriptBuildPayToWitnessPubkeyHash"])
470
+ }
471
+ let script = BitcoinScript.buildPayToWitnessPubkeyHash(hash: hash)
472
+ return script.data.base64EncodedString()
473
+ }
474
+
475
+ Function("bitcoinScriptBuildPayToPublicKeyHash") { (hashBase64: String) -> String in
476
+ guard let hash = Data(base64Encoded: hashBase64) else {
477
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 data for bitcoinScriptBuildPayToPublicKeyHash"])
478
+ }
479
+ let script = BitcoinScript.buildPayToPublicKeyHash(hash: hash)
480
+ return script.data.base64EncodedString()
481
+ }
482
+
483
+ Function("bitcoinScriptLockScriptForAddress") { (address: String, coinType: Int) -> String in
484
+ let ct = try coinTypeFromValue(coinType)
485
+ let script = BitcoinScript.lockScriptForAddress(address: address, coin: ct)
486
+ return script.data.base64EncodedString()
487
+ }
488
+
489
+ Function("bitcoinScriptHashTypeForCoin") { (coinType: Int) -> Int in
490
+ let ct = try coinTypeFromValue(coinType)
491
+ return Int(BitcoinScript.hashTypeForCoin(coinType: ct))
492
+ }
493
+
494
+ // =====================================================================
495
+ // EthereumAbi (simplified — encode a function call)
496
+ // =====================================================================
497
+
498
+ Function("ethereumAbiEncode") { (functionName: String, params: String) -> String in
499
+ // WARNING: Only the 4-byte function selector is encoded. Full ABI encoding
500
+ // requires parsing param types/values and calling fn.addParam*(). Throw if
501
+ // the caller passes non-empty params to avoid silent data loss.
502
+ // TODO: Accept a structured param list (e.g. JSON) and call fn.addParam*().
503
+ if !params.isEmpty {
504
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "ethereumAbiEncode does not yet support params — only the 4-byte selector is encoded"])
505
+ }
506
+ let fn = EthereumAbiFunction(name: functionName)
507
+ let encoded = EthereumAbi.encode(fn: fn)
508
+ return encoded.base64EncodedString()
509
+ }
510
+
511
+ Function("ethereumAbiEncodeTyped") { (messageJson: String) -> String in
512
+ let encoded = EthereumAbi.encodeTyped(messageJson: messageJson)
513
+ return encoded.map { String(format: "%02x", $0) }.joined()
514
+ }
515
+
516
+ // =====================================================================
517
+ // Mnemonic
518
+ // =====================================================================
519
+
520
+ Function("mnemonicIsValid") { (mnemonic: String) -> Bool in
521
+ return Mnemonic.isValid(mnemonic: mnemonic)
522
+ }
523
+
524
+ // =====================================================================
525
+ // TONAddressConverter
526
+ // =====================================================================
527
+
528
+ Function("tonAddressToUserFriendly") { (address: String) -> String in
529
+ guard let result = TONAddressConverter.toUserFriendly(address: address, bounceable: false, testnet: false) else {
530
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to convert TON address"])
531
+ }
532
+ return result
533
+ }
534
+
535
+ // =====================================================================
536
+ // SolanaAddress
537
+ // =====================================================================
538
+
539
+ Function("solanaAddressDefaultTokenAddress") { (address: String, tokenMintAddress: String) -> String in
540
+ guard let solAddr = SolanaAddress(string: address),
541
+ let tokenAddr = solAddr.defaultTokenAddress(tokenMintAddress: tokenMintAddress) else {
542
+ throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid Solana address or token mint"])
543
+ }
544
+ return tokenAddr
545
+ }
546
+ }
547
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@vultisig/walletcore-native",
3
+ "version": "0.1.1",
4
+ "description": "Native WalletCore bridge for Vultisig SDK (React Native / Expo)",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "license": "MIT",
8
+ "files": [
9
+ "src",
10
+ "ios",
11
+ "android",
12
+ "expo-module.config.json"
13
+ ],
14
+ "dependencies": {
15
+ "@noble/hashes": "^1.8.0"
16
+ },
17
+ "peerDependencies": {
18
+ "expo": ">=51.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "expo": ">=51.0.0"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ }
26
+ }