diff --git a/docs/.vitepress/theme/logo-dark.svg b/docs/.vitepress/theme/logo-dark.svg
new file mode 100644
index 0000000..a8e5dd5
--- /dev/null
+++ b/docs/.vitepress/theme/logo-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/.vitepress/theme/logo-light.svg b/docs/.vitepress/theme/logo-light.svg
new file mode 100644
index 0000000..675c98d
--- /dev/null
+++ b/docs/.vitepress/theme/logo-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/frameworks/react.md b/docs/frameworks/react.md
index ac8f2a1..4e5ba24 100644
--- a/docs/frameworks/react.md
+++ b/docs/frameworks/react.md
@@ -1,3 +1,10 @@
# React
-TODO
+## Install
+
+```sh
+npm install @xo/react
+```
+
+## Usage
+
diff --git a/docs/frameworks/svelte.md b/docs/frameworks/svelte.md
index da08a17..2db9b44 100644
--- a/docs/frameworks/svelte.md
+++ b/docs/frameworks/svelte.md
@@ -1,3 +1,10 @@
# Svelte
-TODO
+## Install
+
+```sh
+npm install @xo/svelte
+```
+
+## Usage
+
diff --git a/docs/frameworks/vue.md b/docs/frameworks/vue.md
index f16f9b9..cc632d7 100644
--- a/docs/frameworks/vue.md
+++ b/docs/frameworks/vue.md
@@ -1,3 +1,9 @@
# Vue
-TODO
+## Install
+
+```sh
+npm install @xo/vue
+```
+
+## Usage
diff --git a/examples/node/index.js b/examples/node/index.js
index bbe771e..4a8fc7a 100644
--- a/examples/node/index.js
+++ b/examples/node/index.js
@@ -1,3 +1,4 @@
+import { Transaction } from '@xo/primitives';
import { Blockchain, WalletHD } from '@xo/wallet';
const blockchain = new Blockchain(
@@ -8,24 +9,6 @@ const blockchain = new Blockchain(
await blockchain.start();
-console.log(
- await blockchain.request(
- 'blockchain.address.get_history',
- 'bitcoincash:qqyy3mss5vmthgnu0m5sm39pcfq8z799ku2nxernca',
- 861_606,
- 861_607
- )
-);
-
-/*
-console.log(
- await blockchain.request(
- 'blockchain.transaction.get',
- 'da776b9c273ec406f7ecd488950216aa17a04018201298f263e2fcdb0b903d29'
- )
-);
-*/
-
const wallet = WalletHD.fromMnemonic(
'harsh gospel job ordinary garden clay eight cactus replace general prison permit',
blockchain
@@ -33,4 +16,12 @@ const wallet = WalletHD.fromMnemonic(
await wallet.scan();
+const transactions = wallet.getTransactions();
+
+transactions.forEach((tx) => {
+ blockchain.broadcastTransaction(tx);
+});
+
+console.log(blockchain.pendingTransactions.deriveUtxoState());
+
console.log(wallet.privateKey());
diff --git a/packages/primitives/package.json b/packages/primitives/package.json
index 913a124..e369b4f 100644
--- a/packages/primitives/package.json
+++ b/packages/primitives/package.json
@@ -10,7 +10,7 @@
"dist"
],
"scripts": {
- "build": "tsc --build --clean",
+ "build": "tsc --build --clean && tsc --build",
"prepublishOnly": "npm run build"
},
"author": "",
diff --git a/packages/primitives/src/transaction-graph.ts b/packages/primitives/src/transaction-graph.ts
index 65b867b..6a20b66 100644
--- a/packages/primitives/src/transaction-graph.ts
+++ b/packages/primitives/src/transaction-graph.ts
@@ -22,6 +22,35 @@ export class TransactionGraph {
this.transactions.delete(txHash);
}
+ removeDependentTransactions(txHash: string): string[] {
+ const removedTxids: string[] = [];
+ const toRemove: Set = new Set([txHash]);
+
+ const findDependents = (id: string) => {
+ for (const [otherTxid, tx] of this.transactions) {
+ if (
+ tx
+ .getInputs()
+ .some((input) => binToHex(input.outpointTransactionHash) === id)
+ ) {
+ toRemove.add(otherTxid);
+ findDependents(otherTxid);
+ }
+ }
+ };
+
+ findDependents(txHash);
+
+ for (const id of toRemove) {
+ if (this.transactions.has(id)) {
+ this.transactions.delete(id);
+ removedTxids.push(id);
+ }
+ }
+
+ return removedTxids;
+ }
+
getTransaction(txHash: string): Transaction | undefined {
return this.transactions.get(txHash);
}
@@ -122,4 +151,10 @@ export class TransactionGraph {
return order;
}
+
+ getNextTransactionToBroadcast(): Transaction | undefined {
+ // This is pretty inefficient in the sense we recompile the graph each time.
+ // But I'm not sure if that would really have much real-world performance impact.
+ return this.getBroadcastOrder()[0] ?? undefined;
+ }
}
diff --git a/packages/vue/package.json b/packages/vue/package.json
index 086cd41..ebb1be9 100644
--- a/packages/vue/package.json
+++ b/packages/vue/package.json
@@ -10,7 +10,7 @@
"dist"
],
"scripts": {
- "build": "tsc --build --clean",
+ "build": "tsc --build --clean && tsc --build",
"prepublishOnly": "npm run build"
},
"author": "",
diff --git a/packages/wallet/package.json b/packages/wallet/package.json
index df91c35..7142dad 100644
--- a/packages/wallet/package.json
+++ b/packages/wallet/package.json
@@ -10,7 +10,7 @@
"dist"
],
"scripts": {
- "build": "tsc --build --clean",
+ "build": "tsc --build --clean && tsc --build",
"prepublishOnly": "npm run build"
},
"author": "",
diff --git a/packages/wallet/src/wallet/blockchain.ts b/packages/wallet/src/wallet/blockchain.ts
index 4bebdf7..2f8165a 100644
--- a/packages/wallet/src/wallet/blockchain.ts
+++ b/packages/wallet/src/wallet/blockchain.ts
@@ -88,11 +88,8 @@ export class Blockchain {
transaction: Uint8Array | Transaction | string,
opts: { waitUntilSeen: boolean } = { waitUntilSeen: true }
) {
- if (typeof transaction === 'string') {
- transaction = Transaction.fromHex(transaction);
- }
- if (transaction instanceof Uint8Array) {
- transaction = Transaction.fromBytes(transaction);
+ if (!(transaction instanceof Transaction)) {
+ transaction = Transaction.fromAny(transaction);
}
this.pendingTransactions.addTransaction(transaction);
@@ -107,16 +104,27 @@ export class Blockchain {
transaction: Uint8Array | Transaction | string,
opts: { waitUntilSeen: boolean } = { waitUntilSeen: true }
) {
- if (typeof transaction === 'string') {
- transaction = Transaction.fromHex(transaction);
- }
- if (transaction instanceof Uint8Array) {
- transaction = Transaction.fromBytes(transaction);
+ if (!(transaction instanceof Transaction)) {
+ transaction = Transaction.fromAny(transaction);
}
this.pendingTransactions.addTransaction(transaction);
- // TODO: Broadcast the transaction.
+ // Broadcast any pending transaction.
+ // TODO: Use while loop instead.
+ for (let tx of this.pendingTransactions.getBroadcastOrder()) {
+ try {
+ console.log('broadcasting', tx);
+
+ this.pendingTransactions.removeTransaction(tx.toHashHex());
+ } catch (error) {
+ this.pendingTransactions.removeDependentTransactions(tx.toHashHex());
+
+ throw new Error(
+ `Broadcast failed for ${transaction.getHashHex()}. Removing dependent transactions.`
+ );
+ }
+ }
// TODO: Wait for transaction to be seen on network.
console.log(opts);
diff --git a/packages/wallet/src/wallet/index.ts b/packages/wallet/src/wallet/index.ts
index 6f23f5f..d83459a 100644
--- a/packages/wallet/src/wallet/index.ts
+++ b/packages/wallet/src/wallet/index.ts
@@ -3,3 +3,6 @@ export * from './blockchain-types.js';
export * from './storage.js';
export * from './wallet-hd.js';
export * from './wallet-p2pkh.js';
+export * from './wallet-p2sh20.js';
+export * from './wallet-p2sh32.js';
+export * from './wallet-set.js';
diff --git a/packages/wallet/src/wallet/storage.ts b/packages/wallet/src/wallet/storage.ts
index e18471c..ed3e5a3 100644
--- a/packages/wallet/src/wallet/storage.ts
+++ b/packages/wallet/src/wallet/storage.ts
@@ -6,6 +6,14 @@
* We don't want to extend this - we want to compose it.
* Reason being that we want to support multiple storage adapters.
* And we should allow the possibility of having non-critical storage adapters fail (e.g. remote vs local).
+ *
+ * Additionally, consider how we'll store data for WalletSets.
+ * There will be child wallets. These will have their own data.
+ * And each child may then have its own wallet. And on and on.
+ * So, it's a tree hierarchy.
+ * We can probably emit a "storageUpdated" event that is registered by each parent wallet.
+ * The parent then "wraps" its own state into this and propagates it up to its parent.
+ * Then, when it reaches root, the storage adapter stashes all of the propagated data into actual storage.
*/
export class Storage {
constructor() {
diff --git a/packages/wallet/src/wallet/wallet-hd.ts b/packages/wallet/src/wallet/wallet-hd.ts
index e2c704b..0867295 100644
--- a/packages/wallet/src/wallet/wallet-hd.ts
+++ b/packages/wallet/src/wallet/wallet-hd.ts
@@ -1,5 +1,5 @@
// Import the Stamp class to derive the private keys
-import { HDPrivateNode } from '@xo/primitives';
+import { HDPrivateNode, Transaction } from '@xo/primitives';
import { EventEmitter } from '../utils/index.js';
import { Blockchain } from './blockchain.js';
import {
@@ -232,6 +232,14 @@ export class WalletHD<
return encodedTransaction;
}
+
+ getTransactions() {
+ // NOTE: We use a set so that we don't end up with duplicate transactions.
+ return this.wallets.reduce((txs, wallet) => {
+ wallet.transactions.forEach((tx) => txs.add(tx));
+ return txs; // Make sure to return the accumulator
+ }, new Set());
+ }
}
//-----------------------------------------------------------------------------
diff --git a/packages/wallet/src/wallet/wallet-p2sh20.ts b/packages/wallet/src/wallet/wallet-p2sh20.ts
new file mode 100644
index 0000000..0c9b044
--- /dev/null
+++ b/packages/wallet/src/wallet/wallet-p2sh20.ts
@@ -0,0 +1,11 @@
+/**
+ * Placeholder.
+ *
+ * @remarks
+ * TODO Use AddressData
+ */
+export class WalletP2SH20 {
+ constructor() {
+ console.log('TODO');
+ }
+}
diff --git a/packages/wallet/src/wallet/wallet-p2sh32.ts b/packages/wallet/src/wallet/wallet-p2sh32.ts
new file mode 100644
index 0000000..ab92153
--- /dev/null
+++ b/packages/wallet/src/wallet/wallet-p2sh32.ts
@@ -0,0 +1,11 @@
+/**
+ * Placeholder.
+ *
+ * @remarks
+ * TODO Use AddressData
+ */
+export class WalletP2SH32 {
+ constructor() {
+ console.log('TODO');
+ }
+}
diff --git a/packages/wallet/src/wallet/wallet-set.ts b/packages/wallet/src/wallet/wallet-set.ts
new file mode 100644
index 0000000..bb02f8e
--- /dev/null
+++ b/packages/wallet/src/wallet/wallet-set.ts
@@ -0,0 +1,13 @@
+/**
+ * Placeholder.
+ *
+ * @remarks
+ * One difficult thing here is going to be storage of the subwallets.
+ * We're going to have child wallets and we need to propagate their storage to the root WalletSet.
+ * See remarks on Storage - it's semi-related and I'm still playing with approaches.
+ */
+export class WalletSet {
+ constructor() {
+ console.log('TODO');
+ }
+}
diff --git a/typedoc.json b/typedoc.json
index 8d874c9..1e034ad 100644
--- a/typedoc.json
+++ b/typedoc.json
@@ -14,13 +14,15 @@
"autoConfiguration": true,
"format": "vitepress",
"pretty": true,
- "collapsed": false
+ "collapsed": true
},
"indexFormat": "table",
"parametersFormat": "table",
"interfacePropertiesFormat": "table",
"classPropertiesFormat": "table",
"propertyMembersFormat": "table",
+ "typeDeclarationFormat": "table",
+ "enumMembersFormat": "table",
"useCodeBlocks": true,
"sort": [
"static-first"