@chainberry/trust-wallet-core 2.0.0 → 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 +15 -22
- package/android/build.gradle +29 -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 +4 -3
- package/src/index.ts +27 -12
- package/android/src/main/java/expo/modules/trustwalletcore/ChainSigning.kt +0 -299
- package/android/src/main/java/expo/modules/trustwalletcore/NativeWalletStore.kt +0 -182
- package/android/src/main/java/expo/modules/trustwalletcore/TrustWalletCoreModule.kt +0 -91
- package/ios/TrustWalletCoreModule.swift +0 -107
package/android/src/main/java/com/chainberry/trustwalletcore/ChainberryTrustWalletCoreModule.kt
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
package com.chainberry.trustwalletcore
|
|
2
|
+
|
|
3
|
+
import android.app.AlertDialog
|
|
4
|
+
import androidx.fragment.app.FragmentActivity
|
|
5
|
+
import expo.modules.kotlin.exception.CodedException
|
|
6
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
7
|
+
import expo.modules.kotlin.modules.Module
|
|
8
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
9
|
+
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
10
|
+
import wallet.core.jni.HDWallet
|
|
11
|
+
import java.util.UUID
|
|
12
|
+
import kotlin.coroutines.resume
|
|
13
|
+
import kotlin.coroutines.resumeWithException
|
|
14
|
+
|
|
15
|
+
// Mnemonic/private-key material never crosses back to JS except `exportMnemonic` — an
|
|
16
|
+
// explicit, biometric/device-credential-gated backup flow. Every other method returns only
|
|
17
|
+
// walletIds, addresses, or signed transaction bytes/hex.
|
|
18
|
+
class ChainberryTrustWalletCoreModule : Module() {
|
|
19
|
+
companion object {
|
|
20
|
+
init {
|
|
21
|
+
// Must be loaded once before any JNI calls
|
|
22
|
+
System.loadLibrary("TrustWalletCore")
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private val context get() = appContext.reactContext
|
|
27
|
+
?: throw CodedException("NoContext", "React context unavailable", null)
|
|
28
|
+
|
|
29
|
+
private val activity: FragmentActivity
|
|
30
|
+
get() = appContext.currentActivity as? FragmentActivity
|
|
31
|
+
?: throw CodedException("NoActivity", "No foreground FragmentActivity to host the biometric prompt", null)
|
|
32
|
+
|
|
33
|
+
override fun definition() = ModuleDefinition {
|
|
34
|
+
Name("TrustWalletCore")
|
|
35
|
+
|
|
36
|
+
// strength 128 = 12 words, 256 = 24 words. Returns { walletId, addresses }.
|
|
37
|
+
// No BIP-39 passphrase support: signTransaction always reconstructs the wallet with an
|
|
38
|
+
// empty passphrase, so accepting one here would derive addresses from a seed different
|
|
39
|
+
// from the one actually used to sign — always pass "" to stay consistent with that.
|
|
40
|
+
// isTestnet selects the address format for BTC/LTC/BCH (see ChainSigner.addressForChain) —
|
|
41
|
+
// every other chain's address is the same on mainnet and testnet.
|
|
42
|
+
AsyncFunction("createWallet") Coroutine { strength: Int, isTestnet: Boolean ->
|
|
43
|
+
val wallet = HDWallet(strength, "")
|
|
44
|
+
persistNewWallet(wallet, isTestnet)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// One-time mnemonic exposure from JS, at import only — never retained after this call.
|
|
48
|
+
// Returns { walletId, addresses }. No BIP-39 passphrase support (see `createWallet`).
|
|
49
|
+
AsyncFunction("importWallet") Coroutine { mnemonic: String, isTestnet: Boolean ->
|
|
50
|
+
val wallet = HDWallet(mnemonic, "") // throws on invalid mnemonic
|
|
51
|
+
persistNewWallet(wallet, isTestnet)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Reads only the ungated metadata store — no biometric prompt.
|
|
55
|
+
AsyncFunction("listWallets") {
|
|
56
|
+
NativeWalletStore.loadMetadata(context).map { (walletId, addresses) ->
|
|
57
|
+
mapOf("walletId" to walletId, "addresses" to addresses)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Irreversible — requires a fresh biometric/device-credential confirmation before
|
|
62
|
+
// anything is deleted, same gate as `signTransaction`/`exportMnemonic`. A
|
|
63
|
+
// compromised/malicious JS caller can still invoke this directly (there's no UI call
|
|
64
|
+
// site today), so the gate must live here rather than in JS.
|
|
65
|
+
AsyncFunction("deleteWallet") Coroutine { walletId: String ->
|
|
66
|
+
val id = NativeWalletStore.validateWalletId(walletId)
|
|
67
|
+
NativeWalletStore.confirmIdentity(activity, context, "Delete wallet")
|
|
68
|
+
NativeWalletStore.deleteMnemonic(context, id)
|
|
69
|
+
val metadata = NativeWalletStore.loadMetadata(context).toMutableMap()
|
|
70
|
+
metadata.remove(id)
|
|
71
|
+
NativeWalletStore.saveMetadata(context, metadata)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Triggers the native biometry/device-credential prompt, then signs entirely in-process.
|
|
75
|
+
// Returns { signedTx, meta? }. isTestnet must match whatever `createWallet`/`importWallet`
|
|
76
|
+
// used — see ChainSigner.keyForChain (a mismatch signs with the wrong key for BTC/LTC).
|
|
77
|
+
AsyncFunction("signTransaction") Coroutine { walletId: String, chain: String, unsignedTx: Map<String, Any>, isTestnet: Boolean ->
|
|
78
|
+
val id = NativeWalletStore.validateWalletId(walletId)
|
|
79
|
+
val chainKey = ChainKey.fromJs(chain)
|
|
80
|
+
confirmTransaction(chainKey, unsignedTx)
|
|
81
|
+
val cipher = NativeWalletStore.authenticateForExistingWallet(activity, context, id, "Sign transaction")
|
|
82
|
+
val mnemonic = NativeWalletStore.loadMnemonic(context, id, cipher)
|
|
83
|
+
val wallet = HDWallet(mnemonic, "")
|
|
84
|
+
val result = ChainSigner.sign(chainKey, wallet, unsignedTx, isTestnet)
|
|
85
|
+
val response = mutableMapOf<String, Any>("signedTx" to result.signedTx)
|
|
86
|
+
result.meta?.let { response["meta"] = it }
|
|
87
|
+
response
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The one sanctioned mnemonic exposure — explicit backup flow only.
|
|
91
|
+
AsyncFunction("exportMnemonic") Coroutine { walletId: String ->
|
|
92
|
+
val id = NativeWalletStore.validateWalletId(walletId)
|
|
93
|
+
val cipher = NativeWalletStore.authenticateForExistingWallet(activity, context, id, "Reveal recovery phrase")
|
|
94
|
+
NativeWalletStore.loadMnemonic(context, id, cipher)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Shows a native AlertDialog with decoded tx details before biometric auth fires.
|
|
99
|
+
/// The user must tap "Confirm & Sign" — cancelling throws UserCancelled.
|
|
100
|
+
private suspend fun confirmTransaction(chain: ChainKey, unsignedTx: Map<String, Any>) {
|
|
101
|
+
val message = ChainSigner.buildSummary(chain, unsignedTx)
|
|
102
|
+
suspendCancellableCoroutine<Unit> { continuation ->
|
|
103
|
+
activity.runOnUiThread {
|
|
104
|
+
AlertDialog.Builder(activity)
|
|
105
|
+
.setTitle("Confirm Transaction")
|
|
106
|
+
.setMessage(message)
|
|
107
|
+
.setPositiveButton("Confirm & Sign") { _, _ -> continuation.resume(Unit) }
|
|
108
|
+
.setNegativeButton("Cancel") { _, _ ->
|
|
109
|
+
continuation.resumeWithException(
|
|
110
|
+
CodedException("UserCancelled", "Transaction cancelled by user", null)
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
.setOnCancelListener {
|
|
114
|
+
continuation.resumeWithException(
|
|
115
|
+
CodedException("UserCancelled", "Transaction cancelled by user", null)
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
.show()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private suspend fun persistNewWallet(wallet: HDWallet, isTestnet: Boolean): Map<String, Any> {
|
|
124
|
+
val walletId = UUID.randomUUID().toString()
|
|
125
|
+
val addresses = ChainKey.entries.associate { chain ->
|
|
126
|
+
chain.name.lowercase() to ChainSigner.addressForChain(wallet, chain, isTestnet)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
val cipher = NativeWalletStore.authenticateForNewWallet(activity, context, walletId, "Secure your new wallet")
|
|
130
|
+
NativeWalletStore.saveMnemonic(context, walletId, wallet.mnemonic(), cipher)
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
val metadata = NativeWalletStore.loadMetadata(context).toMutableMap()
|
|
134
|
+
metadata[walletId] = addresses
|
|
135
|
+
NativeWalletStore.saveMetadata(context, metadata)
|
|
136
|
+
} catch (e: Exception) {
|
|
137
|
+
// The mnemonic/key is already persisted but has no metadata pointer — compensate by
|
|
138
|
+
// best-effort deleting it rather than leaving a permanent, invisible orphan. If this
|
|
139
|
+
// rollback delete also fails, there's nothing more useful to do than propagate the
|
|
140
|
+
// original error; the wallet is at least no worse off than before this call.
|
|
141
|
+
runCatching { NativeWalletStore.deleteMnemonic(context, walletId) }
|
|
142
|
+
throw e
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return mapOf("walletId" to walletId, "addresses" to addresses)
|
|
146
|
+
}
|
|
147
|
+
}
|