Skip to content

Commit 584b4bd

Browse files
committed
Merge branch 'wasm_wollet_builder'
2 parents 87bf2e8 + 973260c commit 584b4bd

5 files changed

Lines changed: 154 additions & 13 deletions

File tree

lwk_wasm/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod update;
5252
mod websocket;
5353

5454
mod wollet;
55+
mod wollet_builder;
5556
#[cfg(feature = "simplicity")]
5657
mod xonly_public_key;
5758
mod xpub;
@@ -133,6 +134,7 @@ pub use update::Update;
133134
pub use websocket::WebSocketSerial;
134135

135136
pub use wollet::Wollet;
137+
pub use wollet_builder::WolletBuilder;
136138
#[cfg(feature = "simplicity")]
137139
pub use xonly_public_key::XOnlyPublicKey;
138140
pub use xpub::Xpub;

lwk_wasm/src/wollet.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ pub struct Wollet {
1414
inner: lwk_wollet::Wollet,
1515
}
1616

17+
impl From<lwk_wollet::Wollet> for Wollet {
18+
fn from(inner: lwk_wollet::Wollet) -> Self {
19+
Self { inner }
20+
}
21+
}
22+
1723
impl AsRef<lwk_wollet::Wollet> for Wollet {
1824
fn as_ref(&self) -> &lwk_wollet::Wollet {
1925
&self.inner
@@ -35,18 +41,6 @@ impl Wollet {
3541
Ok(Self { inner })
3642
}
3743

38-
/// Experimental: create a "utxo only" `Wollet`
39-
#[wasm_bindgen(js_name = "newUtxoOnly")]
40-
pub fn new_utxo_only(
41-
network: &Network,
42-
descriptor: &WolletDescriptor,
43-
) -> Result<Wollet, Error> {
44-
let inner = lwk_wollet::WolletBuilder::new(network.into(), descriptor.into())
45-
.utxo_only(true)
46-
.build()?;
47-
Ok(Self { inner })
48-
}
49-
5044
/// Get a wallet address with the correspondong derivation index
5145
///
5246
/// If Some return the address at the given index,

lwk_wasm/src/wollet_builder.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use std::sync::Arc;
2+
3+
use wasm_bindgen::prelude::*;
4+
5+
use crate::{Error, JsStorage, JsStoreLink, Network, Wollet, WolletDescriptor};
6+
7+
/// A builder for constructing [`Wollet`] instances.
8+
#[wasm_bindgen]
9+
pub struct WolletBuilder {
10+
inner: lwk_wollet::WolletBuilder,
11+
}
12+
13+
impl From<lwk_wollet::WolletBuilder> for WolletBuilder {
14+
fn from(value: lwk_wollet::WolletBuilder) -> Self {
15+
Self { inner: value }
16+
}
17+
}
18+
19+
#[wasm_bindgen]
20+
impl WolletBuilder {
21+
/// Create a builder for a watch-only wallet.
22+
#[wasm_bindgen(constructor)]
23+
pub fn new(network: &Network, descriptor: &WolletDescriptor) -> Self {
24+
lwk_wollet::WolletBuilder::new(network.into(), descriptor.into()).into()
25+
}
26+
27+
/// Set the threshold used to merge persisted updates during build.
28+
///
29+
/// `None` disables merging (default behavior).
30+
#[wasm_bindgen(js_name = withMergeThreshold)]
31+
pub fn with_merge_threshold(self, merge_threshold: Option<u32>) -> Self {
32+
self.inner
33+
.with_merge_threshold(merge_threshold.map(|t| t as usize))
34+
.into()
35+
}
36+
37+
/// Experimental: set the wallet as "utxo only".
38+
#[wasm_bindgen(js_name = utxoOnly)]
39+
pub fn utxo_only(self, utxo_only: bool) -> Self {
40+
self.inner.utxo_only(utxo_only).into()
41+
}
42+
43+
/// Persist wallet updates in the given JavaScript storage object.
44+
///
45+
/// The JS object must have `get(key)`, `put(key, value)`, and `remove(key)` methods.
46+
#[wasm_bindgen(js_name = withStore)]
47+
pub fn with_store(self, storage: JsStorage) -> Self {
48+
self.inner
49+
.with_store(Arc::new(JsStoreLink::new(storage)))
50+
.into()
51+
}
52+
53+
/// Build the wallet from this builder.
54+
pub fn build(self) -> Result<Wollet, Error> {
55+
Ok(self.inner.build()?.into())
56+
}
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use super::*;
62+
63+
#[test]
64+
fn test_builder() {
65+
let mnemonic = crate::Mnemonic::new(
66+
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
67+
)
68+
.unwrap();
69+
let network = Network::regtest_default();
70+
let signer = crate::Signer::new(&mnemonic, &network).unwrap();
71+
let descriptor = signer.wpkh_slip77_descriptor().unwrap();
72+
73+
let wollet = WolletBuilder::new(&network, &descriptor)
74+
.with_merge_threshold(Some(2))
75+
.utxo_only(true)
76+
.build()
77+
.unwrap();
78+
79+
assert_eq!(wollet.address(Some(0)).unwrap().index(), 0);
80+
}
81+
}

lwk_wasm/tests/node/list_transactions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async function runListTransactionsTest() {
3939

4040
// Fetch transactions using waterfalls and utxos only
4141
const client_utxo_only = new lwk.EsploraClient(network, "https://waterfalls.liquidwebwallet.org/liquidtestnet/api", true, 4, true);
42-
const wollet_utxo_only = lwk.Wollet.newUtxoOnly(network, desc);
42+
const wollet_utxo_only = new lwk.WolletBuilder(network, desc).utxoOnly(true).build();
4343
console.log("Starting UTXO-only full scan...");
4444
const update_utxo_only = await client_utxo_only.fullScan(wollet_utxo_only);
4545
if (update_utxo_only) {

lwk_wasm/tests/node/store.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const assert = require('assert');
2+
const fs = require('fs');
3+
const lwk = require('lwk_node');
4+
5+
function createStorage() {
6+
const store = new Map();
7+
return {
8+
get(key) {
9+
return store.get(key) || null;
10+
},
11+
put(key, value) {
12+
const valueCopy = value ? new Uint8Array(value) : null;
13+
store.set(key, valueCopy);
14+
},
15+
remove(key) {
16+
store.delete(key);
17+
},
18+
_data: store
19+
};
20+
}
21+
22+
function runWolletBuilderStoreTest() {
23+
const descriptorString = fs
24+
.readFileSync(`${__dirname}/../../test_data/update_with_mnemonic/descriptor.txt`, 'utf8')
25+
.trim();
26+
const encryptedUpdate = fs
27+
.readFileSync(
28+
`${__dirname}/../../test_data/update_with_mnemonic/update_serialized_encrypted.txt`,
29+
'utf8'
30+
)
31+
.trim();
32+
33+
const network = lwk.Network.testnet();
34+
const descriptor = new lwk.WolletDescriptor(descriptorString);
35+
const update = lwk.Update.deserializeDecryptedBase64(encryptedUpdate, descriptor);
36+
const jsStorage = createStorage();
37+
38+
const wollet = new lwk.WolletBuilder(network, descriptor)
39+
.withStore(jsStorage)
40+
.build();
41+
wollet.applyUpdate(update);
42+
43+
assert(jsStorage._data.has('000000000000'));
44+
45+
const expectedAddress = wollet.address(0).address().toString();
46+
const expectedTransactions = wollet.transactions().length;
47+
const expectedBalance = JSON.stringify(wollet.balance().toJSON());
48+
49+
const restored = new lwk.WolletBuilder(network, descriptor)
50+
.withStore(jsStorage)
51+
.build();
52+
53+
assert.strictEqual(restored.address(0).address().toString(), expectedAddress);
54+
assert.strictEqual(1, expectedTransactions);
55+
assert.strictEqual(restored.transactions().length, expectedTransactions);
56+
assert.strictEqual(JSON.stringify(restored.balance().toJSON()), expectedBalance);
57+
}
58+
59+
if (require.main === module) {
60+
runWolletBuilderStoreTest();
61+
console.log("wollet_builder.js: all tests passed");
62+
}
63+
64+
module.exports = { runWolletBuilderStoreTest };

0 commit comments

Comments
 (0)