@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.
Files changed (39) hide show
  1. package/{TrustWalletCoreModule.podspec → ChainberryTrustWalletCoreModule.podspec} +2 -2
  2. package/README.md +15 -22
  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 +526 -0
  23. package/android/src/main/java/com/chainberry/trustwalletcore/ChainberryTrustWalletCoreModule.kt +147 -0
  24. package/android/src/main/java/com/chainberry/trustwalletcore/NativeWalletStore.kt +796 -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 +274 -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 +602 -0
  32. package/ios/ChainberryTrustWalletCoreModule.swift +231 -0
  33. package/ios/NativeWalletStore.swift +232 -0
  34. package/package.json +4 -3
  35. package/src/index.ts +27 -12
  36. package/android/src/main/java/expo/modules/trustwalletcore/ChainSigning.kt +0 -299
  37. package/android/src/main/java/expo/modules/trustwalletcore/NativeWalletStore.kt +0 -182
  38. package/android/src/main/java/expo/modules/trustwalletcore/TrustWalletCoreModule.kt +0 -91
  39. package/ios/TrustWalletCoreModule.swift +0 -107
@@ -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
+ }