@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 CHANGED
@@ -1,49 +1,174 @@
1
1
  # @bubolabs/wallet-rn-sdk
2
2
 
3
- Minimal React Native smoke SDK for the UniFFI wallet library.
3
+ React Native SDK for the Bubo wallet UniFFI bridge.
4
4
 
5
- This package is a standard React Native native module layout:
5
+ This package uses standard RN native-module layout and supports autolinking:
6
6
 
7
- - `android/` React Native Android library module (autolinked)
8
- - `ios/` Pod sources + vendored `BuboWalletFFI.xcframework` (autolinked)
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
- ## API
11
+ ## Install
12
12
 
13
- - `runSmoke()`
14
- - `runBtcSmoke()`
13
+ ```bash
14
+ npm install @bubolabs/wallet-rn-sdk
15
+ ```
16
+
17
+ iOS:
15
18
 
16
- Both methods call native module `BuboWalletSmoke` and return:
19
+ ```bash
20
+ cd ios && pod install
21
+ ```
22
+
23
+ ## Core API
17
24
 
18
25
  ```ts
19
- type WalletSmokeResult = {
20
- supportedChains: Array<{ chainId: string; capabilities: string[] }>;
21
- deriveChainId: string;
22
- derivedAddress: string;
23
- signChainId: string | null;
24
- signedTxHex: string | null;
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
- ## Example
68
+ ### Example
29
69
 
30
70
  ```ts
31
- import { runSmoke } from "@bubolabs/wallet-rn-sdk";
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
- const result = await runSmoke();
34
- console.log(result.deriveChainId, result.derivedAddress);
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
- ## Install
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
- ```bash
40
- npm install @bubolabs/wallet-rn-sdk
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
- iOS:
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
- cd ios && pod install
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
  }
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
@@ -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
  };
@@ -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 WalletSmokeErrorCode = "MODULE_NOT_LINKED" | "NATIVE_ERROR" | "INVALID_RESULT" | "BTC_SIGN_UNAVAILABLE";
13
- export declare class WalletSmokeError extends Error {
14
- readonly code: WalletSmokeErrorCode;
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
@@ -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,oBAAoB,GAC5B,mBAAmB,GACnB,cAAc,GACd,gBAAgB,GAChB,sBAAsB,CAAC;AAE3B,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EAAE,oBAAoB,CAAC;IAC3C,SAAgB,UAAU,CAAC,EAAE,OAAO,CAAC;gBAGnC,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,OAAO;CAOvB"}
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 WalletSmokeError extends Error {
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 = "WalletSmokeError";
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;
@@ -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-arm64_x86_64-simulator</string>
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-arm64</string>
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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubolabs/wallet-rn-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "React Native smoke SDK for bubo wallet UniFFI bridge",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",