Merge branch '31-feat-docs-add-blockchainelectrum-tool' into 'development'

Resolve "feat(Docs): Add BlockchainElectrum Tool"

Closes #31

See merge request GeneralProtocols/xo/stack!53
This commit is contained in:
Jim Hamill
2025-04-07 06:40:02 +00:00
5 changed files with 132 additions and 25 deletions
+1 -14
View File
@@ -207,29 +207,16 @@ export default withMermaid({
text: 'Stack',
collapsed: false,
items: [
/*
{
text: 'Blockchain',
link: '/tools/stack/blockchain',
items: [
{
text: 'Electrum',
link: '/tools/stack/blockchain#encryption',
link: '/tools/stack/blockchain#electrum',
},
],
},
{
text: 'Utils',
link: '/tools/stack/utils',
items: [
{
text: 'Encrypter',
link: '/tools/stack/utils#encrypter',
},
{ text: 'Hasher', link: '/tools/stack/utils#hasher' },
],
},
*/
],
},
],
@@ -75,7 +75,7 @@
</div>
</div>
<div>
<button type="submit" class="gradient-button" @click="() => executeMethod(state.selectedMethod.callback(state.primitive, state.selectedMethod.models))">Execute Method</button>
<button type="submit" class="gradient-button" @click="() => executeMethod(state.selectedMethod?.callback(state.primitive, state.selectedMethod.models))">Execute Method</button>
</div>
<!-- Result -->
<div>
@@ -135,7 +135,7 @@ export interface Method<Primitive> {
error?: string;
// Function that takes the models and returns the primitive
callback: (primitive: Primitive, value: Array<any>) => string;
callback: (primitive: Primitive, value: Array<any>) => string | Promise<string>;
// Whether the input is extended onto a new line
extendedInput?: boolean
@@ -185,13 +185,13 @@ const state: Ref<{
primitive: undefined,
})
async function setPrimitive(functionToExecute: () => Primitive | Promise<Primitive>): void {
async function setPrimitive(functionToExecute: () => Primitive | Promise<Primitive>): Promise<void> {
try {
state.value.primitive = await functionToExecute();
state.value.error = undefined;
state.value.toFunctions.forEach((wrappedMethod, i) => {
state.value.toFunctions[i].result = wrappedMethod.callback(state.value.primitive)
state.value.toFunctions.forEach(async (wrappedMethod, i) => {
state.value.toFunctions[i].result = await wrappedMethod.callback(state.value.primitive)
});
state.value.methods.forEach((method, i) => {
@@ -218,7 +218,7 @@ function getPrimitive(functionToExecute: () => string): string {
}
}
async function executeMethod(wrappedMethod: () => string | Promise<string>): string {
async function executeMethod(wrappedMethod: () => string | Promise<string>): Promise<void | string> {
try {
if(!state.value.primitive) {
return '';
@@ -0,0 +1,60 @@
import { BlockchainElectrum, encodeExtendedJson, Transaction } from '@xocash/stack';
export const electrumClient = {
from: [
{
label: 'From Servers',
inputs: ['Servers'],
models: ['bch.imaginary.cash, cashnode.bch.ninja, electrum.imaginary.cash'],
callback: async (args: [string]) => {
const electrum = await BlockchainElectrum.from({
opts: {
servers: args[0].replaceAll(' ', '').split(','),
application: 'XO-tools',
},
});
return electrum;
},
},
],
to: [],
methods: [
{
label: 'Fetch Unspents',
inputs: ['Address'],
models: ['Address'],
callback: async (electrum: BlockchainElectrum, args: [string]) => {
const unspents = await electrum.fetchUnspents(args[0]);
return encodeExtendedJson(unspents.toArray(), 2);
},
},
{
label: 'Fetch Transaction',
inputs: ['Transaction Hash'],
models: ['Transaction Hash'],
callback: async (electrum: BlockchainElectrum, args: [string]) => {
const transaction = await electrum.fetchTransaction(args[0]);
return encodeExtendedJson(transaction, 2);
},
},
{
label: 'Fetch Transactions',
inputs: ['Address'],
models: ['Address'],
callback: async (electrum: BlockchainElectrum, args: [string]) => {
const transactions = await electrum.fetchTransactions(args[0]);
return encodeExtendedJson(transactions.toArray(), 2);
},
},
{
label: 'Broadcast Transaction',
inputs: ['Transaction'],
models: ['Transaction'],
callback: async (electrum: BlockchainElectrum, args: [string]) => {
const tx = Transaction.from(args[0]);
return await electrum.broadcastTransaction(tx);
},
},
],
};
@@ -0,0 +1,30 @@
---
aside: false
---
<script lang="ts" setup>
import PrimitiveToolVue, { type FromFunction, type ToFunction } from '@theme/components/PrimitiveTool.vue'
// Import our tool functions
import {
electrumClient,
} from './blockchain-functions.ts'
</script>
<!-- Resume Markdown parsing -->
# Blockchain
## Electrum Client
<div class="tool">
<p>A connection to Electrum servers.</p>
<p>Mutliple servers can be provided, and the client will attempt to fallback to the next server in case of failure.</p>
<p>Servers can be provided as a comma separated list.</p>
<PrimitiveToolVue
:fromFunctions="electrumClient.from"
:toFunctions="electrumClient.to"
:methods="electrumClient.methods"
/>
</div>
@@ -48,6 +48,11 @@ export type BlockchainElectrumOpts = {
application: string;
};
export type BlockchainElectrumParams = {
store?: BaseStore;
opts?: Partial<BlockchainElectrumOpts>;
};
export class BlockchainElectrum extends BaseBlockchain {
// Composables.
electrumClient!: ElectrumClient<ElectrumProtocolEvents>;
@@ -60,18 +65,43 @@ export class BlockchainElectrum extends BaseBlockchain {
addressSubscriptions: Map<string, Set<AddressCallback>> = new Map();
transactionSubscriptions: Map<string, Set<TransactionCallback>> = new Map();
// Options
store: BaseStore;
opts: BlockchainElectrumOpts;
constructor(
public readonly store: BaseStore = new StoreInMemory(),
public readonly opts: BlockchainElectrumOpts = {
public readonly params: BlockchainElectrumParams = {},
) {
super();
// Set the store, or use the memory store by default.
this.store = params.store ?? new StoreInMemory();
// Combine provided options with default options.
this.opts = {
servers: [
'bch.imaginary.cash',
'cashnode.bch.ninja',
'electrum.imaginary.cash',
],
application: 'XOApp',
},
) {
super();
...params.opts,
};
}
/**
* Creates a new BlockchainElectrum instance.
* @param {BlockchainElectrumParams} params - The parameters for the BlockchainElectrum instance.
* @returns {BlockchainElectrum} A new BlockchainElectrum instance.
*/
static async from(
params: BlockchainElectrumParams = {},
): Promise<BlockchainElectrum> {
const blockchain = new BlockchainElectrum(params);
await blockchain.start();
return blockchain;
}
async start() {