Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Template Builder

The template builder defines a transaction step. Each output receives part of the available funding and its own compilation context. Reserve fees explicitly after adding all outputs.

This contract pays 1,000 sats, returns the remaining funds as change, and reserves 100 sats for fees:

#![allow(unused)]
fn main() {
use bitcoin::{Amount, XOnlyPublicKey};
use sapio::contract::{CompilationError, Contract};
use sapio::{declare, then, Context};

struct Payment {
    recipient: XOnlyPublicKey,
    change: XOnlyPublicKey,
}

impl Payment {
    #[then]
    fn pay(self, ctx: Context) {
        let fee = Amount::from_sat(100);
        let mut tmpl = ctx
            .template()
            .set_label("Payment".into())
            .add_output(Amount::from_sat(1_000), &self.recipient, None)?;

        let change = tmpl
            .ctx()
            .funds()
            .checked_sub(fee)
            .ok_or(CompilationError::OutOfFunds)?;
        if change != Amount::ZERO {
            tmpl = tmpl.add_output(change, &self.change, None)?;
        }

        tmpl.add_fees(fee)?.into()
    }
}

impl Contract for Payment {
    declare! {actions, Self::pay}
}
}

Builder methods consume the previous builder and return the updated one. ctx().funds() reports the remaining construction budget. Emitted outputs and explicitly reserved fees determine the template’s minimum funding requirement; an unused budget is not a fee reservation.

add_output passes the output’s amount to the receiving contract. When ordinal ranges are known, outputs receive consecutive prefixes in transaction input order. Debiting the builder without creating an output would shift those ordinal assignments, so direct spend_amount access is private.

add_fees records the fee and changes the builder’s state. That state allows additional fee reservations within the remaining budget, but cannot add more outputs or auxiliary funds. This keeps fees after all outputs. Complete metadata, guards, input sequences and change outputs before reserving fees.

Auxiliary input contributions use add_sequence().add_amount(amount)?; these funds are separate from the contract input’s required contribution. When input ordinals are tracked, allocate all known sats to outputs before introducing unknown auxiliary funds. Binding checks the actual funding inputs.

Sapio currently places the contract’s UTXO at input zero. The CTV commitment includes this index. See the builder implementation for the complete set of operations.