Skip to main content

BTC Staking Simulation

Simulating a BTC staking transaction is a crucial step before broadcasting it to the network. Simulation helps:

  • Prepare expected outputs
  • Verify the success of the transaction
  • Avoid errors like No valid UTXO found for treasury fee

Example:

const resDeposit = await simulateStakeBTC({
motoChefContract,
stakeAmount,
feeAmount,
netStakeAmount,
treasuryAddressP2TR,
accountAddressTyped,
});

The returned object contains:

  • Outputs: The fee and staking outputs to include
  • Result: The simulated transaction to send later

Always run this simulation step before sendTransaction().


Full example:

This full implementation is an example taken from a MotoChef staking use case. It demonstrates how to manually construct simulation outputs for a specific pool and pass them to the contract before calling stakeBTC().

export async function simulateStakeBTC({
motoChefContract,
stakeAmount,
feeAmount,
netStakeAmount,
treasuryAddressP2TR,
accountAddressTyped,
}: {
motoChefContract: IMotoChef;
stakeAmount: bigint;
feeAmount: bigint;
netStakeAmount: bigint;
treasuryAddressP2TR: string;
accountAddressTyped: string;
}): Promise<{ result: StakeBTC; outputs: PsbtOutputExtended[] }> {
const outSimulation: StrippedTransactionOutput[] = [];
const outputs: PsbtOutputExtended[] = [];
let index = 2;

// Treasury output
outputs.push({
address: treasuryAddressP2TR,
value: Number(feeAmount),
});
outSimulation.push({
index,
to: treasuryAddressP2TR,
value: feeAmount,
flags: TransactionOutputFlags.hasTo,
scriptPubKey: undefined,
});
index++;

// Stake output
outputs.push({
address: accountAddressTyped,
value: Number(netStakeAmount),
});
outSimulation.push({
index,
to: accountAddressTyped,
value: netStakeAmount,
flags: TransactionOutputFlags.hasTo,
scriptPubKey: undefined,
});

// Set simulation details
const details: ParsedSimulatedTransaction = {
inputs: [],
outputs: outSimulation,
};
motoChefContract.setTransactionDetails(details);

// Simulate stakeBTC
const result = await motoChefContract.stakeBTC(stakeAmount);
if (!result.properties.success) throw new Error("Failed to simulate stake");

return { result, outputs };
}