@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
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
package expo.modules.trustwalletcore
|
|
2
|
-
|
|
3
|
-
import com.google.protobuf.ByteString
|
|
4
|
-
import expo.modules.kotlin.modules.Module
|
|
5
|
-
import expo.modules.kotlin.modules.ModuleDefinition
|
|
6
|
-
import wallet.core.java.AnySigner
|
|
7
|
-
import wallet.core.jni.CoinType
|
|
8
|
-
import wallet.core.jni.HDWallet
|
|
9
|
-
import wallet.core.jni.PrivateKey
|
|
10
|
-
import wallet.core.jni.proto.Common
|
|
11
|
-
import wallet.core.jni.proto.Ethereum
|
|
12
|
-
import wallet.core.jni.proto.Solana
|
|
13
|
-
import java.math.BigInteger
|
|
14
|
-
|
|
15
|
-
class TrustWalletCoreModule : Module() {
|
|
16
|
-
companion object {
|
|
17
|
-
init {
|
|
18
|
-
// Must be loaded once before any JNI calls
|
|
19
|
-
System.loadLibrary("TrustWalletCore")
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
override fun definition() = ModuleDefinition {
|
|
24
|
-
Name("TrustWalletCore")
|
|
25
|
-
|
|
26
|
-
// Returns { mnemonic } — strength 128 = 12 words, 256 = 24 words
|
|
27
|
-
AsyncFunction("generateWallet") { strength: Int, passphrase: String ->
|
|
28
|
-
mapOf("mnemonic" to HDWallet(strength, passphrase).mnemonic())
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Validates mnemonic and returns { mnemonic }
|
|
32
|
-
AsyncFunction("restoreWallet") { mnemonic: String, passphrase: String ->
|
|
33
|
-
HDWallet(mnemonic, passphrase) // throws on invalid mnemonic
|
|
34
|
-
mapOf("mnemonic" to mnemonic)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Derives address and private key for a named coin
|
|
38
|
-
// coin: "ethereum" | "solana"
|
|
39
|
-
AsyncFunction("getAddressForCoin") { mnemonic: String, coin: String, passphrase: String ->
|
|
40
|
-
val wallet = HDWallet(mnemonic, passphrase)
|
|
41
|
-
val coinType = resolveCoinType(coin)
|
|
42
|
-
mapOf(
|
|
43
|
-
"address" to wallet.getAddressForCoin(coinType),
|
|
44
|
-
"privateKey" to wallet.getKeyForCoin(coinType).data().toHex(),
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Signs an Ethereum transaction; returns 0x-prefixed RLP-encoded signed tx hex
|
|
49
|
-
// txParams: { to, valueHex, nonce, gasLimitHex, chainId, dataHex?,
|
|
50
|
-
// gasPriceHex? (legacy) | maxFeePerGasHex + maxPriorityFeePerGasHex (EIP-1559) }
|
|
51
|
-
AsyncFunction("signEthereumTransaction") { privateKeyHex: String, txParams: Map<String, Any> ->
|
|
52
|
-
val privateKey = PrivateKey(privateKeyHex.hexToBytes())
|
|
53
|
-
val to = txParams["to"] as String
|
|
54
|
-
val valueHex = (txParams["valueHex"] as? String)?.ifEmpty { "0" } ?: "0"
|
|
55
|
-
val nonce = (txParams["nonce"] as Number).toInt()
|
|
56
|
-
val gasLimHex = txParams["gasLimitHex"] as String
|
|
57
|
-
val chainId = (txParams["chainId"] as Number).toInt()
|
|
58
|
-
val dataHex = (txParams["dataHex"] as? String) ?: ""
|
|
59
|
-
|
|
60
|
-
val input = Ethereum.SigningInput.newBuilder().apply {
|
|
61
|
-
this.chainId = BigInteger.valueOf(chainId.toLong()).toMinimalByteString()
|
|
62
|
-
this.nonce = BigInteger.valueOf(nonce.toLong()).toMinimalByteString()
|
|
63
|
-
this.gasLimit = BigInteger(gasLimHex, 16).toMinimalByteString()
|
|
64
|
-
this.toAddress = to
|
|
65
|
-
this.privateKey = ByteString.copyFrom(privateKey.data())
|
|
66
|
-
|
|
67
|
-
this.transaction = Ethereum.Transaction.newBuilder().apply {
|
|
68
|
-
this.transfer = Ethereum.Transaction.Transfer.newBuilder().apply {
|
|
69
|
-
this.amount = BigInteger(valueHex, 16).toMinimalByteString()
|
|
70
|
-
if (dataHex.isNotEmpty()) this.data = ByteString.copyFrom(dataHex.hexToBytes())
|
|
71
|
-
}.build()
|
|
72
|
-
}.build()
|
|
73
|
-
|
|
74
|
-
val gasPriceHex = txParams["gasPriceHex"] as? String
|
|
75
|
-
if (gasPriceHex != null) {
|
|
76
|
-
this.gasPrice = BigInteger(gasPriceHex, 16).toMinimalByteString()
|
|
77
|
-
} else {
|
|
78
|
-
val mfHex = txParams["maxFeePerGasHex"] as? String
|
|
79
|
-
val pfHex = txParams["maxPriorityFeePerGasHex"] as? String
|
|
80
|
-
if (mfHex != null && pfHex != null) {
|
|
81
|
-
this.maxFeePerGas = BigInteger(mfHex, 16).toMinimalByteString()
|
|
82
|
-
this.maxInclusionFeePerGas = BigInteger(pfHex, 16).toMinimalByteString()
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}.build()
|
|
86
|
-
|
|
87
|
-
val output = AnySigner.sign(input, CoinType.ETHEREUM, Ethereum.SigningOutput.parser())
|
|
88
|
-
if (output.error != Common.SigningError.OK) {
|
|
89
|
-
throw Exception("Signing failed: ${output.errorMessage}")
|
|
90
|
-
}
|
|
91
|
-
"0x" + output.encoded.toByteArray().toHex()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Signs a Solana transfer; returns base64-encoded signed transaction
|
|
95
|
-
// txParams: { to, lamports (string, lamports amount), recentBlockhash }
|
|
96
|
-
AsyncFunction("signSolanaTransaction") { privateKeyHex: String, txParams: Map<String, Any> ->
|
|
97
|
-
val privateKey = PrivateKey(privateKeyHex.hexToBytes())
|
|
98
|
-
val to = txParams["to"] as String
|
|
99
|
-
val lamports = (txParams["lamports"] as String).toLong()
|
|
100
|
-
val recentBlockhash = txParams["recentBlockhash"] as String
|
|
101
|
-
|
|
102
|
-
val input = Solana.SigningInput.newBuilder().apply {
|
|
103
|
-
this.recentBlockhash = recentBlockhash
|
|
104
|
-
this.privateKey = ByteString.copyFrom(privateKey.data())
|
|
105
|
-
this.transferTransaction = Solana.Transfer.newBuilder().apply {
|
|
106
|
-
this.recipient = to
|
|
107
|
-
this.value = lamports
|
|
108
|
-
}.build()
|
|
109
|
-
}.build()
|
|
110
|
-
|
|
111
|
-
val output = AnySigner.sign(input, CoinType.SOLANA, Solana.SigningOutput.parser())
|
|
112
|
-
if (output.error != Common.SigningError.OK) {
|
|
113
|
-
throw Exception("Signing failed: ${output.errorMessage}")
|
|
114
|
-
}
|
|
115
|
-
output.encoded
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Helpers
|
|
121
|
-
private fun resolveCoinType(coin: String): CoinType = when (coin.lowercase()) {
|
|
122
|
-
"ethereum" -> CoinType.ETHEREUM
|
|
123
|
-
"solana" -> CoinType.SOLANA
|
|
124
|
-
"bnb" -> CoinType.SMARTCHAIN
|
|
125
|
-
else -> throw IllegalArgumentException("Unsupported coin: $coin")
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it) }
|
|
129
|
-
|
|
130
|
-
private fun String.hexToBytes(): ByteArray {
|
|
131
|
-
val s = removePrefix("0x").let { if (it.length % 2 == 0) it else "0$it" }
|
|
132
|
-
return ByteArray(s.length / 2) { s.substring(it * 2, it * 2 + 2).toInt(16).toByte() }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// BigInteger → minimal big-endian ByteString (strips Java's leading sign byte)
|
|
136
|
-
private fun BigInteger.toMinimalByteString(): ByteString {
|
|
137
|
-
val raw = toByteArray()
|
|
138
|
-
return if (raw.size > 1 && raw[0] == 0.toByte()) {
|
|
139
|
-
ByteString.copyFrom(raw, 1, raw.size - 1)
|
|
140
|
-
} else {
|
|
141
|
-
ByteString.copyFrom(raw)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import ExpoModulesCore
|
|
2
|
-
import WalletCore
|
|
3
|
-
|
|
4
|
-
// All numeric transaction fields are bare hex strings (no 0x prefix) to avoid JS number precision loss.
|
|
5
|
-
public class TrustWalletCoreModule: Module {
|
|
6
|
-
public func definition() -> ModuleDefinition {
|
|
7
|
-
Name("TrustWalletCore")
|
|
8
|
-
|
|
9
|
-
// Returns { mnemonic } — strength 128 = 12 words, 256 = 24 words
|
|
10
|
-
AsyncFunction("generateWallet") { (strength: Int, passphrase: String) throws -> [String: String] in
|
|
11
|
-
guard let wallet = HDWallet(strength: UInt32(strength), passphrase: passphrase) else {
|
|
12
|
-
throw Exception(name: "WalletError", description: "Failed to generate wallet")
|
|
13
|
-
}
|
|
14
|
-
return ["mnemonic": wallet.mnemonic]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Validates mnemonic and returns { mnemonic }
|
|
18
|
-
AsyncFunction("restoreWallet") { (mnemonic: String, passphrase: String) throws -> [String: String] in
|
|
19
|
-
guard HDWallet(mnemonic: mnemonic, passphrase: passphrase) != nil else {
|
|
20
|
-
throw Exception(name: "InvalidMnemonic", description: "Invalid mnemonic phrase")
|
|
21
|
-
}
|
|
22
|
-
return ["mnemonic": mnemonic]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Derives address and private key for a named coin
|
|
26
|
-
// coin: "ethereum" | "solana"
|
|
27
|
-
AsyncFunction("getAddressForCoin") { (mnemonic: String, coin: String, passphrase: String) throws -> [String: String] in
|
|
28
|
-
guard let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) else {
|
|
29
|
-
throw Exception(name: "InvalidMnemonic", description: "Invalid mnemonic phrase")
|
|
30
|
-
}
|
|
31
|
-
let coinType = try Self.resolveCoinType(coin)
|
|
32
|
-
return [
|
|
33
|
-
"address": wallet.getAddressForCoin(coin: coinType),
|
|
34
|
-
"privateKey": wallet.getKeyForCoin(coin: coinType).data.hexString,
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Signs an Ethereum transaction; returns 0x-prefixed RLP-encoded signed tx hex
|
|
39
|
-
// txParams: { to, valueHex, nonce, gasLimitHex, chainId, dataHex?,
|
|
40
|
-
// gasPriceHex? (legacy) | maxFeePerGasHex + maxPriorityFeePerGasHex (EIP-1559) }
|
|
41
|
-
AsyncFunction("signEthereumTransaction") { (privateKeyHex: String, txParams: [String: Any]) throws -> String in
|
|
42
|
-
guard let pkData = Data(hexString: privateKeyHex),
|
|
43
|
-
let privateKey = PrivateKey(data: pkData) else {
|
|
44
|
-
throw Exception(name: "InvalidKey", description: "Invalid private key hex")
|
|
45
|
-
}
|
|
46
|
-
guard let to = txParams["to"] as? String,
|
|
47
|
-
let nonce = txParams["nonce"] as? Int,
|
|
48
|
-
let gasLimHex = txParams["gasLimitHex"] as? String,
|
|
49
|
-
let chainId = txParams["chainId"] as? Int else {
|
|
50
|
-
throw Exception(name: "InvalidParams", description: "Missing required tx params")
|
|
51
|
-
}
|
|
52
|
-
let valueHex = (txParams["valueHex"] as? String) ?? "0"
|
|
53
|
-
let dataHex = (txParams["dataHex"] as? String) ?? ""
|
|
54
|
-
|
|
55
|
-
var input = EthereumSigningInput()
|
|
56
|
-
input.chainID = Self.intToData(chainId)
|
|
57
|
-
input.nonce = Self.intToData(nonce)
|
|
58
|
-
input.gasLimit = Self.hexData(gasLimHex) ?? Data()
|
|
59
|
-
input.toAddress = to
|
|
60
|
-
input.privateKey = privateKey.data
|
|
61
|
-
if !dataHex.isEmpty { input.txData = Self.hexData(dataHex) ?? Data() }
|
|
62
|
-
|
|
63
|
-
var transfer = EthereumTransaction.Transfer()
|
|
64
|
-
transfer.amount = Self.hexData(valueHex) ?? Data([0])
|
|
65
|
-
var tx = EthereumTransaction()
|
|
66
|
-
tx.transfer = transfer
|
|
67
|
-
input.transaction = tx
|
|
68
|
-
|
|
69
|
-
if let gasPriceHex = txParams["gasPriceHex"] as? String {
|
|
70
|
-
input.gasPrice = Self.hexData(gasPriceHex) ?? Data()
|
|
71
|
-
} else if let mfHex = txParams["maxFeePerGasHex"] as? String,
|
|
72
|
-
let pfHex = txParams["maxPriorityFeePerGasHex"] as? String {
|
|
73
|
-
input.maxFeePerGas = Self.hexData(mfHex) ?? Data()
|
|
74
|
-
input.maxInclusionFeePerGas = Self.hexData(pfHex) ?? Data()
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum)
|
|
78
|
-
guard output.error == .ok else {
|
|
79
|
-
throw Exception(name: "SigningFailed", description: output.errorMessage)
|
|
80
|
-
}
|
|
81
|
-
return "0x" + output.encoded.hexString
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Signs a Solana transfer; returns base64-encoded signed transaction
|
|
85
|
-
// txParams: { to, lamports (string), recentBlockhash }
|
|
86
|
-
AsyncFunction("signSolanaTransaction") { (privateKeyHex: String, txParams: [String: Any]) throws -> String in
|
|
87
|
-
guard let pkData = Data(hexString: privateKeyHex),
|
|
88
|
-
let privateKey = PrivateKey(data: pkData) else {
|
|
89
|
-
throw Exception(name: "InvalidKey", description: "Invalid private key hex")
|
|
90
|
-
}
|
|
91
|
-
guard let to = txParams["to"] as? String,
|
|
92
|
-
let lamportsStr = txParams["lamports"] as? String,
|
|
93
|
-
let lamports = UInt64(lamportsStr),
|
|
94
|
-
let recentBlockhash = txParams["recentBlockhash"] as? String else {
|
|
95
|
-
throw Exception(name: "InvalidParams", description: "Missing required Solana tx params (to, lamports, recentBlockhash)")
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
var transfer = SolanaTransfer()
|
|
99
|
-
transfer.recipient = to
|
|
100
|
-
transfer.value = lamports
|
|
101
|
-
|
|
102
|
-
var input = SolanaSigningInput()
|
|
103
|
-
input.recentBlockhash = recentBlockhash
|
|
104
|
-
input.privateKey = privateKey.data
|
|
105
|
-
input.transferTransaction = transfer
|
|
106
|
-
|
|
107
|
-
let output: SolanaSigningOutput = AnySigner.sign(input: input, coin: .solana)
|
|
108
|
-
guard output.error == .ok else {
|
|
109
|
-
throw Exception(name: "SigningFailed", description: output.errorMessage)
|
|
110
|
-
}
|
|
111
|
-
return output.encoded
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// MARK: - Helpers
|
|
116
|
-
|
|
117
|
-
private static func resolveCoinType(_ coin: String) throws -> CoinType {
|
|
118
|
-
switch coin.lowercased() {
|
|
119
|
-
case "ethereum": return .ethereum
|
|
120
|
-
case "solana": return .solana
|
|
121
|
-
case "bnb": return .smartChain
|
|
122
|
-
default:
|
|
123
|
-
throw Exception(name: "UnsupportedCoin", description: "Unsupported coin: \(coin)")
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Parses a hex string (with or without 0x, odd or even length) into Data
|
|
128
|
-
private static func hexData(_ hex: String) -> Data? {
|
|
129
|
-
let s = hex.hasPrefix("0x") ? String(hex.dropFirst(2)) : hex
|
|
130
|
-
let padded = s.count % 2 == 0 ? s : "0" + s
|
|
131
|
-
return padded.isEmpty ? Data([0]) : Data(hexString: padded)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Encodes a non-negative integer as minimal big-endian Data
|
|
135
|
-
private static func intToData(_ value: Int) -> Data {
|
|
136
|
-
guard value > 0 else { return Data([0]) }
|
|
137
|
-
var v = value
|
|
138
|
-
var bytes: [UInt8] = []
|
|
139
|
-
while v > 0 {
|
|
140
|
-
bytes.insert(UInt8(v & 0xFF), at: 0)
|
|
141
|
-
v >>= 8
|
|
142
|
-
}
|
|
143
|
-
return Data(bytes)
|
|
144
|
-
}
|
|
145
|
-
}
|