VoteLockCovenant locks governance FTs with an immutable vote choice. The UTXO model eliminates double-voting cryptographically.
Contract path: contracts/core/governance/VoteLockCovenant.cash
CashScript version: ^0.13.0
Constructor Parameters (Bytecode-Embedded)
| Parameter | Type | Description |
|---|
proposalId | bytes32 | Links vote to specific proposal |
voteChoice | int | 0 = AGAINST, 1 = FOR, 2 = ABSTAIN |
voterHash | bytes20 | hash160(voter pubkey) — only owner can reclaim |
unlockTimestamp | int | When tokens can be reclaimed |
NFT State (32 bytes)
[0]: version
[1-4]: proposal_id_prefix (first 4 bytes of proposalId)
[5]: vote_choice
[6-7]: reserved
[8-12]: lock_timestamp (5 bytes)
[13-17]: unlock_timestamp (5 bytes)
[18-31]: reserved
Transaction Structure
Both entrypoints spend vote-lock state from tx.inputs[0] and require token return at tx.outputs[0].
| Function | Required outputs | Notes |
|---|
reclaim | tx.outputs[0] voter P2PKH with full token amount/category | Requires tx.locktime >= unlockTimestamp. |
earlyReclaim | tx.outputs[0] voter P2PKH with full token amount/category | Unlock bypassed when caller provides terminal proposal status enum. |
Functions
reclaim(sig voterSig, pubkey voterPubkey)
Returns all locked governance FTs to voter after unlockTimestamp.
earlyReclaim(sig voterSig, pubkey voterPubkey, int proposalFinalStatus)
Returns tokens before unlockTimestamp if the proposal is done. Caller must pass a valid terminal status: 2 (EXECUTED), 3 (CANCELLED), or 4 (EXPIRED).
BCH has no cross-UTXO reads. The voter supplies proposalFinalStatus as a parameter — the covenant only validates enum membership (2|3|4). Off-chain verification of actual proposal status is required.
earlyReclaim() also does not re-check proposalId from NFT commitment. The function trusts that the locked UTXO already belongs to the intended proposal context established at lock time.