@chainberry/trust-wallet-core 1.0.2 → 2.5.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/{TrustWalletCoreModule.podspec → ChainberryTrustWalletCoreModule.podspec} +2 -2
- package/README.md +38 -30
- package/android/build.gradle +31 -6
- package/android/libs/README.md +34 -0
- package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.aar +0 -0
- package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.aar.md5 +1 -0
- package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.aar.sha1 +1 -0
- package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.pom +22 -0
- package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.pom.md5 +1 -0
- package/android/libs/com/trustwallet/wallet-core/4.1.19/wallet-core-4.1.19.pom.sha1 +1 -0
- package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar +0 -0
- package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar.md5 +1 -0
- package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar.sha1 +1 -0
- package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom +21 -0
- package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom.md5 +1 -0
- package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom.sha1 +1 -0
- package/android/libs/download.sh +52 -0
- package/android/src/androidTest/java/com/chainberry/trustwalletcore/AddressDerivationConformanceTest.kt +106 -0
- package/android/src/androidTest/java/com/chainberry/trustwalletcore/SigningConformanceTest.kt +186 -0
- package/android/src/main/java/com/chainberry/trustwalletcore/AmountParsing.kt +45 -0
- package/android/src/main/java/com/chainberry/trustwalletcore/Bech32.kt +68 -0
- package/android/src/main/java/com/chainberry/trustwalletcore/ChainSigning.kt +526 -0
- package/android/src/main/java/com/chainberry/trustwalletcore/ChainberryTrustWalletCoreModule.kt +147 -0
- package/android/src/main/java/com/chainberry/trustwalletcore/NativeWalletStore.kt +796 -0
- package/android/src/test/java/com/chainberry/trustwalletcore/AmountParsingConformanceTest.kt +57 -0
- package/android/src/test/java/com/chainberry/trustwalletcore/Bech32Test.kt +35 -0
- package/android/src/test/java/com/chainberry/trustwalletcore/NativeWalletStoreTest.kt +274 -0
- package/expo-module.config.json +3 -2
- package/ios/AmountParsing.swift +62 -0
- package/ios/Bech32.swift +66 -0
- package/ios/ChainSigning.swift +602 -0
- package/ios/ChainberryTrustWalletCoreModule.swift +231 -0
- package/ios/NativeWalletStore.swift +232 -0
- package/package.json +5 -4
- package/src/index.ts +70 -60
- package/android/src/main/java/expo/modules/trustwalletcore/TrustWalletCoreModule.kt +0 -143
- package/ios/TrustWalletCoreModule.swift +0 -145
|
@@ -3,7 +3,7 @@ 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 = '
|
|
6
|
+
s.name = 'ChainberryTrustWalletCoreModule'
|
|
7
7
|
s.version = package['version']
|
|
8
8
|
s.summary = package['description']
|
|
9
9
|
s.description = s.summary
|
|
@@ -17,5 +17,5 @@ Pod::Spec.new do |s|
|
|
|
17
17
|
s.source_files = 'ios/**/*.{h,m,mm,swift}'
|
|
18
18
|
|
|
19
19
|
s.dependency 'ExpoModulesCore'
|
|
20
|
-
s.dependency 'TrustWalletCore'
|
|
20
|
+
s.dependency 'TrustWalletCore', '4.1.19'
|
|
21
21
|
end
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Expo native module wrapping [Trust Wallet Core](https://github.com/trustwallet/wallet-core) for HD wallet generation, address derivation, and transaction signing. Runs the real native library (Kotlin/JNI on Android, Swift on iOS) — not the WASM build, so it works fine under Hermes.
|
|
4
4
|
|
|
5
|
-
Supported
|
|
5
|
+
Supported chains: Ethereum, BNB Smart Chain, Polygon, Solana, Tron, TON, Bitcoin, Bitcoin Cash (address derivation only — see Security model), Litecoin, XRP (see `Chain`).
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,53 +12,61 @@ npx expo install @chainberry/trust-wallet-core
|
|
|
12
12
|
|
|
13
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.
|
|
14
14
|
|
|
15
|
-
### Android: GitHub
|
|
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.
|
|
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
|
-
|
|
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
|
|
|
38
|
-
`WalletCore` is pulled in via CocoaPods (`s.dependency 'TrustWalletCore'`) — no extra auth needed, `pod install` handles it.
|
|
23
|
+
`WalletCore` is pulled in via CocoaPods (`s.dependency 'TrustWalletCore'`) — no extra auth needed, `pod install` handles it. Biometric/passcode gating uses `LocalAuthentication` (system framework, no extra dependency).
|
|
24
|
+
|
|
25
|
+
Android biometric gating additionally pulls in `androidx.biometric:biometric:1.1.0`.
|
|
39
26
|
|
|
40
27
|
## Usage
|
|
41
28
|
|
|
42
29
|
```ts
|
|
43
|
-
import {
|
|
30
|
+
import { createWallet, importWallet, signTransaction, exportMnemonic } from "@chainberry/trust-wallet-core";
|
|
44
31
|
|
|
45
|
-
const {
|
|
46
|
-
//
|
|
32
|
+
const { walletId, addresses } = await createWallet(); // 128-bit / 12-word by default
|
|
33
|
+
// addresses: { ethereum: "0x...", solana: "...", bnb: "0x...", bitcoin: "...", ... }
|
|
47
34
|
|
|
48
|
-
const restored = await
|
|
35
|
+
const restored = await importWallet(mnemonic);
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
// Triggers a native biometry/passcode prompt; only signed tx bytes/hex cross back to JS.
|
|
38
|
+
const { signedTx } = await signTransaction(walletId, "ethereum", unsignedTx);
|
|
39
|
+
|
|
40
|
+
// Explicit backup flow only — biometry/passcode gated.
|
|
41
|
+
const mnemonic = await exportMnemonic(walletId);
|
|
51
42
|
```
|
|
52
43
|
|
|
53
44
|
## API
|
|
54
45
|
|
|
55
|
-
- `
|
|
56
|
-
- `
|
|
57
|
-
- `
|
|
46
|
+
- `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.
|
|
47
|
+
- `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.
|
|
48
|
+
- `listWallets()` — returns `{ walletId, addresses }[]` for every persisted wallet, reading only the ungated metadata store. No biometric prompt.
|
|
49
|
+
- `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).
|
|
50
|
+
- `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`.
|
|
51
|
+
- `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.
|
|
52
|
+
|
|
53
|
+
## Security model
|
|
54
|
+
|
|
55
|
+
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.
|
|
56
|
+
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
**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.
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
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).
|
|
64
|
+
|
|
65
|
+
**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.
|
|
58
66
|
|
|
59
|
-
|
|
67
|
+
**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).
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
**iOS gap — not closed by this pass.** No Swift/Xcode toolchain was available in the environment that did this verification, so none of the above has been independently confirmed on iOS. `ChainKey.coinType` maps identically to Android's, so derivation *should* match, but this hasn't been checked on-device. `ConformanceTests/` currently has no address-derivation or signing test target at all (only the pre-existing amount-parsing one) — before this finding is fully closed for both platforms, an iOS engineer needs to (1) add `AddressDerivationConformanceTests`/`SigningConformanceTests` targets mirroring the Android ones, (2) run them against these same fixtures on a Mac, and (3) specifically resolve the **Tron divergence**: Android's `signTron` signs just the `txId` digest and reassembles JSON in app code, while iOS's hands wallet-core the full `rawJson` and returns its own reconstruction — `conformance/signing-vectors.json`'s `tron` entry spells out exactly what to check (the embedded signature hex must match Android's byte-for-byte; a mismatch there is a real bug, not a formatting difference).
|
|
62
70
|
|
|
63
71
|
## License
|
|
64
72
|
|
package/android/build.gradle
CHANGED
|
@@ -12,12 +12,16 @@ apply from: new File(
|
|
|
12
12
|
applyKotlinExpoModulesCorePlugin()
|
|
13
13
|
|
|
14
14
|
android {
|
|
15
|
-
namespace '
|
|
16
|
-
compileSdkVersion
|
|
15
|
+
namespace 'com.chainberry.trustwalletcore'
|
|
16
|
+
compileSdkVersion 36
|
|
17
17
|
|
|
18
18
|
defaultConfig {
|
|
19
19
|
minSdkVersion 24
|
|
20
|
-
targetSdkVersion
|
|
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
|
|
34
|
-
//
|
|
35
|
-
//
|
|
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 {
|
|
@@ -64,4 +75,18 @@ dependencies {
|
|
|
64
75
|
// Check github.com/trustwallet/wallet-core/releases for the latest version
|
|
65
76
|
implementation 'com.trustwallet:wallet-core:4.1.19'
|
|
66
77
|
implementation 'com.google.protobuf:protobuf-javalite:3.21.9'
|
|
78
|
+
// BiometricPrompt (biometry-or-device-credential gating on the Keystore-backed wallet cipher)
|
|
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'
|
|
67
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
|
|
Binary file
|
package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar.md5
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2c47413fd480c3ae3a4245f8487a343e
|
package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.jar.sha1
ADDED
|
@@ -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>
|
package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom.md5
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e3987a3948a049ac60b57fa808b8f8ae
|
package/android/libs/com/trustwallet/wallet-core-proto/4.1.19/wallet-core-proto-4.1.19.pom.sha1
ADDED
|
@@ -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
|
+
}
|