Update to latest engine. Add sync-v2. Various fixes.
This commit is contained in:
@@ -71,7 +71,7 @@ export function AppProvider({
|
||||
});
|
||||
|
||||
// Start the AppService (loads existing invitations)
|
||||
service.start();
|
||||
await service.start();
|
||||
|
||||
// Set the service and mark as initialized
|
||||
setAppService(service);
|
||||
|
||||
@@ -359,7 +359,7 @@ export function TemplateListScreen(): React.ReactElement {
|
||||
|
||||
try {
|
||||
setStatus('Deleting template...');
|
||||
await appService.engine.DANGEROUS_deleteImportedTemplate(
|
||||
await appService.engine.archiveTemplate(
|
||||
templateToDelete.templateIdentifier,
|
||||
);
|
||||
setIsDeleteDialogOpen(false);
|
||||
|
||||
@@ -331,7 +331,6 @@ export function useActionWizard() {
|
||||
);
|
||||
const success = await invitationManager.addInputsAndOutputs(
|
||||
selectedUtxos,
|
||||
utxoSelection.changeAmount,
|
||||
);
|
||||
if (success) focus.resetToContent();
|
||||
return success;
|
||||
@@ -493,9 +492,12 @@ export function useActionWizard() {
|
||||
selectedUtxoIndex: utxoSelection.selectedUtxoIndex,
|
||||
setSelectedUtxoIndex: utxoSelection.setSelectedUtxoIndex,
|
||||
requiredAmount: utxoSelection.requiredAmount,
|
||||
fee: utxoSelection.fee,
|
||||
fee:
|
||||
invitationManager.feeAwareChange?.feeSatoshis ?? utxoSelection.fee,
|
||||
selectedAmount: utxoSelection.selectedAmount,
|
||||
changeAmount: utxoSelection.changeAmount,
|
||||
changeAmount:
|
||||
invitationManager.feeAwareChange?.changeAmountSatoshis ??
|
||||
utxoSelection.changeAmount,
|
||||
toggleUtxoSelection: utxoSelection.toggleSelection,
|
||||
selectAll: utxoSelection.selectAll,
|
||||
deselectAll: utxoSelection.deselectAll,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
resolveProvidedLockingBytecodeHex,
|
||||
} from "../../../../utils/invitation-flow.js";
|
||||
import type { AppService } from "../../../../services/app.js";
|
||||
import type { FeeAwareChangeResult } from "../../../../services/invitation.js";
|
||||
|
||||
interface InvitationManagerDeps {
|
||||
appService: AppService;
|
||||
@@ -34,6 +35,8 @@ export function useInvitationManager(deps: InvitationManagerDeps) {
|
||||
const [requirementsComplete, setRequirementsComplete] = useState(false);
|
||||
const [hasSignedAndBroadcasted, setHasSignedAndBroadcasted] = useState(false);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [feeAwareChange, setFeeAwareChange] =
|
||||
useState<FeeAwareChangeResult | null>(null);
|
||||
|
||||
/** Re-check whether all invitation requirements are satisfied. */
|
||||
const refreshRequirements = useCallback(
|
||||
@@ -195,10 +198,7 @@ export function useInvitationManager(deps: InvitationManagerDeps) {
|
||||
* @returns true on success, false on failure.
|
||||
*/
|
||||
const addInputsAndOutputs = useCallback(
|
||||
async (
|
||||
selectedUtxos: SelectableUTXO[],
|
||||
changeAmount: bigint,
|
||||
): Promise<boolean> => {
|
||||
async (selectedUtxos: SelectableUTXO[]): Promise<boolean> => {
|
||||
if (!invitationId || !appService) return false;
|
||||
|
||||
setIsProcessing(true);
|
||||
@@ -218,7 +218,7 @@ export function useInvitationManager(deps: InvitationManagerDeps) {
|
||||
}));
|
||||
|
||||
await instance.addInputs(inputs);
|
||||
await instance.addOutputs([{ valueSatoshis: changeAmount }]);
|
||||
setFeeAwareChange(await instance.addFeeAwareChange());
|
||||
await refreshRequirements(invitationId);
|
||||
setStatus("Inputs and outputs added");
|
||||
return true;
|
||||
@@ -283,6 +283,7 @@ export function useInvitationManager(deps: InvitationManagerDeps) {
|
||||
invitationId,
|
||||
requirementsComplete,
|
||||
hasSignedAndBroadcasted,
|
||||
feeAwareChange,
|
||||
isProcessing,
|
||||
setIsProcessing,
|
||||
refreshRequirements,
|
||||
|
||||
@@ -29,7 +29,8 @@ import {
|
||||
formatInvitationListItem,
|
||||
formatInvitationId,
|
||||
} from '../../../utils/invitation-utils.js';
|
||||
import type { ResolvedInvitationVariable } from '@xo-cash/engine';
|
||||
// import type { ResolvedInvitationVariable } from '@xo-cash/engine';
|
||||
import type { ResolvedInvitationVariable } from '../../../utils/resolve-invitation-data.js'
|
||||
|
||||
import { InvitationImportFlow } from './invitation-import/InvitationImportFlow.js';
|
||||
import { compileCashAssemblyString } from '@xo-cash/engine';
|
||||
@@ -375,7 +376,7 @@ export function InvitationScreen(): React.ReactElement {
|
||||
setIsLoading(false)
|
||||
setStatus('Ready')
|
||||
}
|
||||
})
|
||||
}, [selectedInvitation, showInfo, showError, setStatus]);
|
||||
|
||||
const copyId = useCallback(async () => {
|
||||
if (!selectedInvitation) {
|
||||
@@ -480,8 +481,6 @@ export function InvitationScreen(): React.ReactElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const changeAmount = accumulated - requiredAmount - fee;
|
||||
|
||||
setStatus('Adding inputs...');
|
||||
await selectedInvitation.addInputs(
|
||||
selectedUtxos.map(u => ({
|
||||
@@ -490,20 +489,16 @@ export function InvitationScreen(): React.ReactElement {
|
||||
}))
|
||||
);
|
||||
|
||||
if (changeAmount >= dust) {
|
||||
setStatus('Adding change output...');
|
||||
await selectedInvitation.addOutputs([{
|
||||
valueSatoshis: changeAmount,
|
||||
}]);
|
||||
}
|
||||
setStatus('Calculating miner fee and change...');
|
||||
const feeAwareChange = await selectedInvitation.addFeeAwareChange();
|
||||
|
||||
showInfo(
|
||||
`Requirements filled!\n\n` +
|
||||
`• Selected ${selectedUtxos.length} UTXO(s)\n` +
|
||||
`• Total: ${formatSatoshis(accumulated)}\n` +
|
||||
`• Required: ${formatSatoshis(requiredAmount)}\n` +
|
||||
`• Fee: ${formatSatoshis(fee)}\n` +
|
||||
`• Change: ${formatSatoshis(changeAmount)}\n\n` +
|
||||
`• Fee: ${formatSatoshis(feeAwareChange.feeSatoshis)}\n` +
|
||||
`• Change: ${formatSatoshis(feeAwareChange.changeAmountSatoshis)}\n\n` +
|
||||
`Now use "Sign Transaction" to complete.`
|
||||
);
|
||||
setStatus('Ready');
|
||||
|
||||
@@ -31,9 +31,6 @@ import { hexToBin } from '@bitauth/libauth';
|
||||
/** Default fee estimate in satoshis. */
|
||||
const DEFAULT_FEE = 500n;
|
||||
|
||||
/** Dust threshold — outputs below this are unspendable. */
|
||||
const DUST_THRESHOLD = 546n;
|
||||
|
||||
/**
|
||||
* Resolve the fixed index of a flow step from `IMPORT_STEPS`.
|
||||
* We centralize this so step transitions do not rely on magic numbers.
|
||||
@@ -70,6 +67,7 @@ export function InvitationImportFlow({
|
||||
const [variableInputs, setVariableInputs] = useState<ImportVariableInput[]>([]);
|
||||
const [selectedInputs, setSelectedInputs] = useState<SelectableUTXO[]>([]);
|
||||
const [changeAmount, setChangeAmount] = useState(0n);
|
||||
const [fee, setFee] = useState(DEFAULT_FEE);
|
||||
const [requiredAmount, setRequiredAmount] = useState(0n);
|
||||
|
||||
// ── Cancel handler ───────────────────────────────────────────────────────
|
||||
@@ -198,33 +196,30 @@ export function InvitationImportFlow({
|
||||
|
||||
/** InputsSelectStep completed — user selected UTXOs. */
|
||||
const handleInputsComplete = useCallback(async (inputs: SelectableUTXO[]) => {
|
||||
setSelectedInputs(inputs);
|
||||
if (!invitation) return;
|
||||
|
||||
await invitation?.addInputs(inputs.map(input => ({
|
||||
outpointTransactionHash: hexToBin(input.outpointTransactionHash),
|
||||
outpointIndex: input.outpointIndex,
|
||||
})));
|
||||
try {
|
||||
setSelectedInputs(inputs);
|
||||
|
||||
// Compute totals from selected inputs
|
||||
const totalSelected = inputs.reduce((sum, u) => sum + u.valueSatoshis, 0n);
|
||||
await invitation.addInputs(inputs.map(input => ({
|
||||
outpointTransactionHash: hexToBin(input.outpointTransactionHash),
|
||||
outpointIndex: input.outpointIndex,
|
||||
})));
|
||||
|
||||
// Determine required amount from invitation variables
|
||||
const requiredSats = await invitation?.getSatsOut() ?? 0n;
|
||||
setRequiredAmount(requiredSats);
|
||||
const requiredSats = await invitation.getSatsOut();
|
||||
setRequiredAmount(requiredSats);
|
||||
|
||||
// Set the change amount for the review step
|
||||
const changeAmountSats = totalSelected - requiredSats - DEFAULT_FEE;
|
||||
setChangeAmount(changeAmountSats);
|
||||
const feeAwareChange = await invitation.addFeeAwareChange();
|
||||
setChangeAmount(feeAwareChange.changeAmountSatoshis);
|
||||
setFee(feeAwareChange.feeSatoshis);
|
||||
|
||||
// Add the change output if it exceeds the dust threshold
|
||||
if (changeAmountSats >= DUST_THRESHOLD) {
|
||||
await invitation?.addOutputs([{
|
||||
valueSatoshis: changeAmountSats,
|
||||
}]);
|
||||
setCurrentStep(REVIEW_STEP_INDEX); // → Review
|
||||
} catch (error) {
|
||||
showError(
|
||||
`Failed to add inputs and calculate change: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
|
||||
setCurrentStep(REVIEW_STEP_INDEX); // → Review
|
||||
}, [invitation]);
|
||||
}, [invitation, showError]);
|
||||
|
||||
/** ReviewStep completed — invitation import is done. */
|
||||
const handleReviewComplete = useCallback(() => {
|
||||
@@ -332,6 +327,7 @@ export function InvitationImportFlow({
|
||||
selectedRole={selectedRole}
|
||||
selectedInputs={selectedInputs}
|
||||
changeAmount={changeAmount}
|
||||
fee={fee}
|
||||
requiredAmount={requiredAmount}
|
||||
appService={appService}
|
||||
onComplete={handleReviewComplete}
|
||||
|
||||
@@ -14,9 +14,6 @@ import { useSatoshisConversion } from '../../../../hooks/useSatoshisConversion.j
|
||||
import { useLayeredInput } from '../../../../hooks/useInputLayer.js';
|
||||
import type { ReviewStepProps, SelectableUTXO } from '../types.js';
|
||||
|
||||
/** Default fee estimate in satoshis. */
|
||||
const DEFAULT_FEE = 500n;
|
||||
|
||||
/** Dust threshold — outputs below this are unspendable. */
|
||||
const DUST_THRESHOLD = 546n;
|
||||
|
||||
@@ -27,6 +24,7 @@ export function ReviewStep({
|
||||
selectedInputs,
|
||||
requiredAmount,
|
||||
changeAmount,
|
||||
fee,
|
||||
onComplete,
|
||||
onCancel,
|
||||
isActive,
|
||||
@@ -35,7 +33,6 @@ export function ReviewStep({
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { formatSatoshisToFiat } = useSatoshisConversion();
|
||||
|
||||
const fee = DEFAULT_FEE;
|
||||
const action = template?.actions?.[invitation.data.actionIdentifier];
|
||||
|
||||
// Compute totals from selected inputs
|
||||
|
||||
@@ -119,6 +119,7 @@ export interface ReviewStepProps {
|
||||
selectedRole: string;
|
||||
selectedInputs: SelectableUTXO[];
|
||||
changeAmount: bigint;
|
||||
fee: bigint;
|
||||
requiredAmount: bigint;
|
||||
appService: AppService;
|
||||
onComplete: () => void;
|
||||
|
||||
Reference in New Issue
Block a user