Skip to main content

Governance Actions

Governance actions are each defined by a unique Proposal<T> type. Proposals adjust protocol parameters, pause or unpause operations, or perform sensitive operations like package upgrades.

How does the committee vote on a proposal?​

Each proposal type has its own threshold, which a quorum of validators must reach by voting in support of the proposal. A proposal moves through these steps:

  1. A member of the current Hashi committee creates the proposal. Only members of the current committee can create proposals, and creating a proposal casts the creator's vote.
  2. Committee members vote in support of the proposal until it reaches the threshold for its proposal type.
  3. The proposal executes. The contract counts quorum against the committee in office when the proposal executes, so votes from members who left at a reconfiguration no longer count. Executed proposals stay onchain.
  4. A proposal expires 7 days after its creation; members can then no longer vote on or execute it, and anyone can delete it.

What proposal types are available?​

The following is the current set of available proposal types.

ProposalEffectAbort codes
UpgradeAuthorizes a package upgrade and records whether it is exclusiveNone
EnableVersionRe-enables a previously disabled package versionNone
DisableVersionDisables a package versionEDisableCurrentVersion
UpdateConfigUpdates existing keys in the instant configEInvalidConfigEntry, EProtectedConfigKey
UpdateEpochConfigUpdates existing keys in the epoch config, including the MPC parametersEInvalidConfigEntry, EInconsistentMpcConfig
AddConfigIntroduces new keys into the instant or epoch configNone
IgnoreMemberFlags a registered member as ignored, or clears the flagNone
EmergencyPausePauses or unpauses the bridgeNone

Configuration proposals target one of 2 stores: the instant config, where new values apply when the proposal executes, and the epoch config, where the next committee formed after execution receives the new values while the active committee keeps its pinned copy.

Upgrade​

Authorizes a package upgrade and records whether it is exclusive. An exclusive upgrade atomically disables all previous versions as the new package is committed; a non-exclusive upgrade deliberately leaves them callable. Every upgrade must make this classification explicitly. See Move Package Upgrades for the decision criteria and validator rollout contract.

EnableVersion​

Re-enables a previously disabled package version, allowing the protocol to use it again.

DisableVersion​

Disables a package version so the protocol can no longer use it. A version cannot disable itself: executing the proposal through package version N refuses to disable N (EDisableCurrentVersion). To disable a broken new version N:

  1. Pick an older version that is still enabled.
  2. Execute the DisableVersion proposal through that older version.

UpdateConfig​

Updates existing parameters in the instant config by key; the new values apply when the proposal executes. The contract validates every entry when a member creates the proposal and again when it executes, and the whole proposal aborts if any entry fails: the key must already exist in the store with a value of the same type (EInvalidConfigEntry), and the key must be governable. The guardian BTC public key and the Bitcoin chain ID stay pinned for the deployment's lifetime, and no config proposal can write them (EProtectedConfigKey). Values themselves are otherwise not bounded onchain: a proposal needs a supermajority of committee weight, and reviewing the proposed value is part of voting. See Configuration for the keys and defaults.

This proposal also changes the guardian's URLs, with entries that set guardian_url (the public endpoint) or guardian_node_url (the endpoint nodes call) to a new string value. The URLs are the only guardian settings governance can change: the guardian's BTC public key is immutable once set, because rotating it would invalidate derived deposit addresses. Executing the proposal changes the onchain value, but a running node keeps the guardian client it already resolved from guardian_node_url, so you must restart each node before it calls a new node URL. Nodes do not read guardian_url.

UpdateEpochConfig​

Updates existing parameters in the epoch config by key, including the MPC parameters, which must pass their range check (EInvalidConfigEntry). At execution, mpc_weight_reduction_allowed_delta must also end up below mpc_max_faulty_in_basis_points (EInconsistentMpcConfig). The next committee formed after execution receives the new values; the active committee keeps its pinned copy. This proposal also refuses the pinned keys (the guardian BTC public key and the Bitcoin chain ID).

AddConfig​

Introduces new keys into the instant or epoch config, which is how a node-side setting becomes governable without a package upgrade. The proposal is insert-only: a key already present in the target store aborts, and the first value fixes the key's type for later updates. The pinned key names cannot be introduced into either store.

IgnoreMember​

Flags a registered member as ignored, or clears the flag. The next committee formed leaves an ignored member out, so its weight drops out of every threshold from then on; the current committee is never altered, and if a reconfiguration is already pending the change lands one epoch later. The member stays registered (no one can remove an ignored member as inactive), and the same proposal type with the flag cleared re-admits it.

EmergencyPause​

Pauses or unpauses the bridge by setting the paused config flag. While paused, the chain refuses new deposit and withdrawal requests and every committee step that processes them, and each node refuses to sign for deposits and withdrawals (not in the 1.0 releases). Users can still cancel withdrawal requests, and reconfiguration and governance keep running. Anyone can still broadcast a withdrawal fully signed before the pause, and a committee member can still finish one the guardian already signed. The pause doesn't stop the guardian. The two directions use different vote thresholds:

  • Pause: governance_emergency_pause_threshold_bps, default 5% of committee weight. Pausing is deliberately cheap so a small fraction of the committee can quickly halt the system.
  • Unpause: governance_emergency_unpause_threshold_bps, default two thirds. Unpausing requires a supermajority.

See Configuration for the keys and defaults. paused is also an ordinary config key, so an UpdateConfig proposal can set or clear it at the two-thirds threshold.

Aborting a stuck reconfiguration is deliberately not a proposal: the committees that could vote on one are the parties a stalled reconfiguration puts in doubt. It is a permissionless entry function gated on onchain state instead; see Abort Reconfig. Holding off the replacement that would otherwise follow is an UpdateConfig setting reconfig_hold, passed by the committee that is still in service.