Skip to main content
ProposalCovenant manages the lifecycle of a vault spending proposal: PENDING → APPROVED → EXECUTED. Contract path: contracts/core/treasury/ProposalCovenant.cash CashScript version: ^0.13.0

Constructor Parameters (Bytecode-Embedded)

ParameterTypeDescription
vaultIdbytes32Links proposal to vault
signer1Hashbytes20Vault signer 1
signer2Hashbytes20Vault signer 2
signer3Hashbytes20Vault signer 3
requiredApprovalsintApproval threshold metadata within the current fixed three-signer proposal design

NFT State (40 bytes)

[0]:    version
[1]:    status (0=PENDING, 1=APPROVED, 2=EXECUTED, 3=CANCELLED, 4=EXPIRED)
[2]:    approval_bitmask (uint8: bit0=signer1, bit1=signer2, bit2=signer3)
[3]:    required_approvals (uint8)
[4-8]:  voting_end_timestamp (5 bytes)
[9-13]: execution_timelock (5 bytes)
[14-33]: payout_hash (bytes20)
[34-39]: reserved

Transaction Structure

All entrypoints spend proposal state from tx.inputs[0].
FunctionRequired outputsNotes
approvetx.outputs[0] updated proposal UTXOUpdates approvals and status transition logic.
executeNo proposal continuation output requiredProposal NFT is consumed/burned by design.
canceltx.outputs[0] updated proposal UTXOSets status to CANCELLED.
expiretx.outputs[0] updated proposal UTXORequires locktime >= voting deadline.

Functions

approve(sig approverSig, pubkey approverPubkey)

Single registered vault signer approval. Validation
  • Signer hash must match one of signer1Hash, signer2Hash, signer3Hash.
  • Signature must validate.
  • Status must be PENDING.
  • Signer must not have already approved (their bit in approval_bitmask must be unset).
State update
  • Signer bit is set in approval_bitmask.
  • Status becomes APPROVED when the number of set bits in approval_bitmask reaches requiredApprovals, otherwise stays PENDING.
  • tx.outputs[0] must preserve locking bytecode/token category with updated commitment.

execute()

Permissionless. Callable by anyone after executionTimelock has elapsed. Burns the proposal NFT (EXECUTED — no NFT output). Validation
  • Status must be APPROVED.
  • tx.locktime >= execution_timelock.
  • vaultId must be non-zero.

cancel(sig sig1, pubkey pubkey1, sig sig2, pubkey pubkey2)

Two distinct registered signer signatures required. Validation
  • Both signer hashes must be in signer set.
  • Signer hashes must be distinct.
  • Both signatures must validate.
  • Status must be PENDING or APPROVED.
State update
  • Status is set to CANCELLED.
  • tx.outputs[0] must preserve locking bytecode/token category with updated commitment.
In this contract version, cancel() is hardcoded to require two distinct signers. It does not directly use requiredApprovals.
FlowGuard’s current proposal covenant is still built around three explicit signer slots and a three-bit approval mask. BCH network upgrades can enable future redesigns, but larger signer sets or arbitrary vote counts still require new FlowGuard contract implementations.

expire()

Permissionless. Marks proposal as EXPIRED if tx.locktime >= voting_end_timestamp and status is still PENDING. Validation
  • Status must be PENDING.
  • voting_end_timestamp > 0.
  • tx.locktime >= voting_end_timestamp.
State update
  • Status is set to EXPIRED.
  • tx.outputs[0] must preserve locking bytecode/token category with updated commitment.