Update to latest engine. Add sync-v2. Various fixes.
This commit is contained in:
+82
-21
@@ -1,16 +1,12 @@
|
||||
import type {
|
||||
InvitationParameters,
|
||||
Engine,
|
||||
GetSpendableResourcesParameters,
|
||||
InvitationParameters,
|
||||
} from "@xo-cash/engine";
|
||||
import {
|
||||
generateTemplateIdentifier,
|
||||
hasInvitationExpired,
|
||||
mergeInvitationCommits,
|
||||
resolveCommitReferences,
|
||||
serializeInvitation,
|
||||
deserializeInvitation,
|
||||
type ResolvedInvitationData,
|
||||
} from "@xo-cash/engine";
|
||||
import type {
|
||||
XOInvitation,
|
||||
@@ -20,6 +16,7 @@ import type {
|
||||
XOInvitationVariable,
|
||||
XOInvitationVariableValue,
|
||||
XOTemplate,
|
||||
XOTemplateActionIntent,
|
||||
} from "@xo-cash/types";
|
||||
import type { UnspentOutputData } from "@xo-cash/state";
|
||||
import {
|
||||
@@ -39,12 +36,13 @@ import { EventEmitter } from "../utils/event-emitter.js";
|
||||
import { decodeExtendedJsonObject } from "../utils/ext-json.js";
|
||||
import { compileCashAssemblyString } from "@xo-cash/engine";
|
||||
|
||||
export type { ResolvedInvitationData } from "@xo-cash/engine";
|
||||
import type { ResolvedInvitationData } from "../utils/resolve-invitation-data.js";
|
||||
import { resolveCommitReferences } from "../utils/resolve-invitation-data.js";
|
||||
|
||||
export type InvitationEventMap = {
|
||||
"invitation-updated": XOInvitation;
|
||||
"invitation-status-changed": string;
|
||||
"invitation-removed": void;
|
||||
"invitation-removed": string;
|
||||
error: Error;
|
||||
};
|
||||
|
||||
@@ -55,6 +53,11 @@ export type InvitationDependencies = {
|
||||
electrum: BlockchainService;
|
||||
};
|
||||
|
||||
export type FeeAwareChangeResult = {
|
||||
changeAmountSatoshis: bigint;
|
||||
feeSatoshis: bigint;
|
||||
};
|
||||
|
||||
function stripLocalInvitationMetadata(invitation: XOInvitation): XOInvitation {
|
||||
const { entityIdentifier: _entityIdentifier, ...sharedInvitation } =
|
||||
invitation as XOInvitation & { entityIdentifier?: string };
|
||||
@@ -103,10 +106,15 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
throw new Error(`Template not found: ${invitation.templateIdentifier}`);
|
||||
}
|
||||
|
||||
// engine invitation (I have no idea if this is required)
|
||||
const engineInvitation = await dependencies.engine.importInvitation(
|
||||
serializeInvitation(invitation),
|
||||
);
|
||||
const ensureEngineInvitation = async (invitation: XOInvitation) => {
|
||||
try {
|
||||
return await dependencies.engine.getInvitationOrThrow(invitation.invitationIdentifier);
|
||||
} catch {
|
||||
return await dependencies.engine.acceptInvitation(invitation);
|
||||
}
|
||||
}
|
||||
|
||||
const engineInvitation = await ensureEngineInvitation(invitation);
|
||||
|
||||
// Create the invitation
|
||||
const invitationInstance = new Invitation(
|
||||
@@ -240,14 +248,17 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
// Prefer keeping the engine's local invitation state in sync.
|
||||
this.updateInvitationData(
|
||||
stripLocalInvitationMetadata(
|
||||
await this.engine.updateInvitation({
|
||||
...this.data,
|
||||
...invitation,
|
||||
commits: combinedCommits,
|
||||
}),
|
||||
await this.engine.updateInvitation(
|
||||
{
|
||||
...this.data,
|
||||
...invitation,
|
||||
commits: combinedCommits,
|
||||
}
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.emit(
|
||||
"error",
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
@@ -273,8 +284,8 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
*
|
||||
* TODO: Invitation should sync up the initial data (top level) then everything after that should be the commits. This makes it easier to merge as we go instead of just having to overwrite the entire invitation.
|
||||
*/
|
||||
private async handleSSEMessage(event: SSEvent): Promise<void> {
|
||||
const invitation = this.parseInvitationFromSSEMessage(event);
|
||||
private async handleSSEMessage(event: { event: "invitation-updated"; data: XOInvitation }): Promise<void> {
|
||||
const invitation = event.data;
|
||||
if (
|
||||
!invitation ||
|
||||
invitation.invitationIdentifier !== this.data.invitationIdentifier
|
||||
@@ -296,6 +307,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.emit(
|
||||
"error",
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
@@ -320,7 +332,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
|
||||
const decoded = decodeExtendedJsonObject(payload) as XOInvitation;
|
||||
return stripLocalInvitationMetadata(
|
||||
deserializeInvitation(serializeInvitation(decoded)),
|
||||
decoded,
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
@@ -587,6 +599,10 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
* @returns The transaction hash returned by the network after broadcast.
|
||||
*/
|
||||
async broadcast(): Promise<string> {
|
||||
const engineInvitation = await this.engine.getInvitationOrThrow(this.data.invitationIdentifier);
|
||||
console.log(engineInvitation);
|
||||
|
||||
|
||||
const txHash = await this.engine.executeAction(
|
||||
this.data.invitationIdentifier,
|
||||
{
|
||||
@@ -658,6 +674,51 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
await this.publishInvitation(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the engine to calculate and append fee-aware change for the current
|
||||
* merged invitation. If no spendable change can be created, the remaining
|
||||
* surplus is left as the miner fee.
|
||||
*/
|
||||
async addFeeAwareChange(): Promise<FeeAwareChangeResult> {
|
||||
const availability = await this.engine.getChangeAvailability(
|
||||
this.data.invitationIdentifier,
|
||||
);
|
||||
const availableFeeSatoshis =
|
||||
availability.inputTotalSatoshis - availability.outputTotalSatoshis;
|
||||
|
||||
if (availableFeeSatoshis < availability.estimatedMinFeeSatoshis) {
|
||||
throw new Error(
|
||||
`Insufficient funds for miner fee. Available ${availableFeeSatoshis} satoshis, estimated minimum ${availability.estimatedMinFeeSatoshis} satoshis.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!availability.canAddChange) {
|
||||
return {
|
||||
changeAmountSatoshis: 0n,
|
||||
feeSatoshis: availableFeeSatoshis,
|
||||
};
|
||||
}
|
||||
|
||||
this.updateInvitationData(
|
||||
await this.engine.addChangeToInvitation(this.data.invitationIdentifier),
|
||||
);
|
||||
|
||||
await this.publishInvitation(this.data);
|
||||
await this.storage.set(this.data.invitationIdentifier, this.data);
|
||||
await this.updateStatus();
|
||||
|
||||
const finalAvailability = await this.engine.getChangeAvailability(
|
||||
this.data.invitationIdentifier,
|
||||
);
|
||||
|
||||
return {
|
||||
changeAmountSatoshis: availability.changeAmountSatoshis,
|
||||
feeSatoshis:
|
||||
finalAvailability.inputTotalSatoshis -
|
||||
finalAvailability.outputTotalSatoshis,
|
||||
};
|
||||
}
|
||||
|
||||
async addVariables(variables: XOInvitationVariable[]): Promise<void> {
|
||||
// Add the variables to the invitation
|
||||
await this.append({ variables });
|
||||
@@ -751,7 +812,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
/**
|
||||
* Get the starting actions for the invitation
|
||||
*/
|
||||
async getStartingActions() {
|
||||
async getStartingActions(): Promise<XOTemplateActionIntent[]> {
|
||||
return this.engine.listStartingActions(this.data.templateIdentifier);
|
||||
}
|
||||
|
||||
@@ -895,7 +956,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
this.storage.remove(this.data.invitationIdentifier);
|
||||
|
||||
// Remove the invitation from the engine's internal db
|
||||
await this.engine.DANGEROUS_deleteStoredInvitation(
|
||||
await this.engine.archiveInvitation(
|
||||
this.data.invitationIdentifier,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user