Huge commit. Multiple fixes. Refactored commands. Invitations, resources, template inspection, mnemonic stuff, cli utils, pretty printing, remove unreserve on start, fix connectino requirement for invitations, format cashAddress to lockingBytecode on send, lots and lots of other stuff.

This commit is contained in:
2026-04-06 11:56:09 +00:00
parent b475b23beb
commit 55c75501d5
24 changed files with 3284 additions and 77 deletions

View File

@@ -34,6 +34,7 @@ import { compileCashAssemblyString } from "@xo-cash/engine";
export type InvitationEventMap = {
"invitation-updated": XOInvitation;
"invitation-status-changed": string;
"error": Error;
};
export type InvitationDependencies = {
@@ -146,32 +147,38 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
* Start the invitation - Connect sync server and download latest invitation data.
*/
async start(): Promise<void> {
// Connect to the sync server and get the invitation (in parallel)
const [_, invitation] = await Promise.all([
this.syncServer.connect(),
this.syncServer.getInvitation(this.data.invitationIdentifier),
]);
// There is a chance we get SSE messages before the invitation is returned, so we want to combine any commits
const sseCommits = this.data.commits;
// Merge the commits
const combinedCommits = this.mergeCommits(
sseCommits,
invitation?.commits ?? [],
);
// Set the invitation data with the combined commits
this.data = { ...this.data, ...invitation, commits: combinedCommits };
// Store the invitation in the storage
await this.storage.set(this.data.invitationIdentifier, this.data);
// Publish the invitation to the sync server
this.syncServer.publishInvitation(this.data);
// Compute and emit initial status
await this.updateStatus();
try {
// Connect to the sync server and get the invitation (in parallel)
const [_, invitation] = await Promise.all([
this.syncServer.connect(),
this.syncServer.getInvitation(this.data.invitationIdentifier),
]);
// There is a chance we get SSE messages before the invitation is returned, so we want to combine any commits
const sseCommits = this.data.commits;
// Merge the commits
const combinedCommits = this.mergeCommits(
sseCommits,
invitation?.commits ?? [],
);
// Set the invitation data with the combined commits
this.data = { ...this.data, ...invitation, commits: combinedCommits };
// Store the invitation in the storage
await this.storage.set(this.data.invitationIdentifier, this.data);
// Publish the invitation to the sync server
this.publishInvitation(this.data);
// Compute and emit initial status
await this.updateStatus();
} catch (err) {
// console.error(`Error starting invitation, could not connect to sync server or get invitation`, err);
// Emit the error event. We might want to throw? but we need a better way of handling errors in the invitation system because we need the invitation to successfully initialize.
this.emit("error", err instanceof Error ? err : new Error(String(err)));
}
}
/**
@@ -205,6 +212,18 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
}
}
/**
* Publish the invitation to the sync server
*/
private async publishInvitation(invitation: XOInvitation = this.data): Promise<void> {
try {
await this.syncServer.publishInvitation(invitation);
} catch (err) {
// Emit the error event. We might want to throw? but we need a better way of handling errors in the invitation system because we need the invitation to successfully initialize.
this.emit("error", err instanceof Error ? err : new Error(String(err)));
}
}
/**
* Merge the commits
* @param initial - The initial commits
@@ -359,7 +378,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
this.data = await this.engine.acceptInvitation(this.data, acceptParams);
// Sync the invitation to the sync server
this.syncServer.publishInvitation(this.data);
this.publishInvitation(this.data);
// Update the status of the invitation
await this.updateStatus();
@@ -373,7 +392,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
const signedInvitation = await this.engine.signInvitation(this.data);
// Publish the signed invitation to the sync server
this.syncServer.publishInvitation(signedInvitation);
this.publishInvitation(signedInvitation);
// Store the signed invitation in the storage
await this.storage.set(this.data.invitationIdentifier, signedInvitation);
@@ -385,16 +404,17 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
}
/**
* Broadcast the invitation
* Broadcast the invitation.
* @returns The transaction hash returned by the network after broadcast.
*/
async broadcast(): Promise<void> {
// Broadcast the transaction (executeAction returns transaction hash when broadcastTransaction: true)
await this.engine.executeAction(this.data, {
async broadcast(): Promise<string> {
const txHash = await this.engine.executeAction(this.data, {
broadcastTransaction: true,
});
// Update the status of the invitation
await this.updateStatus();
return String(txHash);
}
// ============================================================================
@@ -409,7 +429,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
this.data = await this.engine.appendInvitation(this.data, data);
// Sync the invitation to the sync server
await this.syncServer.publishInvitation(this.data);
await this.publishInvitation(this.data);
// Store the invitation in the storage
await this.storage.set(this.data.invitationIdentifier, this.data);
@@ -426,7 +446,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
await this.append({ inputs });
// Sync the invitation to the sync server
await this.syncServer.publishInvitation(this.data);
await this.publishInvitation(this.data);
}
/**
@@ -449,7 +469,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
await this.append({ outputs });
// Sync the invitation to the sync server
await this.syncServer.publishInvitation(this.data);
await this.publishInvitation(this.data);
}
async addVariables(variables: XOInvitationVariable[]): Promise<void> {
@@ -457,7 +477,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
await this.append({ variables });
// Sync the invitation to the sync server
await this.syncServer.publishInvitation(this.data);
await this.publishInvitation(this.data);
}
async findSuitableResources(