Format with prettier. Use screen mode for invitation import - dialog mode is broken.

This commit is contained in:
2026-03-23 10:15:48 +00:00
parent 7fd89c5663
commit b475b23beb
47 changed files with 1718 additions and 1098 deletions

View File

@@ -1,5 +1,5 @@
import type { XOTemplate, XOTemplateTransactionOutput } from '@xo-cash/types';
import type { Invitation } from '../services/invitation.js';
import type { XOTemplate, XOTemplateTransactionOutput } from "@xo-cash/types";
import type { Invitation } from "../services/invitation.js";
export interface SelectableUtxoLike {
outpointTransactionHash: string;
@@ -16,14 +16,17 @@ export const hasMissingRequirements = (missingRequirements: {
roles?: Record<string, unknown>;
}): boolean => {
return (
(missingRequirements.variables?.length ?? 0) > 0
|| (missingRequirements.inputs?.length ?? 0) > 0
|| (missingRequirements.outputs?.length ?? 0) > 0
|| (missingRequirements.roles !== undefined && Object.keys(missingRequirements.roles).length > 0)
(missingRequirements.variables?.length ?? 0) > 0 ||
(missingRequirements.inputs?.length ?? 0) > 0 ||
(missingRequirements.outputs?.length ?? 0) > 0 ||
(missingRequirements.roles !== undefined &&
Object.keys(missingRequirements.roles).length > 0)
);
};
export const isInvitationRequirementsComplete = async (invitation: Invitation): Promise<boolean> => {
export const isInvitationRequirementsComplete = async (
invitation: Invitation,
): Promise<boolean> => {
const missingRequirements = await invitation.getMissingRequirements();
return !hasMissingRequirements(missingRequirements);
};
@@ -34,7 +37,7 @@ export const resolveActionRoles = (
rolesFromNavigation?: string[],
): string[] => {
if (rolesFromNavigation && rolesFromNavigation.length > 0) {
return [ ...new Set(rolesFromNavigation) ];
return [...new Set(rolesFromNavigation)];
}
if (!template || !actionIdentifier) return [];
@@ -43,7 +46,7 @@ export const resolveActionRoles = (
.filter((entry) => entry.action === actionIdentifier)
.map((entry) => entry.role);
return [ ...new Set(roleIds) ];
return [...new Set(roleIds)];
};
export const roleRequiresInputs = (
@@ -61,26 +64,38 @@ export const roleRequiresInputs = (
// Some templates specify slot/input requirements at action.requirements.roles
// instead of role.requirements. Respect those as well.
const roleRequirement = action.requirements?.roles?.find((requirement) => requirement.role === roleIdentifier);
const roleRequirement = action.requirements?.roles?.find(
(requirement) => requirement.role === roleIdentifier,
);
const actionLevelSlotsMin = roleRequirement?.slots?.min ?? 0;
if (actionLevelSlotsMin > 0) return true;
const transactionIdentifier = action.transaction;
const transaction = transactionIdentifier ? template.transactions?.[transactionIdentifier] : undefined;
const transaction = transactionIdentifier
? template.transactions?.[transactionIdentifier]
: undefined;
const roleInputs = transaction?.roles?.[roleIdentifier]?.inputs;
return (roleInputs?.length ?? 0) > 0;
};
export const getTransactionOutputIdentifier = (output: XOTemplateTransactionOutput): string | undefined => {
if (typeof output === 'string') return output;
if (output && typeof output === 'object' && 'output' in output && typeof output.output === 'string') {
export const getTransactionOutputIdentifier = (
output: XOTemplateTransactionOutput,
): string | undefined => {
if (typeof output === "string") return output;
if (
output &&
typeof output === "object" &&
"output" in output &&
typeof output.output === "string"
) {
return output.output;
}
return undefined;
};
export const normalizeLockingBytecodeHex = (value: string): string => value.trim().replace(/^0x/i, '');
export const normalizeLockingBytecodeHex = (value: string): string =>
value.trim().replace(/^0x/i, "");
export const resolveProvidedLockingBytecodeHex = (
template: XOTemplate,
@@ -88,18 +103,23 @@ export const resolveProvidedLockingBytecodeHex = (
variableValues: Record<string, string>,
): string | undefined => {
const outputDefinition = template.outputs?.[outputIdentifier];
if (!outputDefinition || typeof outputDefinition.lockscript !== 'string') return undefined;
if (!outputDefinition || typeof outputDefinition.lockscript !== "string")
return undefined;
const lockingScriptDefinition = (template.lockingScripts as Record<string, unknown> | undefined)?.[outputDefinition.lockscript] as
| { lockingScript?: string }
| undefined;
const lockingScriptDefinition = (
template.lockingScripts as Record<string, unknown> | undefined
)?.[outputDefinition.lockscript] as { lockingScript?: string } | undefined;
const scriptIdentifier = lockingScriptDefinition?.lockingScript;
if (!scriptIdentifier) return undefined;
const scriptExpression = (template.scripts as Record<string, unknown> | undefined)?.[scriptIdentifier];
if (typeof scriptExpression !== 'string') return undefined;
const scriptExpression = (
template.scripts as Record<string, unknown> | undefined
)?.[scriptIdentifier];
if (typeof scriptExpression !== "string") return undefined;
const directVariableMatch = scriptExpression.match(/^<\s*([A-Za-z0-9_]+)\s*>$/);
const directVariableMatch = scriptExpression.match(
/^<\s*([A-Za-z0-9_]+)\s*>$/,
);
if (!directVariableMatch) return undefined;
const variableIdentifier = directVariableMatch[1];
@@ -111,15 +131,17 @@ export const resolveProvidedLockingBytecodeHex = (
return normalizeLockingBytecodeHex(providedValue);
};
export const mapUnspentOutputsToSelectable = (unspentOutputs: any[]): SelectableUtxoLike[] => {
export const mapUnspentOutputsToSelectable = (
unspentOutputs: any[],
): SelectableUtxoLike[] => {
return unspentOutputs.map((utxo: any) => ({
outpointTransactionHash: utxo.outpointTransactionHash,
outpointIndex: utxo.outpointIndex,
valueSatoshis: BigInt(utxo.valueSatoshis),
lockingBytecode: utxo.lockingBytecode
? typeof utxo.lockingBytecode === 'string'
? typeof utxo.lockingBytecode === "string"
? utxo.lockingBytecode
: Buffer.from(utxo.lockingBytecode).toString('hex')
: Buffer.from(utxo.lockingBytecode).toString("hex")
: undefined,
selected: false,
}));
@@ -133,7 +155,10 @@ export const autoSelectGreedyUtxos = (
const seenLockingBytecodes = new Set<string>();
for (const utxo of utxos) {
if (utxo.lockingBytecode && seenLockingBytecodes.has(utxo.lockingBytecode)) {
if (
utxo.lockingBytecode &&
seenLockingBytecodes.has(utxo.lockingBytecode)
) {
continue;
}
if (utxo.lockingBytecode) {