@chainberry/trust-wallet-core 2.0.0 → 2.5.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.
Files changed (41) hide show
  1. package/{TrustWalletCoreModule.podspec → ChainberryTrustWalletCoreModule.podspec} +5 -4
  2. package/README.md +27 -25
  3. package/android/build.gradle +29 -6
  4. package/android/libs/README.md +34 -0
  5. package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.aar +0 -0
  6. package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.aar.md5 +1 -0
  7. package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.aar.sha1 +1 -0
  8. package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.pom +22 -0
  9. package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.pom.md5 +1 -0
  10. package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.pom.sha1 +1 -0
  11. package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar +0 -0
  12. package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar.md5 +1 -0
  13. package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar.sha1 +1 -0
  14. package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom +21 -0
  15. package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom.md5 +1 -0
  16. package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom.sha1 +1 -0
  17. package/android/libs/download.sh +52 -0
  18. package/android/src/androidTest/java/com/chainberry/trustwalletcore/AddressDerivationConformanceTest.kt +106 -0
  19. package/android/src/androidTest/java/com/chainberry/trustwalletcore/SigningConformanceTest.kt +186 -0
  20. package/android/src/main/java/com/chainberry/trustwalletcore/AmountParsing.kt +45 -0
  21. package/android/src/main/java/com/chainberry/trustwalletcore/Bech32.kt +68 -0
  22. package/android/src/main/java/com/chainberry/trustwalletcore/ChainSigning.kt +884 -0
  23. package/android/src/main/java/com/chainberry/trustwalletcore/ChainberryTrustWalletCoreModule.kt +227 -0
  24. package/android/src/main/java/com/chainberry/trustwalletcore/NativeWalletStore.kt +888 -0
  25. package/android/src/test/java/com/chainberry/trustwalletcore/AmountParsingConformanceTest.kt +57 -0
  26. package/android/src/test/java/com/chainberry/trustwalletcore/Bech32Test.kt +35 -0
  27. package/android/src/test/java/com/chainberry/trustwalletcore/NativeWalletStoreTest.kt +344 -0
  28. package/expo-module.config.json +3 -2
  29. package/ios/AmountParsing.swift +62 -0
  30. package/ios/Bech32.swift +66 -0
  31. package/ios/ChainSigning.swift +978 -0
  32. package/ios/ChainberryTrustWalletCoreModule.swift +336 -0
  33. package/ios/ConformanceTests/AddressDerivationConformanceTests.swift +39 -0
  34. package/ios/ConformanceTests/SigningConformanceTests.swift +295 -0
  35. package/ios/NativeWalletStore.swift +288 -0
  36. package/package.json +4 -3
  37. package/src/index.ts +42 -13
  38. package/android/src/main/java/expo/modules/trustwalletcore/ChainSigning.kt +0 -299
  39. package/android/src/main/java/expo/modules/trustwalletcore/NativeWalletStore.kt +0 -182
  40. package/android/src/main/java/expo/modules/trustwalletcore/TrustWalletCoreModule.kt +0 -91
  41. package/ios/TrustWalletCoreModule.swift +0 -107
@@ -3,19 +3,20 @@ require 'json'
3
3
  package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
4
 
5
5
  Pod::Spec.new do |s|
6
- s.name = 'TrustWalletCoreModule'
6
+ s.name = 'ChainberryTrustWalletCoreModule'
7
7
  s.version = package['version']
8
8
  s.summary = package['description']
9
9
  s.description = s.summary
10
10
  s.homepage = 'https://github.com/Chainberry-com/trust-wallet-core'
11
11
  s.license = package['license']
12
12
  s.author = 'Chainberry'
13
- s.platform = :ios, '16.0'
13
+ s.platform = :ios, '15.1'
14
14
  s.source = { git: 'git@github.com:Chainberry-com/trust-wallet-core.git', tag: "v#{package['version']}" }
15
15
  s.static_framework = true
16
16
 
17
- s.source_files = 'ios/**/*.{h,m,mm,swift}'
17
+ s.source_files = 'ios/**/*.{h,m,mm,swift}'
18
+ s.exclude_files = 'ios/ConformanceTests/**'
18
19
 
19
20
  s.dependency 'ExpoModulesCore'
20
- s.dependency 'TrustWalletCore'
21
+ s.dependency 'TrustWalletCore', '4.1.19'
21
22
  end
package/README.md CHANGED
@@ -10,28 +10,13 @@ Supported chains: Ethereum, BNB Smart Chain, Polygon, Solana, Tron, TON, Bitcoin
10
10
  npx expo install @chainberry/trust-wallet-core
11
11
  ```
12
12
 
13
- This is an Expo config plugin module with native Android/iOS code, so it requires a development build (`expo prebuild` / EAS Build) — it will not work in Expo Go.
13
+ This is an Expo native module with native Android/iOS code. It uses Expo's autolinking mechanism and does not ship an Expo config plugin. A development build is required (`expo prebuild` / EAS Build) — it will not work in Expo Go.
14
14
 
15
- ### Android: GitHub Packages authentication required
15
+ ### Android: no GitHub credentials needed
16
16
 
17
- Trust Wallet Core's Android artifact is published to GitHub Packages, which requires authentication even though the package itself is public. Without credentials, `./gradlew` will fail to resolve `com.trustwallet:wallet-core`.
17
+ Trust Wallet Core's Android artifact is only published to GitHub Packages, which requires authentication even though the package itself is public. To avoid making that a requirement for every dev/CI machine, `com.trustwallet:wallet-core` and its `wallet-core-proto` dependency are vendored locally as a plain Maven repo in `android/libs/` — `android/build.gradle` resolves from there first, so a normal `./gradlew` build needs no credentials at all. See `android/libs/README.md` for how it's kept up to date.
18
18
 
19
- Set up **one** of:
20
-
21
- ```sh
22
- # Env vars (CI / one-off terminal)
23
- export GITHUB_ACTOR=your-github-username
24
- export GITHUB_TOKEN=ghp_xxxxxxxxxxxx # classic PAT, read:packages scope only
25
- ```
26
-
27
- or add to `~/.gradle/gradle.properties` (permanent local dev setup):
28
-
29
- ```properties
30
- gpr.user=your-github-username
31
- gpr.key=ghp_xxxxxxxxxxxx
32
- ```
33
-
34
- Generate a token at github.com/settings/tokens → "classic" → check `read:packages`.
19
+ GitHub Packages auth is still needed (via `GITHUB_ACTOR`/`GITHUB_TOKEN` env vars or `gpr.user`/`gpr.key` in `~/.gradle/gradle.properties`) only when bumping the pinned wallet-core version — see `android/libs/download.sh`.
35
20
 
36
21
  ### iOS
37
22
 
@@ -42,7 +27,12 @@ Android biometric gating additionally pulls in `androidx.biometric:biometric:1.1
42
27
  ## Usage
43
28
 
44
29
  ```ts
45
- import { createWallet, importWallet, signTransaction, exportMnemonic } from "@chainberry/trust-wallet-core";
30
+ import {
31
+ createWallet,
32
+ importWallet,
33
+ signTransaction,
34
+ exportMnemonic,
35
+ } from "@chainberry/trust-wallet-core";
46
36
 
47
37
  const { walletId, addresses } = await createWallet(); // 128-bit / 12-word by default
48
38
  // addresses: { ethereum: "0x...", solana: "...", bnb: "0x...", bitcoin: "...", ... }
@@ -58,10 +48,10 @@ const mnemonic = await exportMnemonic(walletId);
58
48
 
59
49
  ## API
60
50
 
61
- - `createWallet(strength = 128, passphrase = "")` — generates a new BIP-39 mnemonic and persists it natively (Keychain on iOS / Keystore-backed file on Android, biometry-or-passcode gated). Returns `{ walletId, addresses }` — the mnemonic itself never leaves native code.
62
- - `importWallet(mnemonic, passphrase = "")` — validates and persists an existing mnemonic the same way. The `mnemonic` argument is a one-time exposure from the caller (e.g. a text-entry backup-restore screen); discard your own copy immediately after this call resolves.
51
+ - `createWallet(strength = 128)` — generates a new BIP-39 mnemonic and persists it natively (Keychain on iOS / Keystore-backed file on Android, biometry-or-passcode gated). Returns `{ walletId, addresses }` — the mnemonic itself never leaves native code. No BIP-39 passphrase support: `signTransaction` always reconstructs the wallet with an empty passphrase, so a caller-supplied one would derive addresses from a seed different from the one actually used to sign.
52
+ - `importWallet(mnemonic)` — validates and persists an existing mnemonic the same way. The `mnemonic` argument is a one-time exposure from the caller (e.g. a text-entry backup-restore screen); discard your own copy immediately after this call resolves.
63
53
  - `listWallets()` — returns `{ walletId, addresses }[]` for every persisted wallet, reading only the ungated metadata store. No biometric prompt.
64
- - `deleteWallet(walletId)` — removes the wallet's native key material and metadata entry. Irreversible; not biometric-gated (deleting reveals nothing, so this is a UX confirmation concern, not a key-secrecy one).
54
+ - `deleteWallet(walletId)` — removes the wallet's native key material and metadata entry. Irreversible; requires a fresh biometric/passcode confirmation before deletion proceeds on both platforms.
65
55
  - `signTransaction(walletId, chain, unsignedTx)` — triggers a native biometry/passcode prompt, then derives the key and signs entirely inside native code. Returns `{ signedTx, meta? }`; `meta` currently only carries TON's `txHash`.
66
56
  - `exportMnemonic(walletId)` — the one sanctioned mnemonic exposure. Biometry/passcode gated. Use only for an explicit "reveal recovery phrase" backup screen; don't hold the result in app state beyond that screen's lifetime.
67
57
 
@@ -69,11 +59,23 @@ const mnemonic = await exportMnemonic(walletId);
69
59
 
70
60
  Mnemonic and derived private keys are generated, persisted, and used for signing entirely inside this module's native code (Swift/Kotlin) — they are never serialized back across the JS bridge, with the single exception of `exportMnemonic`'s explicit backup flow. This is a deliberate change from this module's earlier version, which returned raw private keys to JS on every address derivation; that let a compromised/malicious JS dependency, an attached JS debugger, or a JS-heap memory dump read wallet secrets in full. Now the JS runtime never holds them at all.
71
61
 
72
- Storage: one biometry-or-passcode-gated secret per wallet (`SecAccessControl` + iOS Keychain; a hardware-backed Android Keystore AES key gating an encrypted on-disk file), plus a separate, ungated metadata entry (`walletId` → addresses) for read-only UI that shouldn't need a biometric prompt just to show an address or balance.
62
+ Storage: one biometry-or-passcode-gated secret per wallet (`SecAccessControl` + iOS Keychain; an Android Keystore AES key gating an encrypted on-disk file), plus a separate, ungated metadata entry (`walletId` → addresses) for read-only UI that shouldn't need a biometric prompt just to show an address or balance.
63
+
64
+ **Android hardware backing is best-effort, not guaranteed.** Key creation requests StrongBox first (API 28+), falling back to a plain Keystore key (which Keymaster backs with a TEE on virtually all real devices, but which can be software-only on an emulator or a device with no secure hardware at all) on `StrongBoxUnavailableException` or below API 28. The actual security level achieved is verified via `KeyInfo` right after creation and logged (`NativeWalletStore` logcat tag) — a software-only key is tolerated rather than blocking wallet creation, so this is a best-effort hardening measure, not an enforced guarantee. Don't assume every wallet on every device is hardware-backed; check the logs on a specific device/build if that matters for your threat model.
73
65
 
74
66
  Bitcoin Cash is derivation-only: sending BCH is unsupported both here and upstream (`chainberry-wallet`'s self-custody transaction preparation has no BCH case), so this module only derives its address.
75
67
 
76
- **Not yet verified against a real device/build**: the per-chain `SigningInput` field mappings for Bitcoin, Litecoin, Tron, XRP, and TON, and Solana's raw-transaction signing mode, were written against the general Trust Wallet Core API shape but haven't been compiled or run against this module's pinned `wallet-core` version. Confirm field names and produce byte-for-byte signed-output parity against the previous JS-based signers (or a testnet broadcast) before trusting any of these chains with real funds. Ethereum/BNB/Polygon signing is a straightforward reshape of what this module already proved out.
68
+ Both platforms pin **Trust Wallet Core 4.1.19** (`com.trustwallet:wallet-core:4.1.19` in `android/build.gradle`; `s.dependency 'TrustWalletCore', '4.1.19'` in the podspec).
69
+
70
+ **Address derivation is verified for all 10 chains** in `conformance/address-derivation-vectors.json`, asserted by the Android instrumented test (`src/androidTest/.../AddressDerivationConformanceTest.kt`; run via `./gradlew connectedDebugAndroidTest`). Methodology: real on-device 4.1.19 addresses were harvested from the instrumented test running on an emulator, cross-checked against an independent derivation via the WASM build (`@trustwallet/wallet-core` 3.3.3, run standalone in Node — a different upstream release than the pinned 4.1.19, used only as a second data point); on-device 4.1.19 is authoritative wherever the two disagree, since that's what the app actually ships. They agreed on 6 of 7 previously-pending chains exactly; `ton` disagreed only in address-flag encoding (bounceable vs. non-bounceable — same underlying key/hash, see that fixture entry's `_note`). This same run also caught a real bug: the Ethereum/BNB/Polygon address that had been marked `"verified"` since before this pass was actually wrong — the instrumented test that should have caught it had never successfully executed (two pre-existing bugs: `coin.name()` didn't compile against this Kotlin binding, and no `testInstrumentationRunner` was configured, so `connectedAndroidTest` silently ran "0 tests" instead of failing). Both are fixed now; see the test file's header comment for details.
71
+
72
+ **Byte-for-byte signing output is verified for 8 of 9 signable chains** in `conformance/signing-vectors.json`, asserted by `src/androidTest/.../SigningConformanceTest.kt`. Each vector calls `ChainSigner.sign()` — the same call path production code uses — with a fixed, deterministic (not necessarily broadcast-valid) unsigned tx. `ton` is verified but _not_ byte-exact-asserted: `signTon()` embeds a wall-clock `expireAt` into the signed payload, so its output legitimately differs every run — the test instead checks the output is a well-formed signed BOC. `bitcoincash` has no signing vector (sending is unsupported, see above).
73
+
74
+ Both Android suites are CI-gated on every push/PR (`.github/workflows/ci.yml`'s `android-unit-tests` and `android-instrumented-tests` jobs, via `example/` — see that folder's README for why an Expo host app is needed to build this module at all). iOS's CI job is still a structural podspec lint only; see the conformance-suite note below for what's missing to close that gap.
75
+
76
+ **Tron divergence resolved.** iOS's `signTron` used to hand wallet-core the full `rawJson` and return its own reconstruction, diverging from Android's `txId`-only input + manual JSON reassembly — and `TronSigningInput` has no `rawJson` field in the pinned 4.1.19 anyway, so the old iOS code didn't compile. iOS now signs the same way Android does: `TronSigningInput.txID` set to the digest, signature reassembled into `{...tx, signature}` in app code (see `ios/ChainSigning.swift`'s `signTron`). Same key + same digest is guaranteed to produce the same ECDSA signature, so this is a structural fix, not something that needs a device to confirm.
77
+
78
+ **iOS conformance suites are written but not yet wired into a runnable target.** `ios/ConformanceTests/AddressDerivationConformanceTests.swift` and `SigningConformanceTests.swift` mirror the Android fixtures above (all 10 address-derivation chains, the same 8 signing vectors, including a `testTron()` that asserts the reassembled signature matches Android's byte-for-byte). Neither file is part of the `ConformanceTests/` SwiftPM package (which still only builds the amount-parsing target — these two `import WalletCore`/`import XCTest`, which that dependency-free package deliberately avoids) or of any Xcode project (`ios/` is gitignored, regenerated by `expo prebuild`, so nothing durable lives there). An iOS engineer still needs to add both files to an XCTest target in the host app's workspace that links `WalletCore.xcframework`, run them on a Mac, and promote `conformance/signing-vectors.json`'s `tron` vector off `"verified-android-only"` once that run confirms the signature match. Until then, `ChainKey.coinType` mapping identically to Android's makes address derivation _likely_ correct on iOS, but this hasn't been independently run on-device.
77
79
 
78
80
  ## License
79
81
 
@@ -12,12 +12,16 @@ apply from: new File(
12
12
  applyKotlinExpoModulesCorePlugin()
13
13
 
14
14
  android {
15
- namespace 'expo.modules.trustwalletcore'
16
- compileSdkVersion 35
15
+ namespace 'com.chainberry.trustwalletcore'
16
+ compileSdkVersion 36
17
17
 
18
18
  defaultConfig {
19
19
  minSdkVersion 24
20
- targetSdkVersion 35
20
+ targetSdkVersion 36
21
+ // Without this, AGP defaults to the legacy android.test.InstrumentationTestRunner, which
22
+ // cannot discover @RunWith(AndroidJUnit4::class)-annotated tests at all (connectedAndroidTest
23
+ // silently reports "0 tests"/"No tests found" instead of running AddressDerivationConformanceTest).
24
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
21
25
  }
22
26
 
23
27
  compileOptions {
@@ -30,9 +34,14 @@ android {
30
34
  }
31
35
  }
32
36
 
33
- // ─── wallet-core credentials ──────────────────────────────────────────────────
34
- // GitHub Packages requires auth even for public packages.
35
- // Priority order (first non-empty wins):
37
+ // ─── wallet-core artifact ──────────────────────────────────────────────────
38
+ // com.trustwallet:wallet-core and its wallet-core-proto dependency are vendored
39
+ // in libs/ as a plain local Maven repo, so a normal build needs NO GitHub
40
+ // credentials at all — see libs/README.md for how it got there and how to
41
+ // re-vendor when bumping the pinned version below.
42
+ //
43
+ // The GitHub Packages repo is kept as a fallback (e.g. resolving a version not
44
+ // yet vendored) and needs auth even for this public package:
36
45
  // 1. Env vars GITHUB_ACTOR / GITHUB_TOKEN (CI / one-off terminal)
37
46
  // 2. Gradle props gpr.user / gpr.key in (permanent dev setup)
38
47
  // ~/.gradle/gradle.properties
@@ -49,6 +58,8 @@ def githubToken = System.getenv("GITHUB_TOKEN")
49
58
  ?: (project.hasProperty("gpr.key") ? project.property("gpr.key") : "")
50
59
 
51
60
  repositories {
61
+ maven { url = uri("$projectDir/libs") }
62
+
52
63
  maven {
53
64
  url = uri("https://maven.pkg.github.com/trustwallet/wallet-core")
54
65
  credentials {
@@ -66,4 +77,16 @@ dependencies {
66
77
  implementation 'com.google.protobuf:protobuf-javalite:3.21.9'
67
78
  // BiometricPrompt (biometry-or-device-credential gating on the Keystore-backed wallet cipher)
68
79
  implementation 'androidx.biometric:biometric:1.1.0'
80
+
81
+ // JVM unit tests (src/test) — AmountParsingConformanceTest reads the shared JSON fixture
82
+ // in ../conformance/. Use the pure-Java org.json artifact, not android.jar's stubbed
83
+ // org.json (which throws "Stub!" outside Robolectric/instrumented tests).
84
+ testImplementation 'junit:junit:4.13.2'
85
+ testImplementation 'org.json:json:20240303'
86
+
87
+ // Instrumented tests (src/androidTest) — AddressDerivationConformanceTest needs the
88
+ // TrustWalletCore JNI library, which can only load on a real device or emulator.
89
+ // Run via: ./gradlew connectedDebugAndroidTest
90
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
91
+ androidTestImplementation 'androidx.test:runner:1.5.2'
69
92
  }
@@ -0,0 +1,34 @@
1
+ # Vendored wallet-core (local Maven repo)
2
+
3
+ `com.trustwallet:wallet-core` (and its `wallet-core-proto` dependency) are only
4
+ published to GitHub Packages, which requires authentication even to download a
5
+ public package. To spare every dev/CI machine from needing a
6
+ `read:packages` PAT, the resolved `.aar`/`.jar` + `.pom` files are vendored
7
+ here in a plain Maven-layout directory:
8
+
9
+ ```
10
+ libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.{aar,pom}
11
+ libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.{jar,pom}
12
+ ```
13
+
14
+ `../build.gradle` points its `repositories {}` block at this directory
15
+ *before* the GitHub Packages fallback, so a normal build resolves entirely
16
+ from here — no credentials needed.
17
+
18
+ Files were downloaded from `maven.pkg.github.com/trustwallet/wallet-core` on
19
+ 2026-09-04 and their sha1 checksums verified against GitHub's own
20
+ `.sha1` files (still alongside each artifact here).
21
+
22
+ ## Bumping the pinned version
23
+
24
+ 1. `./download.sh <new-version>` (needs `GITHUB_ACTOR`/`GITHUB_TOKEN` env vars,
25
+ or `gpr.user`/`gpr.key` in `~/.gradle/gradle.properties` — same credentials
26
+ the GitHub Packages fallback in `build.gradle` already uses).
27
+ 2. Update the pinned version in `../build.gradle`
28
+ (`implementation 'com.trustwallet:wallet-core:...'`) and in
29
+ `../../ChainberryTrustWalletCoreModule.podspec` (`s.dependency 'TrustWalletCore', '...'`)
30
+ to keep both platforms on the same wallet-core release.
31
+ 3. Delete the old version's directories under `com/trustwallet/wallet-core*/`
32
+ here (or leave them if you want to keep an easy rollback).
33
+ 4. Re-run the conformance suite (`conformance/`) — a wallet-core bump can
34
+ change derivation/signing output.
@@ -0,0 +1 @@
1
+ 3f07349ecbe013f2098684cc70cef49e
@@ -0,0 +1 @@
1
+ 58c6ee87c3ab4fb90c6ef4e9ba4f78260740ca16
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4
+ <!-- This module was also published with a richer model, Gradle metadata, -->
5
+ <!-- which should be used instead. Do not delete the following line which -->
6
+ <!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
7
+ <!-- that they should prefer consuming it instead. -->
8
+ <!-- do_not_remove: published-with-gradle-metadata -->
9
+ <modelVersion>4.0.0</modelVersion>
10
+ <groupId>com.trustwallet</groupId>
11
+ <artifactId>wallet-core</artifactId>
12
+ <version>4.1.19</version>
13
+ <packaging>aar</packaging>
14
+ <dependencies>
15
+ <dependency>
16
+ <groupId>com.trustwallet</groupId>
17
+ <artifactId>wallet-core-proto</artifactId>
18
+ <version>4.1.19</version>
19
+ <scope>compile</scope>
20
+ </dependency>
21
+ </dependencies>
22
+ </project>
@@ -0,0 +1 @@
1
+ d4a399c80d4f896ae4d4ea6013f1f15f
@@ -0,0 +1 @@
1
+ a00894bc91fbbbf496fd3792484082334363e0fe
@@ -0,0 +1 @@
1
+ 1a68899feeb645d9895264d4c58e046dbcb3d528
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4
+ <!-- This module was also published with a richer model, Gradle metadata, -->
5
+ <!-- which should be used instead. Do not delete the following line which -->
6
+ <!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
7
+ <!-- that they should prefer consuming it instead. -->
8
+ <!-- do_not_remove: published-with-gradle-metadata -->
9
+ <modelVersion>4.0.0</modelVersion>
10
+ <groupId>com.trustwallet</groupId>
11
+ <artifactId>wallet-core-proto</artifactId>
12
+ <version>4.1.19</version>
13
+ <dependencies>
14
+ <dependency>
15
+ <groupId>com.google.protobuf</groupId>
16
+ <artifactId>protobuf-javalite</artifactId>
17
+ <version>3.22.3</version>
18
+ <scope>compile</scope>
19
+ </dependency>
20
+ </dependencies>
21
+ </project>
@@ -0,0 +1 @@
1
+ 90383b24ce13303b7c7a435ba815209115fa7d06
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bash
2
+ # Re-vendors com.trustwallet:wallet-core (+ its wallet-core-proto dependency)
3
+ # from GitHub Packages into this directory, laid out as a plain local Maven
4
+ # repo (com/<group>/<artifact>/<version>/...). build.gradle points at this
5
+ # directory first, so a normal build needs no GitHub credentials — only
6
+ # re-running this script (to bump the pinned version, or the first time
7
+ # someone sets this up) needs a token.
8
+ #
9
+ # Usage:
10
+ # GITHUB_ACTOR=you GITHUB_TOKEN=ghp_xxx ./download.sh 4.1.19
11
+ # or rely on gpr.user / gpr.key in ~/.gradle/gradle.properties (same
12
+ # credentials used by build.gradle's GitHub Packages fallback).
13
+ set -euo pipefail
14
+
15
+ VERSION="${1:?Usage: download.sh <wallet-core version, e.g. 4.1.19>}"
16
+ LIBS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
+
18
+ GITHUB_USER="${GITHUB_ACTOR:-}"
19
+ GITHUB_PASS="${GITHUB_TOKEN:-}"
20
+ if [ -z "$GITHUB_USER" ] || [ -z "$GITHUB_PASS" ]; then
21
+ GRADLE_PROPS="$HOME/.gradle/gradle.properties"
22
+ if [ -f "$GRADLE_PROPS" ]; then
23
+ GITHUB_USER="${GITHUB_USER:-$(grep '^gpr.user=' "$GRADLE_PROPS" | cut -d= -f2-)}"
24
+ GITHUB_PASS="${GITHUB_PASS:-$(grep '^gpr.key=' "$GRADLE_PROPS" | cut -d= -f2-)}"
25
+ fi
26
+ fi
27
+ if [ -z "$GITHUB_USER" ] || [ -z "$GITHUB_PASS" ]; then
28
+ echo "error: no GitHub credentials found (env vars or ~/.gradle/gradle.properties gpr.user/gpr.key)" >&2
29
+ exit 1
30
+ fi
31
+
32
+ REGISTRY="https://maven.pkg.github.com/trustwallet/wallet-core"
33
+
34
+ # module:packaging pairs — wallet-core is an .aar, wallet-core-proto is a plain .jar
35
+ MODULES=("wallet-core:aar" "wallet-core-proto:jar")
36
+
37
+ for entry in "${MODULES[@]}"; do
38
+ artifact="${entry%%:*}"
39
+ ext="${entry##*:}"
40
+ dest="$LIBS_DIR/com/trustwallet/$artifact/$VERSION"
41
+ mkdir -p "$dest"
42
+ base="$REGISTRY/com/trustwallet/$artifact/$VERSION"
43
+ for file in "$artifact-$VERSION.pom" "$artifact-$VERSION.$ext" "$artifact-$VERSION.pom.sha1" "$artifact-$VERSION.$ext.sha1"; do
44
+ echo "fetching $file"
45
+ curl -sS -L -f -u "$GITHUB_USER:$GITHUB_PASS" -o "$dest/$file" "$base/$file"
46
+ done
47
+ # verify what we just wrote against GitHub's own sha1
48
+ ( cd "$dest" && sha1sum -c "$artifact-$VERSION.pom.sha1" "$artifact-$VERSION.$ext.sha1" )
49
+ done
50
+
51
+ echo "done — vendored wallet-core $VERSION into $LIBS_DIR"
52
+ echo "remember to bump the version in ../build.gradle (dependencies block) and the podspec, and delete the old version's directories above if this is a version bump rather than a first-time vendor."
@@ -0,0 +1,106 @@
1
+ package com.chainberry.trustwalletcore
2
+
3
+ import androidx.test.ext.junit.runners.AndroidJUnit4
4
+ import org.junit.Assert.assertEquals
5
+ import org.junit.Assert.assertNotEquals
6
+ import org.junit.Test
7
+ import org.junit.runner.RunWith
8
+ import wallet.core.jni.CoinType
9
+ import wallet.core.jni.HDWallet
10
+
11
+ // Instrumented test — must run on a device or emulator (needs the TrustWalletCore JNI library).
12
+ // Run via: ./gradlew connectedDebugAndroidTest
13
+ //
14
+ // Verifies that HDWallet.getAddressForCoin produces the expected address for every chain
15
+ // under WalletCore 4.1.19 (the pinned version). The test mnemonic is the BIP39 standard
16
+ // test vector — never use it with real funds.
17
+ //
18
+ // All 10 chains were verified on-device (real emulator, WalletCore 4.1.19 JNI) on 2026-08-27,
19
+ // cross-checked against an independent derivation via the WASM build (@trustwallet/wallet-core
20
+ // 3.3.3, run in plain Node — see conformance/address-derivation-vectors.json's _note). On-device
21
+ // 4.1.19 is authoritative wherever the two disagree.
22
+ //
23
+ // IMPORTANT — this test did not actually run before this pass. Two pre-existing bugs prevented
24
+ // it from ever executing: (1) `coin.name()` doesn't compile against this Kotlin CoinType binding
25
+ // (name is a property, not a method — fixed here), and (2) no `testInstrumentationRunner` was
26
+ // configured in build.gradle, so AGP defaulted to the legacy runner, which cannot discover
27
+ // @RunWith(AndroidJUnit4::class) tests at all ("0 tests"/"No tests found", not a real pass).
28
+ // Once actually run, it immediately caught a real bug: the ETHEREUM/SMARTCHAIN "verified" address
29
+ // below was wrong (0x9d8A62f6...) — never actually checked against real WalletCore output despite
30
+ // being marked "verified". The corrected value below is the real on-device 4.1.19 result.
31
+ //
32
+ // For any NEW chain added to ChainKey in the future: add its CoinType to PENDING, run this test,
33
+ // read the printed address in logcat, verify it independently (e.g. against a TWC 4.1.19 REPL or
34
+ // the iOS companion test — see ConformanceTests/), then move it into VERIFIED.
35
+ //
36
+ // Human-readable source of truth: conformance/address-derivation-vectors.json.
37
+ @RunWith(AndroidJUnit4::class)
38
+ class AddressDerivationConformanceTest {
39
+
40
+ companion object {
41
+ init { System.loadLibrary("TrustWalletCore") }
42
+
43
+ const val MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
44
+
45
+ // Chains with expected addresses confirmed against WalletCore 4.1.19, on a real device/emulator.
46
+ // ETH / BNB / POL all share CoinType.ETHEREUM (same secp256k1 key, BIP44 m/44'/60'/0'/0/0).
47
+ val VERIFIED = mapOf(
48
+ CoinType.ETHEREUM to "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", // ethereum / bnb / polygon
49
+ CoinType.SMARTCHAIN to "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", // bnb (same key as ethereum)
50
+ CoinType.BITCOIN to "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu",
51
+ CoinType.LITECOIN to "ltc1qjmxnz78nmc8nq77wuxh25n2es7rzm5c2rkk4wh",
52
+ CoinType.XRP to "rHsMGQEkVNJmpGWs8XUBoTBiAAbwxZN5v3",
53
+ CoinType.TRON to "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH",
54
+ // Non-bounceable (UQ...) — WalletCore 4.1.19's default for this CoinType. The WASM build
55
+ // (@trustwallet/wallet-core 3.3.3) derives the same underlying hash but defaults to the
56
+ // bounceable (EQ...) encoding instead — a version/binding difference in address-flag
57
+ // defaults, not a key-derivation discrepancy. On-device 4.1.19 (what the app actually ships)
58
+ // is authoritative here.
59
+ CoinType.TON to "UQAzWZa6nM5mJev91wGc7VCSfBoIsYRqKJpV78N8Add9-RKY",
60
+ CoinType.SOLANA to "GjJyeC1r2RgkuoCWMyPYkCWSGSGLcz266EaAkLA27AhL",
61
+ CoinType.BITCOINCASH to "bitcoincash:qqyx49mu0kkn9ftfj6hje6g2wfer34yfnq5tahq3q6",
62
+ )
63
+
64
+ // Steady state is empty — add a chain's CoinType here (and drop it from VERIFIED) only while
65
+ // actively harvesting a newly-added chain's address for the first time.
66
+ val PENDING = emptyList<CoinType>()
67
+ }
68
+
69
+ @Test
70
+ fun verifiedAddressesMatch() {
71
+ val wallet = HDWallet(MNEMONIC, "")
72
+ for ((coin, expected) in VERIFIED) {
73
+ val actual = wallet.getAddressForCoin(coin)
74
+ assertEquals("address mismatch for coin=${coin.name}", expected, actual)
75
+ }
76
+ }
77
+
78
+ // Regression test for a real bug: ChainSigner.addressForChain originally derived Litecoin's
79
+ // and Bitcoin Cash's hand-rolled testnet address from the identical path m/44'/1'/0'/0/0.
80
+ // BIP32 derivation only depends on (seed, path, curve) — CoinType alone doesn't perturb it
81
+ // when the literal path and curve (secp256k1 for both) are the same — so LTC and BCH silently
82
+ // derived the *same* key, and therefore the same address. Fixed by using each coin's own
83
+ // SLIP-44 index as the account (3rd) path component (see ChainSigner.utxoTestnetPath).
84
+ @Test
85
+ fun testnetAddressesAreDistinctPerChain() {
86
+ val wallet = HDWallet(MNEMONIC, "")
87
+ val btc = ChainSigner.addressForChain(wallet, ChainKey.BITCOIN, isTestnet = true)
88
+ val ltc = ChainSigner.addressForChain(wallet, ChainKey.LITECOIN, isTestnet = true)
89
+ val bch = ChainSigner.addressForChain(wallet, ChainKey.BITCOINCASH, isTestnet = true)
90
+ println("[AddressDerivationConformanceTest] testnet: BTC=$btc LTC=$ltc BCH=$bch")
91
+ assertNotEquals("BTC/LTC testnet addresses must not collide", btc, ltc)
92
+ assertNotEquals("BTC/BCH testnet addresses must not collide", btc, bch)
93
+ assertNotEquals("LTC/BCH testnet addresses must not collide", ltc, bch)
94
+ }
95
+
96
+ @Test
97
+ fun printPendingAddressesForVerification() {
98
+ val wallet = HDWallet(MNEMONIC, "")
99
+ val lines = PENDING.map { coin -> " ${coin.name} -> ${wallet.getAddressForCoin(coin)}" }
100
+ println(
101
+ "\n[AddressDerivationConformanceTest] Pending — verify and move to VERIFIED map:\n" +
102
+ lines.joinToString("\n")
103
+ )
104
+ // This test intentionally never fails — it exists only to harvest pending addresses.
105
+ }
106
+ }