@chainberry/trust-wallet-core 1.0.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/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @chainberry/trust-wallet-core
2
+
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
+
5
+ Supported coins: Ethereum, Solana, BNB Smart Chain (see `SupportedCoin`).
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npx expo install @chainberry/trust-wallet-core
11
+ ```
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.
14
+
15
+ ### Android: GitHub Packages authentication required
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`.
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`.
35
+
36
+ ### iOS
37
+
38
+ `WalletCore` is pulled in via CocoaPods (`s.dependency 'TrustWalletCore'`) — no extra auth needed, `pod install` handles it.
39
+
40
+ ## Usage
41
+
42
+ ```ts
43
+ import { generateWallet, restoreWallet, getAddressForCoin } from "@chainberry/trust-wallet-core";
44
+
45
+ const { mnemonic, wallets } = await generateWallet(); // 128-bit / 12-word by default
46
+ // wallets: { ethereum: "0x...", solana: "...", bnb: "0x..." }
47
+
48
+ const restored = await restoreWallet(mnemonic);
49
+
50
+ const { address, privateKey } = await getAddressForCoin(mnemonic, "ethereum");
51
+ ```
52
+
53
+ ## API
54
+
55
+ - `generateWallet(strength = 128, passphrase = "")` — creates a new BIP-39 mnemonic (128 = 12 words, 256 = 24 words) and returns it along with the derived address for every supported coin.
56
+ - `restoreWallet(mnemonic, passphrase = "")` — validates an existing mnemonic and returns the same shape as `generateWallet`. Throws if the mnemonic is invalid.
57
+ - `getAddressForCoin(mnemonic, coin, passphrase = "")` — derives `{ address, privateKey }` for a single coin (`"ethereum" | "solana" | "bnb"`).
58
+
59
+ ## Security note
60
+
61
+ `getAddressForCoin` returns the raw private key as hex to JS. That's a deliberate tradeoff for this module — it does no key storage or signing orchestration itself, it only derives keys. Callers are responsible for how the mnemonic and private keys are held, encrypted at rest, and cleared from memory. Don't treat this module as a secure enclave; it isn't one.
62
+
63
+ ## License
64
+
65
+ MIT. Trust Wallet Core itself is Apache-2.0 — see [trustwallet/wallet-core](https://github.com/trustwallet/wallet-core) for its license.
@@ -0,0 +1,22 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'TrustWalletCoreModule'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = s.summary
10
+ # TODO: point at the real public repo URL before publishing to npm.
11
+ s.homepage = 'https://github.com/chainberry/trust-wallet-core'
12
+ s.license = package['license']
13
+ s.author = 'Chainberry'
14
+ s.platform = :ios, '16.0'
15
+ s.source = { git: 'https://github.com/chainberry/trust-wallet-core.git', tag: "v#{package['version']}" }
16
+ s.static_framework = true
17
+
18
+ s.source_files = 'ios/**/*.{h,m,mm,swift}'
19
+
20
+ s.dependency 'ExpoModulesCore'
21
+ s.dependency 'TrustWalletCore'
22
+ end
@@ -0,0 +1,67 @@
1
+ apply plugin: 'com.android.library'
2
+ apply plugin: 'kotlin-android'
3
+
4
+ apply from: new File(
5
+ providers.exec {
6
+ workingDir(rootDir)
7
+ commandLine("node", "--print", "require.resolve('expo-modules-core/package.json')")
8
+ }.standardOutput.asText.get().trim(),
9
+ "../android/ExpoModulesCorePlugin.gradle"
10
+ )
11
+
12
+ applyKotlinExpoModulesCorePlugin()
13
+
14
+ android {
15
+ namespace 'expo.modules.trustwalletcore'
16
+ compileSdkVersion 35
17
+
18
+ defaultConfig {
19
+ minSdkVersion 24
20
+ targetSdkVersion 35
21
+ }
22
+
23
+ compileOptions {
24
+ sourceCompatibility JavaVersion.VERSION_17
25
+ targetCompatibility JavaVersion.VERSION_17
26
+ }
27
+
28
+ kotlinOptions {
29
+ jvmTarget = '17'
30
+ }
31
+ }
32
+
33
+ // ─── wallet-core credentials ──────────────────────────────────────────────────
34
+ // GitHub Packages requires auth even for public packages.
35
+ // Priority order (first non-empty wins):
36
+ // 1. Env vars GITHUB_ACTOR / GITHUB_TOKEN (CI / one-off terminal)
37
+ // 2. Gradle props gpr.user / gpr.key in (permanent dev setup)
38
+ // ~/.gradle/gradle.properties
39
+ //
40
+ // One-time dev setup — paste into ~/.gradle/gradle.properties:
41
+ // gpr.user=your-github-username
42
+ // gpr.key=ghp_xxxxxxxxxxxx (classic PAT, read:packages scope only)
43
+ //
44
+ // Generate a token at: github.com/settings/tokens → "classic" → check read:packages
45
+
46
+ def githubUser = System.getenv("GITHUB_ACTOR")
47
+ ?: (project.hasProperty("gpr.user") ? project.property("gpr.user") : "")
48
+ def githubToken = System.getenv("GITHUB_TOKEN")
49
+ ?: (project.hasProperty("gpr.key") ? project.property("gpr.key") : "")
50
+
51
+ repositories {
52
+ maven {
53
+ url = uri("https://maven.pkg.github.com/trustwallet/wallet-core")
54
+ credentials {
55
+ username = githubUser
56
+ password = githubToken
57
+ }
58
+ }
59
+ }
60
+
61
+ useCoreDependencies()
62
+
63
+ dependencies {
64
+ // Check github.com/trustwallet/wallet-core/releases for the latest version
65
+ implementation 'com.trustwallet:wallet-core:4.1.19'
66
+ implementation 'com.google.protobuf:protobuf-javalite:3.21.9'
67
+ }
@@ -0,0 +1,143 @@
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
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "platforms": ["ios", "android"],
3
+ "ios": {
4
+ "modules": ["TrustWalletCoreModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["expo.modules.trustwalletcore.TrustWalletCoreModule"]
8
+ }
9
+ }
@@ -0,0 +1,145 @@
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
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@chainberry/trust-wallet-core",
3
+ "version": "1.0.0",
4
+ "description": "Expo native module wrapping Trust Wallet Core for HD wallet generation, address derivation, and transaction signing (Ethereum, Solana, BNB)",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "license": "MIT",
8
+ "files": [
9
+ "src",
10
+ "ios/TrustWalletCoreModule.swift",
11
+ "android/src",
12
+ "android/build.gradle",
13
+ "expo-module.config.json",
14
+ "TrustWalletCoreModule.podspec",
15
+ "README.md"
16
+ ],
17
+ "peerDependencies": {
18
+ "expo": "*",
19
+ "expo-modules-core": "*"
20
+ }
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { requireNativeModule } from "expo-modules-core";
2
+
3
+ export type SupportedCoin = "ethereum" | "solana" | "bnb";
4
+
5
+ export type CoinAccount = {
6
+ address: string;
7
+ privateKey: string;
8
+ };
9
+
10
+ export type WalletResult = {
11
+ mnemonic: string;
12
+ wallets: Record<SupportedCoin, string>;
13
+ };
14
+
15
+ const TrustWalletCore = requireNativeModule("TrustWalletCore");
16
+
17
+ // ─── Native calls ────────────────────────────────────────────────────────────
18
+
19
+ /** Create a new HD wallet and store encrypted keys for all supported coins */
20
+ export async function generateWallet(
21
+ strength = 128,
22
+ passphrase = "",
23
+ ): Promise<WalletResult> {
24
+ const { mnemonic } = (await TrustWalletCore.generateWallet(
25
+ strength,
26
+ passphrase,
27
+ )) as { mnemonic: string };
28
+ const coins: SupportedCoin[] = ["ethereum", "solana", "bnb"];
29
+ const entries = await Promise.all(
30
+ coins.map(async (coin) => {
31
+ const { address } = await getAddressForCoin(
32
+ mnemonic,
33
+ coin,
34
+ passphrase,
35
+ );
36
+ return [coin, address] as const;
37
+ }),
38
+ );
39
+ return {
40
+ mnemonic,
41
+ wallets: Object.fromEntries(entries) as Record<SupportedCoin, string>,
42
+ };
43
+ }
44
+
45
+ /** Validate and restore an existing wallet from a BIP-39 mnemonic */
46
+ export async function restoreWallet(
47
+ mnemonic: string,
48
+ passphrase = "",
49
+ ): Promise<WalletResult> {
50
+ await TrustWalletCore.restoreWallet(mnemonic, passphrase);
51
+ const coins: SupportedCoin[] = ["ethereum", "solana", "bnb"];
52
+ const entries = await Promise.all(
53
+ coins.map(async (coin) => {
54
+ const { address } = await getAddressForCoin(
55
+ mnemonic,
56
+ coin,
57
+ passphrase,
58
+ );
59
+ return [coin, address] as const;
60
+ }),
61
+ );
62
+ return {
63
+ mnemonic,
64
+ wallets: Object.fromEntries(entries) as Record<SupportedCoin, string>,
65
+ };
66
+ }
67
+
68
+ /** Derive the address and private key for a specific coin from a mnemonic */
69
+ export function getAddressForCoin(
70
+ mnemonic: string,
71
+ coin: SupportedCoin,
72
+ passphrase = "",
73
+ ): Promise<CoinAccount> {
74
+ return TrustWalletCore.getAddressForCoin(mnemonic, coin, passphrase);
75
+ }