@bubolabs/wallet-rn-sdk 0.1.2 → 0.1.5
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 +148 -23
- package/android/src/main/java/com/bubo/wallet/BuboWalletSmokeModule.kt +103 -0
- package/android/src/main/jniLibs/arm64-v8a/libbubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libuniffi_bubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libbubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libuniffi_bubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/x86/libbubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/x86/libuniffi_bubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/x86_64/libbubo_wallet_ffi.so +0 -0
- package/android/src/main/jniLibs/x86_64/libuniffi_bubo_wallet_ffi.so +0 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/native.d.ts +4 -0
- package/dist/native.d.ts.map +1 -1
- package/dist/types.d.ts +25 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -3
- package/dist/wallet.d.ts +6 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +144 -0
- package/ios/BuboWalletSmokeModule.m +21 -0
- package/ios/BuboWalletSmokeModule.swift +143 -0
- package/ios/lib/BuboWalletFFI.xcframework/Info.plist +5 -5
- package/ios/lib/BuboWalletFFI.xcframework/ios-arm64/libbubo_wallet_ffi.a +0 -0
- package/ios/lib/BuboWalletFFI.xcframework/ios-arm64_x86_64-simulator/libbubo_wallet_ffi.a +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,49 +1,174 @@
|
|
|
1
1
|
# @bubolabs/wallet-rn-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React Native SDK for the Bubo wallet UniFFI bridge.
|
|
4
4
|
|
|
5
|
-
This package
|
|
5
|
+
This package uses standard RN native-module layout and supports autolinking:
|
|
6
6
|
|
|
7
|
-
- `android/` React Native Android library module
|
|
8
|
-
- `ios/` Pod sources + vendored `BuboWalletFFI.xcframework`
|
|
7
|
+
- `android/` React Native Android library module
|
|
8
|
+
- `ios/` Pod sources + vendored `BuboWalletFFI.xcframework`
|
|
9
9
|
- `react-native.config.js` for Android package registration
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Install
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
-
|
|
13
|
+
```bash
|
|
14
|
+
npm install @bubolabs/wallet-rn-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
iOS:
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
```bash
|
|
20
|
+
cd ios && pod install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Core API
|
|
17
24
|
|
|
18
25
|
```ts
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
import {
|
|
27
|
+
init,
|
|
28
|
+
listSupportedChains,
|
|
29
|
+
deriveAddress,
|
|
30
|
+
buildAndSign,
|
|
31
|
+
} from "@bubolabs/wallet-rn-sdk";
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `init(): Promise<void>`
|
|
35
|
+
- `listSupportedChains(): Promise<SupportedChainInfo[]>`
|
|
36
|
+
- `deriveAddress(request: DeriveAddressRequest): Promise<string>`
|
|
37
|
+
- `buildAndSign(request: BuildAndSignRequest): Promise<BuildAndSignResult>`
|
|
38
|
+
|
|
39
|
+
### Types
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
type SupportedChainInfo = {
|
|
43
|
+
chainId: string;
|
|
44
|
+
capabilities: string[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type DeriveAddressRequest = {
|
|
48
|
+
chainId: string;
|
|
49
|
+
mnemonic: string;
|
|
50
|
+
account: number; // uint32
|
|
51
|
+
index: number; // uint32
|
|
52
|
+
passphrase?: string | null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type BuildAndSignRequest = {
|
|
56
|
+
chainId: string;
|
|
57
|
+
mnemonic: string;
|
|
58
|
+
txPayload: string;
|
|
59
|
+
txEncoding?: "utf8" | "hex" | "base64"; // default: utf8
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type BuildAndSignResult = {
|
|
63
|
+
chainId: string;
|
|
64
|
+
signedTxHex: string;
|
|
25
65
|
};
|
|
26
66
|
```
|
|
27
67
|
|
|
28
|
-
|
|
68
|
+
### Example
|
|
29
69
|
|
|
30
70
|
```ts
|
|
31
|
-
import {
|
|
71
|
+
import {
|
|
72
|
+
init,
|
|
73
|
+
listSupportedChains,
|
|
74
|
+
deriveAddress,
|
|
75
|
+
buildAndSign,
|
|
76
|
+
} from "@bubolabs/wallet-rn-sdk";
|
|
77
|
+
|
|
78
|
+
await init();
|
|
79
|
+
const chains = await listSupportedChains();
|
|
80
|
+
const address = await deriveAddress({
|
|
81
|
+
chainId: "btc",
|
|
82
|
+
mnemonic: "<mnemonic>",
|
|
83
|
+
account: 0,
|
|
84
|
+
index: 0,
|
|
85
|
+
passphrase: null,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const signed = await buildAndSign({
|
|
89
|
+
chainId: "btc",
|
|
90
|
+
mnemonic: "<mnemonic>",
|
|
91
|
+
txPayload: "<tx payload>",
|
|
92
|
+
txEncoding: "utf8",
|
|
93
|
+
});
|
|
94
|
+
```
|
|
32
95
|
|
|
33
|
-
|
|
34
|
-
|
|
96
|
+
### `txPayload` JSON Examples
|
|
97
|
+
|
|
98
|
+
`txEncoding` uses `"utf8"` by default, so pass JSON text directly.
|
|
99
|
+
|
|
100
|
+
BTC:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"account": 0,
|
|
105
|
+
"address_index": 0,
|
|
106
|
+
"change_index": 0,
|
|
107
|
+
"network": "mainnet",
|
|
108
|
+
"prev_txid": "0000000000000000000000000000000000000000000000000000000000000001",
|
|
109
|
+
"prev_vout": 0,
|
|
110
|
+
"prev_script_pubkey_hex": "76a914d986ed01b7a22225a70edbf2ba7cfb63a15cb3aa88ac",
|
|
111
|
+
"prev_value_sat": 100000,
|
|
112
|
+
"to_address": "1BoatSLRHtKNngkdXEeobR76b53LETtpyT",
|
|
113
|
+
"amount_sat": 10000,
|
|
114
|
+
"fee_sat": 1000,
|
|
115
|
+
"lock_time": 0,
|
|
116
|
+
"sequence": 4294967295
|
|
117
|
+
}
|
|
35
118
|
```
|
|
36
119
|
|
|
37
|
-
|
|
120
|
+
ETH (legacy transfer):
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"account": 0,
|
|
125
|
+
"index": 0,
|
|
126
|
+
"chain_id": 1,
|
|
127
|
+
"nonce": 0,
|
|
128
|
+
"gas_price_wei": "1000000000",
|
|
129
|
+
"gas_limit": 21000,
|
|
130
|
+
"to_address": "0x1111111111111111111111111111111111111111",
|
|
131
|
+
"value_wei": "1000000000000000"
|
|
132
|
+
}
|
|
133
|
+
```
|
|
38
134
|
|
|
39
|
-
|
|
40
|
-
|
|
135
|
+
Solana (system transfer):
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"account": 0,
|
|
140
|
+
"index": 0,
|
|
141
|
+
"to_address": "11111111111111111111111111111111",
|
|
142
|
+
"lamports": 1000,
|
|
143
|
+
"recent_blockhash": "11111111111111111111111111111111"
|
|
144
|
+
}
|
|
41
145
|
```
|
|
42
146
|
|
|
43
|
-
|
|
147
|
+
### Chain Feature Notes
|
|
148
|
+
|
|
149
|
+
- Published package content depends on Rust feature set at build time.
|
|
150
|
+
- When `BUBO_RUST_FEATURES` is not set, packaging auto-enables all `chain-*` features from `crates/ffi/Cargo.toml`.
|
|
151
|
+
- You can still override for focused builds, for example:
|
|
44
152
|
|
|
45
153
|
```bash
|
|
46
|
-
|
|
154
|
+
BUBO_RUST_FEATURES=chain-eth,chain-btc ./scripts/build-rn-sdk-native-artifacts.sh
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Compatibility Smoke API
|
|
158
|
+
|
|
159
|
+
Legacy smoke APIs remain available for regression checks:
|
|
160
|
+
|
|
161
|
+
- `runSmoke()`
|
|
162
|
+
- `runBtcSmoke()`
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
type WalletSmokeResult = {
|
|
166
|
+
supportedChains: Array<{ chainId: string; capabilities: string[] }>;
|
|
167
|
+
deriveChainId: string;
|
|
168
|
+
derivedAddress: string;
|
|
169
|
+
signChainId: string | null;
|
|
170
|
+
signedTxHex: string | null;
|
|
171
|
+
};
|
|
47
172
|
```
|
|
48
173
|
|
|
49
174
|
## Distribution Scripts
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.bubo.wallet
|
|
2
2
|
|
|
3
|
+
import android.util.Base64
|
|
3
4
|
import com.facebook.react.bridge.Arguments
|
|
4
5
|
import com.facebook.react.bridge.Promise
|
|
5
6
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
@@ -8,15 +9,77 @@ import com.facebook.react.bridge.ReactMethod
|
|
|
8
9
|
import com.facebook.react.bridge.WritableArray
|
|
9
10
|
import com.facebook.react.bridge.WritableMap
|
|
10
11
|
import uniffi.bubo_wallet_ffi.SupportedChainInfo
|
|
12
|
+
import uniffi.bubo_wallet_ffi.WalletLib
|
|
11
13
|
import uniffi.bubo_wallet_ffi.WalletLibSmoke
|
|
12
14
|
import uniffi.bubo_wallet_ffi.WalletSmokeResult
|
|
13
15
|
|
|
14
16
|
class BuboWalletSmokeModule(
|
|
15
17
|
reactContext: ReactApplicationContext
|
|
16
18
|
) : ReactContextBaseJavaModule(reactContext) {
|
|
19
|
+
private val wallet: WalletLib = WalletLib()
|
|
17
20
|
|
|
18
21
|
override fun getName(): String = "BuboWalletSmoke"
|
|
19
22
|
|
|
23
|
+
@ReactMethod
|
|
24
|
+
fun init(promise: Promise) {
|
|
25
|
+
try {
|
|
26
|
+
wallet.init()
|
|
27
|
+
promise.resolve(null)
|
|
28
|
+
} catch (t: Throwable) {
|
|
29
|
+
promise.reject("BUBO_WALLET", t.message, t)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@ReactMethod
|
|
34
|
+
fun listSupportedChains(promise: Promise) {
|
|
35
|
+
try {
|
|
36
|
+
val chains = wallet.listSupportedChains()
|
|
37
|
+
promise.resolve(toWritableChains(chains))
|
|
38
|
+
} catch (t: Throwable) {
|
|
39
|
+
promise.reject("BUBO_WALLET", t.message, t)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@ReactMethod
|
|
44
|
+
fun deriveAddress(
|
|
45
|
+
chainId: String,
|
|
46
|
+
mnemonic: String,
|
|
47
|
+
account: Double,
|
|
48
|
+
index: Double,
|
|
49
|
+
passphrase: String?,
|
|
50
|
+
promise: Promise
|
|
51
|
+
) {
|
|
52
|
+
try {
|
|
53
|
+
val address = wallet.deriveAddress(
|
|
54
|
+
chainId = chainId,
|
|
55
|
+
mnemonic = mnemonic,
|
|
56
|
+
account = asUInt32(account, "account"),
|
|
57
|
+
index = asUInt32(index, "index"),
|
|
58
|
+
passphrase = passphrase,
|
|
59
|
+
)
|
|
60
|
+
promise.resolve(address)
|
|
61
|
+
} catch (t: Throwable) {
|
|
62
|
+
promise.reject("BUBO_WALLET", t.message, t)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactMethod
|
|
67
|
+
fun buildAndSign(
|
|
68
|
+
chainId: String,
|
|
69
|
+
mnemonic: String,
|
|
70
|
+
txPayload: String,
|
|
71
|
+
txEncoding: String,
|
|
72
|
+
promise: Promise
|
|
73
|
+
) {
|
|
74
|
+
try {
|
|
75
|
+
val payloadBytes = decodePayload(txPayload, txEncoding)
|
|
76
|
+
val signed = wallet.buildAndSign(chainId, mnemonic, payloadBytes)
|
|
77
|
+
promise.resolve(signed.toHex())
|
|
78
|
+
} catch (t: Throwable) {
|
|
79
|
+
promise.reject("BUBO_WALLET", t.message, t)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
20
83
|
@ReactMethod
|
|
21
84
|
fun runSmoke(promise: Promise) {
|
|
22
85
|
try {
|
|
@@ -62,4 +125,44 @@ class BuboWalletSmokeModule(
|
|
|
62
125
|
}
|
|
63
126
|
return out
|
|
64
127
|
}
|
|
128
|
+
|
|
129
|
+
private fun asUInt32(value: Double, field: String): UInt {
|
|
130
|
+
require(value.isFinite() && value >= 0 && value <= UInt.MAX_VALUE.toDouble() && value % 1.0 == 0.0) {
|
|
131
|
+
"$field must be uint32"
|
|
132
|
+
}
|
|
133
|
+
return value.toLong().toUInt()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private fun decodePayload(payload: String, encoding: String): ByteArray {
|
|
137
|
+
return when (encoding.lowercase()) {
|
|
138
|
+
"utf8" -> payload.toByteArray(Charsets.UTF_8)
|
|
139
|
+
"base64" -> Base64.decode(payload, Base64.DEFAULT)
|
|
140
|
+
"hex" -> decodeHex(payload)
|
|
141
|
+
else -> error("txEncoding must be utf8|hex|base64")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private fun decodeHex(value: String): ByteArray {
|
|
146
|
+
val raw = value.trim()
|
|
147
|
+
require(raw.length % 2 == 0) { "hex payload length must be even" }
|
|
148
|
+
val out = ByteArray(raw.length / 2)
|
|
149
|
+
var i = 0
|
|
150
|
+
while (i < raw.length) {
|
|
151
|
+
val hi = raw[i].digitToIntOrNull(16)
|
|
152
|
+
val lo = raw[i + 1].digitToIntOrNull(16)
|
|
153
|
+
require(hi != null && lo != null) { "txPayload must be valid hex" }
|
|
154
|
+
out[i / 2] = ((hi shl 4) or lo).toByte()
|
|
155
|
+
i += 2
|
|
156
|
+
}
|
|
157
|
+
return out
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private fun ByteArray.toHex(): String {
|
|
161
|
+
val out = StringBuilder(size * 2)
|
|
162
|
+
for (byte in this) {
|
|
163
|
+
out.append(((byte.toInt() ushr 4) and 0xF).toString(16))
|
|
164
|
+
out.append((byte.toInt() and 0xF).toString(16))
|
|
165
|
+
}
|
|
166
|
+
return out.toString()
|
|
167
|
+
}
|
|
65
168
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
export { buildAndSign, deriveAddress, init, listSupportedChains } from "./wallet";
|
|
1
2
|
export { runBtcSmoke, runSmoke } from "./smoke";
|
|
2
|
-
export type { SupportedChainInfo, WalletSmokeErrorCode, WalletSmokeResult, } from "./types";
|
|
3
|
-
export { WalletSmokeError } from "./types";
|
|
3
|
+
export type { BuildAndSignRequest, BuildAndSignResult, DeriveAddressRequest, SupportedChainInfo, TxPayloadEncoding, WalletErrorCode, WalletSmokeErrorCode, WalletSmokeResult, } from "./types";
|
|
4
|
+
export { WalletError, WalletSmokeError } from "./types";
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WalletSmokeError = exports.runSmoke = exports.runBtcSmoke = void 0;
|
|
3
|
+
exports.WalletSmokeError = exports.WalletError = exports.runSmoke = exports.runBtcSmoke = exports.listSupportedChains = exports.init = exports.deriveAddress = exports.buildAndSign = void 0;
|
|
4
|
+
var wallet_1 = require("./wallet");
|
|
5
|
+
Object.defineProperty(exports, "buildAndSign", { enumerable: true, get: function () { return wallet_1.buildAndSign; } });
|
|
6
|
+
Object.defineProperty(exports, "deriveAddress", { enumerable: true, get: function () { return wallet_1.deriveAddress; } });
|
|
7
|
+
Object.defineProperty(exports, "init", { enumerable: true, get: function () { return wallet_1.init; } });
|
|
8
|
+
Object.defineProperty(exports, "listSupportedChains", { enumerable: true, get: function () { return wallet_1.listSupportedChains; } });
|
|
4
9
|
var smoke_1 = require("./smoke");
|
|
5
10
|
Object.defineProperty(exports, "runBtcSmoke", { enumerable: true, get: function () { return smoke_1.runBtcSmoke; } });
|
|
6
11
|
Object.defineProperty(exports, "runSmoke", { enumerable: true, get: function () { return smoke_1.runSmoke; } });
|
|
7
12
|
var types_1 = require("./types");
|
|
13
|
+
Object.defineProperty(exports, "WalletError", { enumerable: true, get: function () { return types_1.WalletError; } });
|
|
8
14
|
Object.defineProperty(exports, "WalletSmokeError", { enumerable: true, get: function () { return types_1.WalletSmokeError; } });
|
package/dist/native.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export type NativeWalletSmokeResult = {
|
|
|
10
10
|
signedTxHex: string | null;
|
|
11
11
|
};
|
|
12
12
|
type BuboWalletSmokeModule = {
|
|
13
|
+
init(): Promise<void>;
|
|
14
|
+
listSupportedChains(): Promise<NativeSupportedChainInfo[]>;
|
|
15
|
+
deriveAddress(chainId: string, mnemonic: string, account: number, index: number, passphrase: string | null): Promise<string>;
|
|
16
|
+
buildAndSign(chainId: string, mnemonic: string, txPayload: string, txEncoding: string): Promise<string>;
|
|
13
17
|
runSmoke(): Promise<NativeWalletSmokeResult>;
|
|
14
18
|
runBtcSmoke(): Promise<NativeWalletSmokeResult>;
|
|
15
19
|
};
|
package/dist/native.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,eAAe,EAAE,wBAAwB,EAAE,CAAC;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7C,WAAW,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACjD,CAAC;AA+BF,wBAAgB,wBAAwB,IAAI,qBAAqB,CAMhE"}
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,eAAe,EAAE,wBAAwB,EAAE,CAAC;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,mBAAmB,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC;IAC3D,aAAa,CACX,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,YAAY,CACV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7C,WAAW,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACjD,CAAC;AA+BF,wBAAgB,wBAAwB,IAAI,qBAAqB,CAMhE"}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,24 @@ export type SupportedChainInfo = {
|
|
|
2
2
|
chainId: string;
|
|
3
3
|
capabilities: string[];
|
|
4
4
|
};
|
|
5
|
+
export type TxPayloadEncoding = "utf8" | "hex" | "base64";
|
|
6
|
+
export type DeriveAddressRequest = {
|
|
7
|
+
chainId: string;
|
|
8
|
+
mnemonic: string;
|
|
9
|
+
account: number;
|
|
10
|
+
index: number;
|
|
11
|
+
passphrase?: string | null;
|
|
12
|
+
};
|
|
13
|
+
export type BuildAndSignRequest = {
|
|
14
|
+
chainId: string;
|
|
15
|
+
mnemonic: string;
|
|
16
|
+
txPayload: string;
|
|
17
|
+
txEncoding?: TxPayloadEncoding;
|
|
18
|
+
};
|
|
19
|
+
export type BuildAndSignResult = {
|
|
20
|
+
chainId: string;
|
|
21
|
+
signedTxHex: string;
|
|
22
|
+
};
|
|
5
23
|
export type WalletSmokeResult = {
|
|
6
24
|
supportedChains: SupportedChainInfo[];
|
|
7
25
|
deriveChainId: string;
|
|
@@ -9,10 +27,14 @@ export type WalletSmokeResult = {
|
|
|
9
27
|
signChainId: string | null;
|
|
10
28
|
signedTxHex: string | null;
|
|
11
29
|
};
|
|
12
|
-
export type
|
|
13
|
-
export declare class
|
|
14
|
-
readonly code:
|
|
30
|
+
export type WalletErrorCode = "MODULE_NOT_LINKED" | "NATIVE_ERROR" | "INVALID_RESULT" | "INVALID_ARGUMENT" | "BTC_SIGN_UNAVAILABLE" | "NOT_INITIALIZED" | "UNSUPPORTED_CHAIN" | "UNSUPPORTED_CAPABILITY" | "INVALID_MNEMONIC" | "INVALID_DERIVATION_PATH" | "INVALID_ADDRESS" | "TX_BUILD_FAILED" | "TX_SIGN_FAILED" | "SERIALIZATION_FAILED" | "INTERNAL_ERROR";
|
|
31
|
+
export declare class WalletError extends Error {
|
|
32
|
+
readonly code: WalletErrorCode;
|
|
15
33
|
readonly causeValue?: unknown;
|
|
34
|
+
constructor(code: WalletErrorCode, message: string, causeValue?: unknown);
|
|
35
|
+
}
|
|
36
|
+
export type WalletSmokeErrorCode = WalletErrorCode;
|
|
37
|
+
export declare class WalletSmokeError extends WalletError {
|
|
16
38
|
constructor(code: WalletSmokeErrorCode, message: string, causeValue?: unknown);
|
|
17
39
|
}
|
|
18
40
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,sBAAsB,GACtB,iBAAiB,GACjB,mBAAmB,GACnB,wBAAwB,GACxB,kBAAkB,GAClB,yBAAyB,GACzB,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB,GAChB,sBAAsB,GACtB,gBAAgB,CAAC;AAErB,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,IAAI,EAAE,eAAe,CAAC;IACtC,SAAgB,UAAU,CAAC,EAAE,OAAO,CAAC;gBAGnC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,OAAO;CAOvB;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEnD,qBAAa,gBAAiB,SAAQ,WAAW;gBAE7C,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,OAAO;CAKvB"}
|
package/dist/types.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WalletSmokeError = void 0;
|
|
4
|
-
class
|
|
3
|
+
exports.WalletSmokeError = exports.WalletError = void 0;
|
|
4
|
+
class WalletError extends Error {
|
|
5
5
|
constructor(code, message, causeValue) {
|
|
6
6
|
super(message);
|
|
7
|
-
this.name = "
|
|
7
|
+
this.name = "WalletError";
|
|
8
8
|
this.code = code;
|
|
9
9
|
this.causeValue = causeValue;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
exports.WalletError = WalletError;
|
|
13
|
+
class WalletSmokeError extends WalletError {
|
|
14
|
+
constructor(code, message, causeValue) {
|
|
15
|
+
super(code, message, causeValue);
|
|
16
|
+
this.name = "WalletSmokeError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
12
19
|
exports.WalletSmokeError = WalletSmokeError;
|
package/dist/wallet.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BuildAndSignRequest, BuildAndSignResult, DeriveAddressRequest, SupportedChainInfo } from "./types";
|
|
2
|
+
export declare function init(): Promise<void>;
|
|
3
|
+
export declare function listSupportedChains(): Promise<SupportedChainInfo[]>;
|
|
4
|
+
export declare function deriveAddress(request: DeriveAddressRequest): Promise<string>;
|
|
5
|
+
export declare function buildAndSign(request: BuildAndSignRequest): Promise<BuildAndSignResult>;
|
|
6
|
+
//# sourceMappingURL=wallet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAInB,MAAM,SAAS,CAAC;AAgHjB,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAM1C;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAUzE;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAoB7B"}
|
package/dist/wallet.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init = init;
|
|
4
|
+
exports.listSupportedChains = listSupportedChains;
|
|
5
|
+
exports.deriveAddress = deriveAddress;
|
|
6
|
+
exports.buildAndSign = buildAndSign;
|
|
7
|
+
const native_1 = require("./native");
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
const HEX_REGEX = /^[0-9a-fA-F]+$/;
|
|
10
|
+
function isObject(value) {
|
|
11
|
+
return typeof value === "object" && value !== null;
|
|
12
|
+
}
|
|
13
|
+
function parseCoreErrorCode(message) {
|
|
14
|
+
var _a;
|
|
15
|
+
const m = message.match(/^([A-Za-z]+):/);
|
|
16
|
+
const ffiCode = (_a = m === null || m === void 0 ? void 0 : m[1]) !== null && _a !== void 0 ? _a : "";
|
|
17
|
+
switch (ffiCode) {
|
|
18
|
+
case "NotInitialized":
|
|
19
|
+
return "NOT_INITIALIZED";
|
|
20
|
+
case "UnsupportedChain":
|
|
21
|
+
return "UNSUPPORTED_CHAIN";
|
|
22
|
+
case "UnsupportedCapability":
|
|
23
|
+
return "UNSUPPORTED_CAPABILITY";
|
|
24
|
+
case "InvalidMnemonic":
|
|
25
|
+
return "INVALID_MNEMONIC";
|
|
26
|
+
case "InvalidDerivationPath":
|
|
27
|
+
return "INVALID_DERIVATION_PATH";
|
|
28
|
+
case "InvalidAddress":
|
|
29
|
+
return "INVALID_ADDRESS";
|
|
30
|
+
case "TxBuildFailed":
|
|
31
|
+
return "TX_BUILD_FAILED";
|
|
32
|
+
case "TxSignFailed":
|
|
33
|
+
return "TX_SIGN_FAILED";
|
|
34
|
+
case "SerializationFailed":
|
|
35
|
+
return "SERIALIZATION_FAILED";
|
|
36
|
+
case "Internal":
|
|
37
|
+
return "INTERNAL_ERROR";
|
|
38
|
+
default:
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function mapNativeError(error) {
|
|
43
|
+
var _a;
|
|
44
|
+
if (error instanceof types_1.WalletError) {
|
|
45
|
+
return error;
|
|
46
|
+
}
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
return new types_1.WalletError((_a = parseCoreErrorCode(message)) !== null && _a !== void 0 ? _a : "NATIVE_ERROR", message, error);
|
|
49
|
+
}
|
|
50
|
+
function assertNonEmptyString(value, field) {
|
|
51
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
52
|
+
throw new types_1.WalletError("INVALID_ARGUMENT", `${field} must be a non-empty string`, value);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
function assertUint32(value, field) {
|
|
57
|
+
if (typeof value !== "number" ||
|
|
58
|
+
!Number.isFinite(value) ||
|
|
59
|
+
!Number.isInteger(value) ||
|
|
60
|
+
value < 0 ||
|
|
61
|
+
value > 0xffffffff) {
|
|
62
|
+
throw new types_1.WalletError("INVALID_ARGUMENT", `${field} must be a uint32`, value);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function assertEncoding(value) {
|
|
67
|
+
const encoding = value !== null && value !== void 0 ? value : "utf8";
|
|
68
|
+
if (encoding !== "utf8" && encoding !== "hex" && encoding !== "base64") {
|
|
69
|
+
throw new types_1.WalletError("INVALID_ARGUMENT", "txEncoding must be one of: utf8 | hex | base64", value);
|
|
70
|
+
}
|
|
71
|
+
return encoding;
|
|
72
|
+
}
|
|
73
|
+
function normalizeChainInfo(value, index) {
|
|
74
|
+
if (!isObject(value)) {
|
|
75
|
+
throw new types_1.WalletError("INVALID_RESULT", `supportedChains[${index}] is not an object`, value);
|
|
76
|
+
}
|
|
77
|
+
const chainId = value.chainId;
|
|
78
|
+
const capabilities = value.capabilities;
|
|
79
|
+
if (typeof chainId !== "string" || chainId.length === 0) {
|
|
80
|
+
throw new types_1.WalletError("INVALID_RESULT", `supportedChains[${index}].chainId must be a non-empty string`, value);
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(capabilities) || !capabilities.every((item) => typeof item === "string")) {
|
|
83
|
+
throw new types_1.WalletError("INVALID_RESULT", `supportedChains[${index}].capabilities must be string[]`, value);
|
|
84
|
+
}
|
|
85
|
+
return { chainId, capabilities };
|
|
86
|
+
}
|
|
87
|
+
function validateBuildPayload(payload, txEncoding) {
|
|
88
|
+
if (txEncoding === "hex") {
|
|
89
|
+
if (payload.length % 2 !== 0 || !HEX_REGEX.test(payload)) {
|
|
90
|
+
throw new types_1.WalletError("INVALID_ARGUMENT", "txPayload must be valid even-length hex when txEncoding=hex", payload);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function init() {
|
|
95
|
+
try {
|
|
96
|
+
await (0, native_1.getBuboWalletSmokeModule)().init();
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw mapNativeError(error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function listSupportedChains() {
|
|
103
|
+
try {
|
|
104
|
+
const raw = await (0, native_1.getBuboWalletSmokeModule)().listSupportedChains();
|
|
105
|
+
if (!Array.isArray(raw)) {
|
|
106
|
+
throw new types_1.WalletError("INVALID_RESULT", "native result must be array", raw);
|
|
107
|
+
}
|
|
108
|
+
return raw.map(normalizeChainInfo);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
throw mapNativeError(error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function deriveAddress(request) {
|
|
115
|
+
const chainId = assertNonEmptyString(request.chainId, "chainId");
|
|
116
|
+
const mnemonic = assertNonEmptyString(request.mnemonic, "mnemonic");
|
|
117
|
+
const account = assertUint32(request.account, "account");
|
|
118
|
+
const index = assertUint32(request.index, "index");
|
|
119
|
+
const passphrase = request.passphrase == null ? null : String(request.passphrase);
|
|
120
|
+
try {
|
|
121
|
+
const address = await (0, native_1.getBuboWalletSmokeModule)().deriveAddress(chainId, mnemonic, account, index, passphrase);
|
|
122
|
+
return assertNonEmptyString(address, "native address");
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
throw mapNativeError(error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function buildAndSign(request) {
|
|
129
|
+
const chainId = assertNonEmptyString(request.chainId, "chainId");
|
|
130
|
+
const mnemonic = assertNonEmptyString(request.mnemonic, "mnemonic");
|
|
131
|
+
const txPayload = assertNonEmptyString(request.txPayload, "txPayload");
|
|
132
|
+
const txEncoding = assertEncoding(request.txEncoding);
|
|
133
|
+
validateBuildPayload(txPayload, txEncoding);
|
|
134
|
+
try {
|
|
135
|
+
const signedTxHex = await (0, native_1.getBuboWalletSmokeModule)().buildAndSign(chainId, mnemonic, txPayload, txEncoding);
|
|
136
|
+
return {
|
|
137
|
+
chainId,
|
|
138
|
+
signedTxHex: assertNonEmptyString(signedTxHex, "native signedTxHex"),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
throw mapNativeError(error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
@interface RCT_EXTERN_MODULE(BuboWalletSmoke, NSObject)
|
|
4
4
|
|
|
5
|
+
RCT_EXTERN_METHOD(init:(RCTPromiseResolveBlock)resolve
|
|
6
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
7
|
+
|
|
8
|
+
RCT_EXTERN_METHOD(listSupportedChains:(RCTPromiseResolveBlock)resolve
|
|
9
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
10
|
+
|
|
11
|
+
RCT_EXTERN_METHOD(deriveAddress:(NSString *)chainId
|
|
12
|
+
mnemonic:(NSString *)mnemonic
|
|
13
|
+
account:(nonnull NSNumber *)account
|
|
14
|
+
index:(nonnull NSNumber *)index
|
|
15
|
+
passphrase:(NSString * _Nullable)passphrase
|
|
16
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
17
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
18
|
+
|
|
19
|
+
RCT_EXTERN_METHOD(buildAndSign:(NSString *)chainId
|
|
20
|
+
mnemonic:(NSString *)mnemonic
|
|
21
|
+
txPayload:(NSString *)txPayload
|
|
22
|
+
txEncoding:(NSString *)txEncoding
|
|
23
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
24
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
25
|
+
|
|
5
26
|
RCT_EXTERN_METHOD(runSmoke:(RCTPromiseResolveBlock)resolve
|
|
6
27
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
7
28
|
|
|
@@ -3,11 +3,87 @@ import React
|
|
|
3
3
|
|
|
4
4
|
@objc(BuboWalletSmoke)
|
|
5
5
|
public final class BuboWalletSmoke: NSObject {
|
|
6
|
+
private let wallet = WalletLib()
|
|
7
|
+
|
|
6
8
|
@objc
|
|
7
9
|
public static func requiresMainQueueSetup() -> Bool {
|
|
8
10
|
false
|
|
9
11
|
}
|
|
10
12
|
|
|
13
|
+
@objc(init:rejecter:)
|
|
14
|
+
public func `init`(
|
|
15
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
16
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
17
|
+
) {
|
|
18
|
+
do {
|
|
19
|
+
try wallet.`init`()
|
|
20
|
+
resolve(nil)
|
|
21
|
+
} catch {
|
|
22
|
+
reject("BUBO_WALLET", String(describing: error), error)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc(listSupportedChains:rejecter:)
|
|
27
|
+
public func listSupportedChains(
|
|
28
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
29
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
30
|
+
) {
|
|
31
|
+
do {
|
|
32
|
+
let chains = try wallet.listSupportedChains()
|
|
33
|
+
resolve(chains.map { ["chainId": $0.chainId, "capabilities": $0.capabilities] })
|
|
34
|
+
} catch {
|
|
35
|
+
reject("BUBO_WALLET", String(describing: error), error)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@objc(deriveAddress:mnemonic:account:index:passphrase:resolver:rejecter:)
|
|
40
|
+
public func deriveAddress(
|
|
41
|
+
_ chainId: String,
|
|
42
|
+
mnemonic: String,
|
|
43
|
+
account: NSNumber,
|
|
44
|
+
index: NSNumber,
|
|
45
|
+
passphrase: String?,
|
|
46
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
47
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
48
|
+
) {
|
|
49
|
+
do {
|
|
50
|
+
let accountU32 = try toUInt32(account, field: "account")
|
|
51
|
+
let indexU32 = try toUInt32(index, field: "index")
|
|
52
|
+
let address = try wallet.deriveAddress(
|
|
53
|
+
chainId: chainId,
|
|
54
|
+
mnemonic: mnemonic,
|
|
55
|
+
account: accountU32,
|
|
56
|
+
index: indexU32,
|
|
57
|
+
passphrase: passphrase
|
|
58
|
+
)
|
|
59
|
+
resolve(address)
|
|
60
|
+
} catch {
|
|
61
|
+
reject("BUBO_WALLET", String(describing: error), error)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@objc(buildAndSign:mnemonic:txPayload:txEncoding:resolver:rejecter:)
|
|
66
|
+
public func buildAndSign(
|
|
67
|
+
_ chainId: String,
|
|
68
|
+
mnemonic: String,
|
|
69
|
+
txPayload: String,
|
|
70
|
+
txEncoding: String,
|
|
71
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
72
|
+
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
73
|
+
) {
|
|
74
|
+
do {
|
|
75
|
+
let payload = try decodePayload(txPayload, encoding: txEncoding)
|
|
76
|
+
let signed = try wallet.buildAndSign(
|
|
77
|
+
chainId: chainId,
|
|
78
|
+
mnemonic: mnemonic,
|
|
79
|
+
txPayload: payload
|
|
80
|
+
)
|
|
81
|
+
resolve(hexEncodedString(signed))
|
|
82
|
+
} catch {
|
|
83
|
+
reject("BUBO_WALLET", String(describing: error), error)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
11
87
|
@objc(runSmoke:rejecter:)
|
|
12
88
|
public func runSmoke(
|
|
13
89
|
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
@@ -48,4 +124,71 @@ public final class BuboWalletSmoke: NSObject {
|
|
|
48
124
|
"signedTxHex": result.signedTxHex as Any,
|
|
49
125
|
]
|
|
50
126
|
}
|
|
127
|
+
|
|
128
|
+
private func toUInt32(_ value: NSNumber, field: String) throws -> UInt32 {
|
|
129
|
+
let raw = value.doubleValue
|
|
130
|
+
if !raw.isFinite || raw < 0 || raw > Double(UInt32.max) || floor(raw) != raw {
|
|
131
|
+
throw NSError(
|
|
132
|
+
domain: "BuboWalletSmoke",
|
|
133
|
+
code: 1,
|
|
134
|
+
userInfo: [NSLocalizedDescriptionKey: "\(field) must be uint32"]
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
return UInt32(raw)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private func decodePayload(_ payload: String, encoding: String) throws -> Data {
|
|
141
|
+
switch encoding.lowercased() {
|
|
142
|
+
case "utf8":
|
|
143
|
+
return Data(payload.utf8)
|
|
144
|
+
case "base64":
|
|
145
|
+
guard let data = Data(base64Encoded: payload) else {
|
|
146
|
+
throw NSError(
|
|
147
|
+
domain: "BuboWalletSmoke",
|
|
148
|
+
code: 2,
|
|
149
|
+
userInfo: [NSLocalizedDescriptionKey: "txPayload must be valid base64"]
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
return data
|
|
153
|
+
case "hex":
|
|
154
|
+
return try decodeHex(payload)
|
|
155
|
+
default:
|
|
156
|
+
throw NSError(
|
|
157
|
+
domain: "BuboWalletSmoke",
|
|
158
|
+
code: 3,
|
|
159
|
+
userInfo: [NSLocalizedDescriptionKey: "txEncoding must be utf8|hex|base64"]
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private func decodeHex(_ value: String) throws -> Data {
|
|
165
|
+
let raw = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
166
|
+
if raw.count % 2 != 0 {
|
|
167
|
+
throw NSError(
|
|
168
|
+
domain: "BuboWalletSmoke",
|
|
169
|
+
code: 4,
|
|
170
|
+
userInfo: [NSLocalizedDescriptionKey: "hex payload length must be even"]
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
var data = Data(capacity: raw.count / 2)
|
|
174
|
+
var index = raw.startIndex
|
|
175
|
+
while index < raw.endIndex {
|
|
176
|
+
let next = raw.index(index, offsetBy: 2)
|
|
177
|
+
let byteStr = raw[index..<next]
|
|
178
|
+
guard let byte = UInt8(byteStr, radix: 16) else {
|
|
179
|
+
throw NSError(
|
|
180
|
+
domain: "BuboWalletSmoke",
|
|
181
|
+
code: 5,
|
|
182
|
+
userInfo: [NSLocalizedDescriptionKey: "txPayload must be valid hex"]
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
data.append(byte)
|
|
186
|
+
index = next
|
|
187
|
+
}
|
|
188
|
+
return data
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private func hexEncodedString(_ data: Data) -> String {
|
|
192
|
+
data.map { String(format: "%02x", $0) }.joined()
|
|
193
|
+
}
|
|
51
194
|
}
|
|
@@ -10,18 +10,15 @@
|
|
|
10
10
|
<key>HeadersPath</key>
|
|
11
11
|
<string>Headers</string>
|
|
12
12
|
<key>LibraryIdentifier</key>
|
|
13
|
-
<string>ios-
|
|
13
|
+
<string>ios-arm64</string>
|
|
14
14
|
<key>LibraryPath</key>
|
|
15
15
|
<string>libbubo_wallet_ffi.a</string>
|
|
16
16
|
<key>SupportedArchitectures</key>
|
|
17
17
|
<array>
|
|
18
18
|
<string>arm64</string>
|
|
19
|
-
<string>x86_64</string>
|
|
20
19
|
</array>
|
|
21
20
|
<key>SupportedPlatform</key>
|
|
22
21
|
<string>ios</string>
|
|
23
|
-
<key>SupportedPlatformVariant</key>
|
|
24
|
-
<string>simulator</string>
|
|
25
22
|
</dict>
|
|
26
23
|
<dict>
|
|
27
24
|
<key>BinaryPath</key>
|
|
@@ -29,15 +26,18 @@
|
|
|
29
26
|
<key>HeadersPath</key>
|
|
30
27
|
<string>Headers</string>
|
|
31
28
|
<key>LibraryIdentifier</key>
|
|
32
|
-
<string>ios-
|
|
29
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
33
30
|
<key>LibraryPath</key>
|
|
34
31
|
<string>libbubo_wallet_ffi.a</string>
|
|
35
32
|
<key>SupportedArchitectures</key>
|
|
36
33
|
<array>
|
|
37
34
|
<string>arm64</string>
|
|
35
|
+
<string>x86_64</string>
|
|
38
36
|
</array>
|
|
39
37
|
<key>SupportedPlatform</key>
|
|
40
38
|
<string>ios</string>
|
|
39
|
+
<key>SupportedPlatformVariant</key>
|
|
40
|
+
<string>simulator</string>
|
|
41
41
|
</dict>
|
|
42
42
|
</array>
|
|
43
43
|
<key>CFBundlePackageType</key>
|
|
Binary file
|
|
Binary file
|