@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/android/build.gradle +68 -0
- package/android/src/main/java/expo/modules/walletcore/ExpoWalletCoreModule.kt +349 -0
- package/expo-module.config.json +5 -0
- package/ios/ExpoWalletCore.podspec +17 -0
- package/ios/ExpoWalletCoreModule.swift +547 -0
- package/package.json +26 -0
- package/src/ExpoWalletCoreModule.ts +226 -0
- package/src/index.ts +947 -0
|
@@ -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
|
+
}
|