@vultisig/walletcore-native 0.1.1 → 0.2.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # @vultisig/walletcore-native
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1541](https://github.com/vultisig/vultisig-sdk/pull/1541) [`b9f81af`](https://github.com/vultisig/vultisig-sdk/commit/b9f81af9065a5c0bfc2f86f8fb20aa51e670ab77) Thanks [@realpaaao](https://github.com/realpaaao)! - Add Robinhood Chain (Arbitrum Orbit EVM L2, chain id 4663, ETH gas). Swaps enabled via LiFi and KyberSwap.
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - [#272](https://github.com/vultisig/vultisig-sdk/pull/272) [`496fa54`](https://github.com/vultisig/vultisig-sdk/commit/496fa54a7132d14c82933f27b78b428d4c0caf4a) Thanks [@NeOMakinG](https://github.com/NeOMakinG)! - Fix Android Kotlin compilation against TrustWallet JNI bindings
14
+ - Import `wallet.core.java.AnySigner` (lives outside `wallet.core.jni.*`)
15
+ - Fix `anyAddressIsValidSS58` return type (`Nothing` -> `Boolean`) so Kotlin can reify the type parameter
16
+ - Use `AnySigner.nativePlan(byte[], int)` instead of protobuf `plan()` overload that requires 3 params
17
+ - Pass required `bounceable` and `testnet` boolean params to `TONAddressConverter.toUserFriendly`
@@ -47,7 +47,8 @@ repositories {
47
47
  username = System.getenv("GITHUB_USER")
48
48
  ?: System.getenv("GITHUB_ACTOR")
49
49
  ?: "token"
50
- password = System.getenv("GITHUB_TOKEN")
50
+ password = System.getenv("TRUSTWALLET_PAT")
51
+ ?: System.getenv("GITHUB_TOKEN")
51
52
  ?: System.getenv("GH_TOKEN")
52
53
  ?: ""
53
54
  }
@@ -60,7 +61,7 @@ repositories {
60
61
  dependencies {
61
62
  implementation project(':expo-modules-core')
62
63
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.8.10')}"
63
- implementation 'com.trustwallet:wallet-core:4.3.22'
64
+ implementation 'com.trustwallet:wallet-core:4.7.0'
64
65
  }
65
66
 
66
67
  def safeExtGet(prop, fallback) {
@@ -3,6 +3,7 @@ package expo.modules.walletcore
3
3
  import expo.modules.kotlin.modules.Module
4
4
  import expo.modules.kotlin.modules.ModuleDefinition
5
5
  import wallet.core.jni.*
6
+ import wallet.core.java.AnySigner
6
7
 
7
8
  class ExpoWalletCoreModule : Module() {
8
9
  // NOTE: nextHandle and the key/wallet maps are accessed from the Expo module
@@ -64,6 +65,7 @@ class ExpoWalletCoreModule : Module() {
64
65
  "cronosChain" to CoinType.CRONOSCHAIN,
65
66
  "blast" to CoinType.BLAST,
66
67
  "zksync" to CoinType.ZKSYNC,
68
+ "robinhoodChain" to CoinType.ROBINHOODCHAIN,
67
69
  "osmosis" to CoinType.OSMOSIS,
68
70
  "terraV2" to CoinType.TERRAV2,
69
71
  "noble" to CoinType.NOBLE,
@@ -143,15 +145,8 @@ class ExpoWalletCoreModule : Module() {
143
145
  AnyAddress.isValidBech32(address, CoinType.createFromValue(coinType), hrp)
144
146
  }
145
147
 
146
- // The Trust Wallet Core Android JNI binding does not expose an SS58-prefix
147
- // overload on AnyAddress. Throw rather than returning a potentially incorrect
148
- // result, so callers know this code path is not supported yet.
149
- Function("anyAddressIsValidSS58") { _address: String, _coinType: Int, _ss58Prefix: Int ->
150
- throw Exception(
151
- "anyAddressIsValidSS58 is not supported on Android: the JNI binding " +
152
- "does not expose an SS58-prefix overload. Use anyAddressIsValid as a " +
153
- "fallback (ignores ss58Prefix) or implement a pure-Kotlin SS58 check."
154
- )
148
+ Function("anyAddressIsValidSS58") { address: String, coinType: Int, ss58Prefix: Int ->
149
+ AnyAddress.isValidSS58(address, CoinType.createFromValue(coinType), ss58Prefix)
155
150
  }
156
151
 
157
152
  Function("anyAddressCreateWithString") { address: String, coinType: Int ->
@@ -198,7 +193,7 @@ class ExpoWalletCoreModule : Module() {
198
193
  // AnySigner
199
194
  Function("anySignerPlan") { txInputDataBase64: String, coinType: Int ->
200
195
  val txData = android.util.Base64.decode(txInputDataBase64, android.util.Base64.NO_WRAP)
201
- val result = AnySigner.plan(txData, CoinType.createFromValue(coinType))
196
+ val result = AnySigner.nativePlan(txData, coinType)
202
197
  android.util.Base64.encodeToString(result, android.util.Base64.NO_WRAP)
203
198
  }
204
199
 
@@ -338,7 +333,7 @@ class ExpoWalletCoreModule : Module() {
338
333
 
339
334
  // TONAddressConverter
340
335
  Function("tonAddressToUserFriendly") { address: String ->
341
- TONAddressConverter.toUserFriendly(address)
336
+ TONAddressConverter.toUserFriendly(address, false, false)
342
337
  }
343
338
 
344
339
  // SolanaAddress
@@ -13,5 +13,5 @@ Pod::Spec.new do |s|
13
13
  s.source_files = '*.swift'
14
14
 
15
15
  s.dependency 'ExpoModulesCore'
16
- s.dependency 'TrustWalletCore', '4.3.22'
16
+ s.dependency 'TrustWalletCore', '4.7.0'
17
17
  end
@@ -92,6 +92,7 @@ public class ExpoWalletCoreModule: Module {
92
92
  "polygon": .polygon, "optimism": .optimism,
93
93
  "cronosChain": .cronosChain, "blast": .blast,
94
94
  "zksync": .zksync, "osmosis": .osmosis,
95
+ "robinhoodChain": .robinhoodChain,
95
96
  "terraV2": .terraV2, "terra": .terra,
96
97
  "noble": .noble, "kujira": .kujira,
97
98
  "dydx": .dydx, "akash": .akash, "mantle": .mantle, "sei": .sei,
@@ -342,7 +343,13 @@ public class ExpoWalletCoreModule: Module {
342
343
  throw NSError(domain: "ExpoWalletCore", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HDWallet handle"])
343
344
  }
344
345
  let ct = try coinTypeFromValue(coinType)
345
- let key = wallet.getKey(coin: ct, derivationPath: derivationPath)
346
+ guard let key = wallet.getKey(coin: ct, derivationPath: derivationPath) else {
347
+ throw NSError(
348
+ domain: "ExpoWalletCore",
349
+ code: -1,
350
+ userInfo: [NSLocalizedDescriptionKey: "Failed to derive PrivateKey for CoinType raw value \(coinType)"]
351
+ )
352
+ }
346
353
  return storePrivateKey(key)
347
354
  }
348
355
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/walletcore-native",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Native WalletCore bridge for Vultisig SDK (React Native / Expo)",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -15,10 +15,10 @@
15
15
  "@noble/hashes": "^1.8.0"
16
16
  },
17
17
  "peerDependencies": {
18
- "expo": ">=51.0.0"
18
+ "expo": ">=56.0.8"
19
19
  },
20
20
  "devDependencies": {
21
- "expo": ">=51.0.0"
21
+ "expo": ">=56.0.12"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public"
@@ -129,12 +129,7 @@ interface ExpoWalletCoreModuleType {
129
129
  hdWalletGetAddressForCoin(handle: number, coinType: number): string
130
130
 
131
131
  /** Get the extended private key. */
132
- hdWalletGetExtendedPrivateKey(
133
- handle: number,
134
- purposeValue: number,
135
- coinType: number,
136
- versionValue: number
137
- ): string
132
+ hdWalletGetExtendedPrivateKey(handle: number, purposeValue: number, coinType: number, versionValue: number): string
138
133
 
139
134
  /** Free an HDWallet handle. */
140
135
  freeHDWallet(handle: number): void
@@ -0,0 +1,41 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ const native = vi.hoisted(() => ({
4
+ derivationPath: vi.fn((_coinType: number) => "m/44'/60'/0'/0/0"),
5
+ deriveAddressFromPublicKey: vi.fn((_coinType: number, _handle: number) => '0x1234'),
6
+ }))
7
+
8
+ vi.mock('expo-modules-core', () => ({
9
+ requireNativeModule: () => native,
10
+ }))
11
+
12
+ import { type NativePublicKeyInstance, NativeWalletCore } from './index'
13
+
14
+ describe('NativeWalletCore Robinhood contract', () => {
15
+ beforeEach(() => vi.clearAllMocks())
16
+
17
+ it('passes the canonical finite coin type to the native derivation bridge', () => {
18
+ const walletCore = NativeWalletCore.getInstance()
19
+ const coinType = walletCore.CoinType.robinhoodChain
20
+
21
+ expect(coinType).toBe(10_004_663)
22
+ expect(Number.isInteger(coinType)).toBe(true)
23
+ expect(walletCore.CoinTypeExt.derivationPath(coinType)).toBe("m/44'/60'/0'/0/0")
24
+ expect(native.derivationPath).toHaveBeenCalledWith(10_004_663)
25
+ })
26
+
27
+ it('uses the same EVM address derivation contract as Ethereum', () => {
28
+ const walletCore = NativeWalletCore.getInstance()
29
+ const publicKey = { _handle: 7 } as NativePublicKeyInstance
30
+
31
+ const ethereumAddress = walletCore.CoinTypeExt.deriveAddressFromPublicKey(walletCore.CoinType.ethereum, publicKey)
32
+ const robinhoodAddress = walletCore.CoinTypeExt.deriveAddressFromPublicKey(
33
+ walletCore.CoinType.robinhoodChain,
34
+ publicKey
35
+ )
36
+
37
+ expect(robinhoodAddress).toBe(ethereumAddress)
38
+ expect(native.deriveAddressFromPublicKey).toHaveBeenNthCalledWith(1, 60, 7)
39
+ expect(native.deriveAddressFromPublicKey).toHaveBeenNthCalledWith(2, 10_004_663, 7)
40
+ })
41
+ })
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * Provides a JS object matching the WalletCore API shape expected by the SDK.
6
6
  */
7
7
  import { keccak_256 } from '@noble/hashes/sha3'
8
+
8
9
  import ExpoWalletCore from './ExpoWalletCoreModule'
9
10
 
10
11
  // ---------------------------------------------------------------------------
@@ -95,13 +96,22 @@ export interface WalletCoreLike {
95
96
  isValidBech32(address: string, coinType: number, hrp: string): boolean
96
97
  isValidSS58(address: string, coinType: number, ss58Prefix: number): boolean
97
98
  createWithString(address: string, coinType: number): NativeAnyAddressInstance
98
- createBech32WithPublicKey(publicKey: NativePublicKeyInstance, coinType: number, hrp: string): NativeAnyAddressInstance
99
+ createBech32WithPublicKey(
100
+ publicKey: NativePublicKeyInstance,
101
+ coinType: number,
102
+ hrp: string
103
+ ): NativeAnyAddressInstance
99
104
  createBech32(address: string, coinType: number, hrp: string): NativeAnyAddressInstance
100
105
  }
101
106
 
102
107
  TransactionCompiler: {
103
108
  preImageHashes(coinType: number, txInputData: Uint8Array): Uint8Array
104
- compileWithSignatures(coinType: number, txInputData: Uint8Array, signatures: NativeDataVectorInstance, publicKeys: NativeDataVectorInstance): Uint8Array
109
+ compileWithSignatures(
110
+ coinType: number,
111
+ txInputData: Uint8Array,
112
+ signatures: NativeDataVectorInstance,
113
+ publicKeys: NativeDataVectorInstance
114
+ ): Uint8Array
105
115
  }
106
116
 
107
117
  DataVector: {
@@ -161,31 +171,27 @@ export interface WalletCoreLike {
161
171
  // ---------------------------------------------------------------------------
162
172
  // Helpers — base64 <-> Uint8Array
163
173
  // ---------------------------------------------------------------------------
174
+ //
175
+ // Both helpers assume `Buffer` is globally available. On RN/Hermes that is
176
+ // guaranteed because `@vultisig/sdk`'s react-native entrypoint installs the
177
+ // buffer polyfill before any module graph import runs; on Node it is a
178
+ // builtin. Consumers importing `@vultisig/walletcore-native` directly
179
+ // (without going through @vultisig/sdk's RN entrypoint) must polyfill
180
+ // `globalThis.Buffer` themselves before invoking any WalletCore method.
164
181
 
165
182
  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)
183
+ const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength)
184
+ return buf.toString('base64')
174
185
  }
175
186
 
176
187
  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
188
+ return new Uint8Array(Buffer.from(b64, 'base64'))
183
189
  }
184
190
 
185
191
  // ---------------------------------------------------------------------------
186
192
  // 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
193
+ // Values sourced from TrustWallet wallet-core v4.7.0:
194
+ // https://github.com/trustwallet/wallet-core/blob/4.7.0/include/TrustWalletCore/TWCoinType.h
189
195
  // ---------------------------------------------------------------------------
190
196
 
191
197
  const CoinTypeValues: Record<string, number> = {
@@ -216,6 +222,7 @@ const CoinTypeValues: Record<string, number> = {
216
222
  cronosChain: 10000025,
217
223
  blast: 81457,
218
224
  zksync: 10000324,
225
+ robinhoodChain: 10004663,
219
226
  osmosis: 10000118,
220
227
  terraV2: 10000330,
221
228
  noble: 18000118,
@@ -601,9 +608,7 @@ function abiEncode(name: string, params: AbiParam[]): Uint8Array {
601
608
 
602
609
  // Concatenate: selector + heads + tails
603
610
  const totalLen =
604
- selector.length +
605
- headParts.reduce((sum, p) => sum + p.length, 0) +
606
- tailParts.reduce((sum, p) => sum + p.length, 0)
611
+ selector.length + headParts.reduce((sum, p) => sum + p.length, 0) + tailParts.reduce((sum, p) => sum + p.length, 0)
607
612
 
608
613
  const result = new Uint8Array(totalLen)
609
614
  let offset = 0
@@ -785,16 +790,8 @@ export class NativeWalletCore {
785
790
  const dataB64 = ExpoWalletCore.anyAddressData(address, coinType)
786
791
  return new NativeAnyAddress(desc, dataB64)
787
792
  },
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
- )
793
+ createBech32WithPublicKey(publicKey: NativePublicKey, coinType: number, hrp: string): NativeAnyAddress {
794
+ const desc = ExpoWalletCore.anyAddressCreateBech32WithPublicKey(publicKey._handle, coinType, hrp)
798
795
  return new NativeAnyAddress(desc)
799
796
  },
800
797
  createBech32(address: string, coinType: number, hrp: string): NativeAnyAddress {