Governance Contracts
currentPre-launch technical reference. Any token amounts, fees, collateral, rewards, or economic behavior below describe experimental testnet software, not finalized launch terms or promised benefits. Launch tokenomics remain to be determined.
The governance surface is four contracts that decide how the network spends its treasury, how it settles a disputed compute result, and how it keeps an accountable record of what its agents do. They sit on the public ledger, Citrate Network, and any contracted integrator should expect to read and call them.
What it is
Governance on Citrate is not one monolith. It is four small contracts, each with one job, so that a
treasury vote, a compute dispute, an agent audit record, and a formal-specification pin never share a
failure surface. Three of the four inherit a common ownership mixin, Governable, which hands authority
over in two steps: the sitting governance account proposes a successor with transferGovernance, and the
successor must call acceptGovernance before it takes effect, with cancelGovernanceTransfer available
before acceptance. A mistyped or unreachable successor therefore cannot lock governance, because the
handover does not complete until the new account acts.
| Contract | Role | Status |
|---|---|---|
TreasuryGovernor | On-chain governor for SALT and stSALT treasury spending | Implemented, pre-audit |
DisputeResolution | Bisection game that settles a challenged compute result | Implemented, TLA+ specified |
AgentDecisionRegistry | Audit trail of high-risk agent tool calls, with trust tiers | Implemented, pre-audit |
SpecRegistry | Domain to IPFS map of the behavioral specs agents check | Implemented, pre-audit |
The four are pre-audit. They carry inline remediation notes from internal review, for example the SOL-21 governance follow-on, but they have not completed a final third-party audit. Treat every address and parameter as subject to change before mainnet.
How to use it
Most readers will interact with one contract at a time. The two paths worth walking end to end are a treasury spend and a compute dispute.
- Move treasury funds. Acquire the voting power,
PROPOSAL_THRESHOLDis 10,000 SALT, then callproposeTreasurySpend. Voters callcastVoteover the voting window. After the window, callqueue, wait outEXECUTION_DELAY, then callexecute. Two proposal types execute on-chain:TreasurySpendcallstreasury.distribute, andCallperforms a generic(target, value, calldata)call so the governor can drive any governance function on a target it controls.ParameterChange,OracleUpdate, andEmergencyemit an event for an off-chain multisig to act on. - Dispute a compute result. As the challenger, call
initiateDisputewith the job and the step range, posting the bond. The defender callsacknowledgeDisputewith a matching bond. The challenger narrows the range withbisecteach round, the defender commits a step withrespond, and the governance referee ends it withresolve. If either side stalls past the deadline, anyone callstimeoutDisputeand the challenger wins. - Record an agent decision. An authorized recorder calls
registerDecision(orregisterDecisionWithTierCheck) with the agent id, the tool name, and a hash of the parameters. An authorized disputer can file against it; governance resolves withresolveDispute. - Pin a behavioral spec. Governance calls
registerSpecwith the operation domain and the IPFS CID of the Gherkin feature file, andupdateSpecto bump the version. Agents readgetSpecbefore a critical operation.
Reference
The audited surface, each item citing its source file under contracts/src/.
TreasuryGovernor
Source: contracts/src/TreasuryGovernor.sol. A full on-chain governor for treasury operations. Voting
power is the voter's native SALT balance plus their stSALT shares valued at the share price read from
LiquidStakingPool. The lifecycle is propose, vote, queue behind a timelock, then execute. Parameters are
fixed as constants and match core/economics/src/governance.rs.
| Constant | Value | Meaning |
|---|---|---|
PROPOSAL_THRESHOLD | 10,000 SALT | Minimum voting power to open a proposal |
VOTING_PERIOD | 50,400 blocks | Voting window, measured in blocks (wall-clock depends on block time) |
EXECUTION_DELAY | 7,200 blocks | Timelock before a queued proposal can execute |
QUORUM_BPS | 1,000 (10%) | Quorum as basis points of total supply |
APPROVAL_BPS | 6,000 (60%) | Approval threshold as basis points of votes cast |
GRACE_PERIOD | 50,400 blocks | Window to execute before a queued proposal expires |
EMERGENCY_THRESHOLD_MULTIPLIER | 3 | Emergency proposals need three times the threshold |
Proposal creation, each payable, each returning a proposalId:
proposeTreasurySpend(title, description, stablecoin, recipients[], amounts[])proposeParameterChange(title, description, parameterKey, parameterValue)proposeOracleUpdate(title, description, target, newOracle)proposeCall(title, description, target, value, data), generic on-chain execution against a governed targetproposeEmergency(title, description), which requires three times the threshold
Voting and lifecycle:
castVote(proposalId, support), whereVoteTypeisFor(0),Against(1),Abstain(2)queue(proposalId), only from theSucceededstateexecute(proposalId), guarded bynonReentrant, re-checks quorum and approval, runs the treasury callcancel(proposalId), by the proposer or the guardianacceptGovernanceOf(target), a permissionless completion of aGovernablehandover where this governor is the pending successortransferGuardian(newGuardian),onlyGuardian
Views: state, getVotingPower, getProposal, quorumThreshold, getSpendDetails. Events:
ProposalCreated, VoteCast, ProposalQueued, ProposalExecuted, ProposalCanceled,
GuardianTransferred.
DisputeResolution
Source: contracts/src/DisputeResolution.sol. A bisection game for challenged compute jobs, inheriting
ReentrancyGuard and Governable. The constructor takes a dispute bond and a maximum bisection-round
count. The design is written down and checked: the contract header cites DisputeResolution.tla with nine
invariants and the AdversarialCompute.tla properties, including that griefing is never profitable.
Flow:
initiateDispute(jobId, defender, rangeStart, rangeEnd),payable,nonReentrant; the challenger posts the bondacknowledgeDispute(disputeId),payable; the defender posts a matching bondbisect(disputeId, claimFaulty), the challenger halves the range each roundrespond(disputeId, stepResultHash), the defender commits a step resultresolve(disputeId, challengerWins),onlyGovernance; pays the winner and, if the challenger wins, slashes the defender through theINematocystSlashinginterface at theInconsistencytiertimeoutDispute(disputeId), callable by anyone after the deadline; the challenger wins
Admin, all onlyGovernance: setDisputeBond, setMaxBisectionRounds, setSlashingContract,
setRoundDeadline. The constant DEFAULT_ROUND_DEADLINE is 150 blocks. Views: getDispute,
isDisputeActive, getRangeSize.
The slash call is wrapped in try / catch, so a missing or reverting slashing contract does not block the
dispute from resolving and paying the winner. The slashing target itself is documented under
security contracts.
AgentDecisionRegistry
Source: contracts/src/AgentDecisionRegistry.sol. An on-chain audit trail of high-risk agent tool calls,
inheriting Governable. Each record holds an agent id, the tool name, a parameters hash, the block and
timestamp, the executing address, a status, and any dispute evidence. From the record count and the dispute
count it derives a trust score, decisions minus twice disputes, and a tier: Untrusted below 100,
Standard from 100 to under 500, Trusted at 500 or above.
Functions:
registerDecision(agentId, toolName, paramsHash),onlyAuthorizedRecorderdisputeDecision(decisionId, evidence),onlyAuthorizedDisputerresolveDispute(decisionId, upheld),onlyGovernanceregisterDecisionWithTierCheckanddisputeDecisionWithTierCheck, which do the same work and also emitTrustTierChangedwhen a tier boundary is crossedsetAuthorizedRecorder(recorder, allowed)andsetAuthorizedDisputer(disputer, allowed),onlyGovernance
Views: getDecisionHistory, getDecisionCount, getDisputeStatus, getTrustScore, getTrustTier.
Events include DecisionRecorded, DecisionDisputed, DisputeResolved, and TrustTierChanged. The
contract reverts with NotAuthorizedRecorder, NotAuthorizedDisputer, or ZeroAddress.
A second contract, AgentDecisionRegistryV2, lives at contracts/src/rbac/AgentDecisionRegistryV2.sol. It
is a separate, newer design, not a drop-in replacement: it logs every signed action with a fuller shape
(user, tenant, correlation id, event class, artifact root) and is append-only against the
AgentDecisionLog.tla spec. It ships as a new contract with its own migration path; the V1 contract above
is the one this page documents as the governance audit trail, and it remains current.
SpecRegistry
Source: contracts/src/SpecRegistry.sol. A map from an operation domain, for example "contract_deploy",
to the IPFS CID of a Gherkin .feature file that agents must check before a critical operation. It inherits
Governable; an earlier version had its own single-step transferGovernor, which was removed in the
RM-L/WP-L1.1 migration in favor of the two-step mixin.
Mutators, all onlyGovernance: registerSpec(domain, cid), updateSpec(domain, newCid) which bumps the
version, deactivateSpec(domain), reactivateSpec(domain). Views: getSpec returning cid, active flag,
and version; hasActiveSpec; getAllDomains. Events: SpecRegistered, SpecUpdated, SpecDeactivated,
SpecReactivated.
A treasury spend, start to finish:
uint256 id = governor.proposeTreasurySpend(
"Grant: Q3 dev fund", "...", usdc, recipients, amounts
);
governor.castVote(id, TreasuryGovernor.VoteType.For);
// voting period elapses
governor.queue(id);
// EXECUTION_DELAY blocks elapse
governor.execute(id);Design rationale
We split governance into four contracts rather than one because the failure modes are different in kind. A treasury vote is slow and deliberate and gated on capital. A compute dispute is fast and adversarial and gated on a bond. An agent audit record is high-volume and append-only. A spec pin is rare and governed. Folding them together would mean one upgrade, one bug, and one blast radius covering all four. Keeping them apart costs a little duplication and buys independent review and independent upgrade.
The dispute game is bisection rather than full re-execution because re-running a long computation on chain is not affordable. Bisection narrows the disagreement to a single step in a logarithmic number of rounds, and only that step needs adjudication. The design is written in TLA+ first so the property that matters, that an attacker who griefs always loses net SALT, is checked before the code is trusted. That formal work is described under research.
Failure modes
- Governance handover. A handover that names the wrong successor does not complete, because the
successor must call
acceptGovernance. The system stays under the current governance until a real successor accepts. This is the point of the two-step mixin. - Stalled dispute. If a party goes silent, the dispute does not hang. After the round deadline anyone
calls
timeoutDisputeand the challenger wins, so a defender cannot escape a losing position by waiting. - Slashing dependency.
DisputeResolutioncalls the slashing contract insidetry / catch. If the slashing target is unset or reverts, the dispute still resolves and the winner is still paid; only the stake penalty is skipped, and it can be applied once the dependency is fixed. - Treasury execution.
executere-checks quorum and approval at execution time, not only at proposal time, and isnonReentrant. A proposal that lost or that has expired past the grace period cannot be forced through.
Access and canon
Tier: commercial. These are deep governance contracts. An anonymous copy would materially help a competitor clone the network's economic and dispute machinery, so the full detail is served to contracted builders, not published openly. There are no secrets here, no keys and no private endpoints; addresses are not yet listed because the contracts are pre-audit and pre-deployment.
The agent-safety and formal-methods pieces, AgentDecisionRegistry and SpecRegistry, are the on-chain
edge of the research surface. The behavioral specs they pin and the TLA+ work behind DisputeResolution
live under research. Slashing and finality are covered under
Citrate Network consensus.
Source and verification
- Source repo:
citrate-chain, files undercontracts/src/:TreasuryGovernor.sol,DisputeResolution.sol,AgentDecisionRegistry.sol,SpecRegistry.sol, pluscontracts/src/rbac/AgentDecisionRegistryV2.solfor the V2 note andcontracts/src/lib/Governable.solfor the ownership mixin. - Audited against
citrate-chainSHA9d5959e. - Status by contract:
TreasuryGovernorImplemented, pre-audit;DisputeResolutionImplemented and TLA+ specified (DisputeResolution.tla,AdversarialCompute.tla), pre-audit;AgentDecisionRegistryImplemented, pre-audit, withAgentDecisionRegistryV2Implemented and TLA+ specified (AgentDecisionLog.tla);SpecRegistryImplemented, pre-audit. None has completed a final third-party audit. Verify deployed bytecode yourself witheth_getCodeonce addresses are published.