Use xo-cash/utils sse. Add vending machine template. Greatly improve startup times.

This commit is contained in:
2026-05-24 19:35:50 +02:00
parent def261b568
commit 85746c3306
11 changed files with 367 additions and 31 deletions
+15 -3
View File
@@ -18,10 +18,13 @@ import { EventEmitter } from "../utils/event-emitter.js";
// TODO: Remove this. Exists to hash the seed for database namespace.
import { createHash } from "crypto";
import { p2pkhTemplate } from "@xo-cash/templates";
import { hexToBin } from "@bitauth/libauth";
import { parseTemplate } from "@xo-cash/engine";
import { p2pkhTemplate } from "@xo-cash/templates";
import { vendingMachineTemplate } from "../templates/vending-machine.js";
import { wrapBCHTemplate } from "../templates/wrap-template.js";
export type AppEventMap = {
"invitation-added": Invitation;
"invitation-removed": Invitation;
@@ -53,6 +56,12 @@ export class AppService extends EventEmitter<AppEventMap> {
public settings: SettingsService;
public invitations: Invitation[] = [];
/**
* Incremented whenever the invitation list or any invitation's data/status changes.
* Used by TUI hooks so useSyncExternalStore snapshots change on in-place mutations.
*/
public invitationsRevision = 0;
private invitationRevisions = new Map<string, number>();
private invitationEventCleanup = new Map<
string,
{
@@ -82,7 +91,9 @@ export class AppService extends EventEmitter<AppEventMap> {
// TODO: We *technically* dont want this here, but we also need some initial templates for the wallet, so im doing it here
// Import the default P2PKH template
await engine.importTemplate(p2pkhTemplate);
await engine.importTemplate(vendingMachineTemplate);
await engine.importTemplate(wrapBCHTemplate);
// Update all the unspents for every template, and subscribe to the locking bytecodes for changes
// TODO: Remove the above lines that do the same thing. Minimising changes for BLISS.
const updateTemplates = async () => {
@@ -160,8 +171,9 @@ export class AppService extends EventEmitter<AppEventMap> {
// Create the invitation
const invitationInstance = await Invitation.create(invitation, deps);
// Add the invitation to the invitations array
// Attach listeners before SSE connects so updates are not missed.
await this.addInvitation(invitationInstance);
await invitationInstance.start();
return invitationInstance;
}
-1
View File
@@ -3,7 +3,6 @@ import { compileCashAssemblyString, type Engine } from "@xo-cash/engine";
import type { ScriptHashData, State, UnspentOutputData } from "@xo-cash/state";
import type {
XOInvitation,
XOInvitationCommit,
XOInvitationInput,
XOInvitationOutput,
XOInvitationVariableValue,
+12 -11
View File
@@ -1,10 +1,9 @@
import type {
AcceptInvitationParameters,
AppendInvitationParameters,
InvitationParameters,
Engine,
GetSpendableResourcesParameters,
} from "@xo-cash/engine";
import { generateTemplateIdentifier, hasInvitationExpired, mergeInvitationCommits } from "@xo-cash/engine";
import { generateTemplateIdentifier, hasInvitationExpired, mergeInvitationCommits, serializeInvitation } from "@xo-cash/engine";
import type {
XOInvitation,
XOInvitationCommit,
@@ -15,10 +14,6 @@ import type {
} from "@xo-cash/types";
import type { UnspentOutputData } from "@xo-cash/state";
import {
bigIntToBinUint64LE,
bigIntToBinUintBE,
bigIntToBinUintLE,
bigIntToVmNumber,
binToHex,
encodeTransaction,
generateTransaction,
@@ -90,13 +85,13 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
}
// engine invitation (I have no idea if this is required)
const engineInvitation = await dependencies.engine.acceptInvitation(invitation);
const engineInvitation = await dependencies.engine.importInvitation(serializeInvitation(invitation));
// Create the invitation
const invitationInstance = new Invitation(engineInvitation, dependencies);
// Start the invitation and its tracking
await invitationInstance.start();
invitationInstance.start();
return invitationInstance;
}
@@ -387,7 +382,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
/**
* Accept the invitation
*/
async accept(acceptParams?: AcceptInvitationParameters): Promise<void> {
async accept(acceptParams?: InvitationParameters): Promise<void> {
// Accept the invitation
this.data = await this.engine.acceptInvitation(this.data, acceptParams);
@@ -438,7 +433,13 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
/**
* Append a commit to the invitation
*/
async append(data: AppendInvitationParameters): Promise<void> {
async append(data: InvitationParameters): Promise<void> {
try {
await this.engine.acceptInvitation(this.data);
} catch (err) {
// Literally do nothing here. We are just trying to accept the invitation in case we haven't already
}
// Append the commit to the invitation
this.data = await this.engine.appendInvitation(this.data.invitationIdentifier, data);
+12 -2
View File
@@ -1,3 +1,4 @@
import { OracleClient } from '@generalprotocols/oracle-client';
import { EventEmitter } from '../utils/event-emitter.js';
import {
type RatesEventMap,
@@ -73,8 +74,17 @@ export class RatesService extends EventEmitter<RatesServiceEventMap> {
settings: SettingsService,
adapter?: RatesAdapter,
): Promise<RatesService> {
const resolvedAdapter = adapter ?? (await RatesOracle.from(undefined, settings));
return new RatesService(resolvedAdapter, settings);
if (adapter) {
return new RatesService(adapter, settings);
}
const oracleClient = new OracleClient();
oracleClient.start();
const ratesOracle = new RatesOracle(oracleClient, settings);
ratesOracle.start();
return new RatesService(ratesOracle, settings);
}
/**