Merge branch '88-wallets-emit-statusupdated-if-payload-is-not-specialized' into 'development'

Resolve "Wallets: Emit statusUpdated if payload is not specialized"

Closes #88

See merge request GeneralProtocols/xo/stack!126
This commit is contained in:
Emergent Reasons
2025-09-02 07:09:41 +00:00
2 changed files with 19 additions and 33 deletions
@@ -6,8 +6,7 @@ import {
type BlockchainUTXO, type BlockchainUTXO,
type BlockchainUTXOs, type BlockchainUTXOs,
type BlockchainChainTip, type BlockchainChainTip,
AddressStatusPayload, type BlockHeightPayload,
BlockHeightPayload,
} from './types.js'; } from './types.js';
import { import {
@@ -29,10 +28,7 @@ import {
/** /**
* @remarks This provider DOES NOT enforce the 100 confirmations Coinbase Transaction rule. * @remarks This provider DOES NOT enforce the 100 confirmations Coinbase Transaction rule.
**/ **/
export class BlockchainInMemory extends BaseBlockchain< export class BlockchainInMemory extends BaseBlockchain<BlockHeightPayload> {
AddressStatusPayload,
BlockHeightPayload
> {
static async from<T extends typeof BlockchainInMemory>(this: T) { static async from<T extends typeof BlockchainInMemory>(this: T) {
const blockchain = new this(); const blockchain = new this();
await blockchain.start(); await blockchain.start();
@@ -41,10 +37,7 @@ export class BlockchainInMemory extends BaseBlockchain<
private blockHeaders: Array<BlockHeader>; private blockHeaders: Array<BlockHeader>;
private transactions: BlockchainTransactions; private transactions: BlockchainTransactions;
private addressSubscriptions: Map< private addressSubscriptions: Map<string, Set<BlockchainCallback>>;
string,
Set<BlockchainCallback<AddressStatusPayload>>
>;
private transactionSubscriptions: Map<string, Set<BlockchainCallback>>; private transactionSubscriptions: Map<string, Set<BlockchainCallback>>;
constructor() { constructor() {
@@ -177,7 +170,7 @@ export class BlockchainInMemory extends BaseBlockchain<
async subscribeLockscript( async subscribeLockscript(
lockscript: string | Uint8Array, lockscript: string | Uint8Array,
callback: BlockchainCallback<AddressStatusPayload>, callback: BlockchainCallback,
) { ) {
// Fulcrum does not support subscribing to lockscripts, so we cast it to an address. // Fulcrum does not support subscribing to lockscripts, so we cast it to an address.
const address = Address.fromLockscript(lockscript); const address = Address.fromLockscript(lockscript);
@@ -191,7 +184,7 @@ export class BlockchainInMemory extends BaseBlockchain<
async unsubscribeLockscript( async unsubscribeLockscript(
lockscript: string | Uint8Array, lockscript: string | Uint8Array,
callback: BlockchainCallback<AddressStatusPayload>, callback: BlockchainCallback,
): Promise<boolean> { ): Promise<boolean> {
// Fulcrum does not support subscribing to lockscripts, so we cast it to an address. // Fulcrum does not support subscribing to lockscripts, so we cast it to an address.
const address = Address.fromLockscript(lockscript); const address = Address.fromLockscript(lockscript);
@@ -203,10 +196,7 @@ export class BlockchainInMemory extends BaseBlockchain<
return await this.unsubscribeAddress(address.toCashAddr(), callback); return await this.unsubscribeAddress(address.toCashAddr(), callback);
} }
async subscribeAddress( async subscribeAddress(address: string, callback: BlockchainCallback) {
address: string,
callback: BlockchainCallback<AddressStatusPayload>,
) {
// Convert the address to it's lockscript hex so that it's consistently represented. // Convert the address to it's lockscript hex so that it's consistently represented.
const lockscriptHex = Address.from(address).toLockscriptHex(); const lockscriptHex = Address.from(address).toLockscriptHex();
@@ -222,10 +212,7 @@ export class BlockchainInMemory extends BaseBlockchain<
return async () => await this.unsubscribeAddress(address, callback); return async () => await this.unsubscribeAddress(address, callback);
} }
async unsubscribeAddress( async unsubscribeAddress(address: string, callback: BlockchainCallback) {
address: string,
callback: BlockchainCallback<AddressStatusPayload>,
) {
// Convert the address to it's lockscript hex so that it's consistently represented. // Convert the address to it's lockscript hex so that it's consistently represented.
const lockscriptHex = Address.from(address).toLockscriptHex(); const lockscriptHex = Address.from(address).toLockscriptHex();
@@ -282,12 +269,6 @@ export class BlockchainInMemory extends BaseBlockchain<
} }
private notifyAddressSubscribers(tx: Transaction) { private notifyAddressSubscribers(tx: Transaction) {
const payload = {
type: 'address-status',
// 32 random characters. Since we only need this to be unique per transaction, we can just randomise the first 16 bytes and pad the rest with 0s.
status: Math.random().toString(36).substring(2).padEnd(32, '0'),
} as const;
// First, notify all source output listeners. // First, notify all source output listeners.
tx.getInputs().forEach((input) => { tx.getInputs().forEach((input) => {
// Skip coinbase-like inputs // Skip coinbase-like inputs
@@ -307,7 +288,7 @@ export class BlockchainInMemory extends BaseBlockchain<
const callbacks = this.addressSubscriptions.get(address); const callbacks = this.addressSubscriptions.get(address);
if (callbacks) { if (callbacks) {
callbacks.forEach((callback) => callback(address, payload)); callbacks.forEach((callback) => callback(address));
} }
}); });
@@ -320,7 +301,7 @@ export class BlockchainInMemory extends BaseBlockchain<
const callbacks = this.addressSubscriptions.get(address); const callbacks = this.addressSubscriptions.get(address);
if (callbacks) { if (callbacks) {
callbacks.forEach((callback) => callback(address, payload)); callbacks.forEach((callback) => callback(address));
} }
}); });
} }
+10 -5
View File
@@ -380,14 +380,19 @@ export class WalletP2PKH extends BaseWallet {
_address: string, _address: string,
payload?: AddressStatusPayload, payload?: AddressStatusPayload,
) { ) {
// If wallet state has changed... // Not all blockchain adapters will provide a status payload. But if they do, we check if it's the same as our current status.
if (payload && payload.status !== this.status) { // If it is, we don't want the wallet to try to update its state as it will be the same and is a waste of resources.
if (payload && payload.status === this.status) {
return;
}
if (payload) {
// Set our new status. // Set our new status.
this.status = payload.status; this.status = payload.status;
// Emit an event.
this.emit('stateUpdated', this.status);
} }
// Emit an event.
this.emit('stateUpdated', this.status);
} }
} }