Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 152 additions & 96 deletions docs/governance/02_proposal_submission.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,129 +5,185 @@ sidebar_label: Submission Guidelines

# Submission Guidelines

Olympus On-Chain Governance (OCG) proposals are executable transactions. A strong submission should make the intended protocol change clear, prove the calldata is correct, and give tokenholders enough time and context to review it before voting.

:::info
Before submitting a new proposal, it's essential to understand Olympus' On-Chain Governance (OCG) system. Please review the details in the [Intro page](./00_governance.md) and [Proposal lifecycle page](./proposal_lifecycle).
Before submitting a new proposal, review the [Governance intro](./00_governance.md) and [Proposal lifecycle](./proposal_lifecycle). The lifecycle page explains activation, voting, queueing, and execution after the initial submission.
:::

## Requirements
## Submission Paths

To submit a new proposal to on-chain governance (OCG), submitters interact directly with Governor Bravo contract by calling `propose()` function. However, the following requirements must be met before submitting a proposal:
There are two common ways to move a proposal from idea to on-chain submission:

1. **Minimum voting power** - proposer must hold, and maintain, at least `proposalThreshold` of the total gOHM supply at time of proposal submission.
2. **Code review** - proposals must be added, tested and simulated in [olympus-v3 repository](https://github.com/OlympusDAO/olympus-v3/). This process ensures that the proposal is secure and achieves the intended outcomes without putting the protocol at risk.
1. **Self-submission** - the author holds or receives enough delegated gOHM voting power, prepares the proposal in the `olympus-v3` repository, and submits it directly.
2. **Operator-assisted submission** - the author prepares the intent and review materials, while a trusted proposer or agent operator helps convert the proposal into tested calldata and submits from an eligible proposer address.

### Minimum voting power
Both paths should produce the same public artifacts: a reviewed `olympus-v3` pull request, test output, proposal calldata, and an on-chain proposal that references the PR or other review material in the description.

## Requirements

Whily anyone can submit a proposal by calling `propose()`, the transaction will revert unless proposer has a minimum amount of voting power to submit a proposal, determined by calling the `getProposalThresholdVotes()` function:
To submit a new OCG proposal, a proposer calls Governor Bravo's `propose()` function. The transaction will revert unless the proposer satisfies these requirements.

### Minimum Voting Power

The proposer must hold, or be delegated, at least `proposalThreshold` of the total gOHM supply. The threshold is calculated by `getProposalThresholdVotes()`:

```solidity
function getProposalThresholdVotes() public view returns (uint256) {
return (gohm.totalSupply() * proposalThreshold) / 100_000;
}
```

The proposer's gOHM balance is checked within the `propose()`, `queue()`, and `execute()` functions. If, at any time, the proposer's gOHM balance falls below the proposal threshold, the proposal will fail. This approach ensures that proposers (or their delegators) maintain skin in the game, preventing them from benefiting from malicious proposals.
The proposer's votes are checked during `propose()`, `queue()`, and `execute()`. Delegated voting power must remain in place through the full proposal lifecycle.

:::info
The current proposalThreshold is set to 0.017% of the total gOHM supply
The current `proposalThreshold` is 0.017% of the total gOHM supply. Check the live Governor contract before submitting, because the exact gOHM amount changes with supply.
:::

:::tip
Governor Bravo checks prior votes. If voting power is delegated to a proposer address, wait until at least the next block before calling `propose()`.
:::

### Code review
### Proposal Simulation

Proposals must be added, tested, and simulated in the [olympus-v3 repository](https://github.com/OlympusDAO/olympus-v3/). Olympus uses [forge-proposal-simulator](https://solidity-labs.gitbook.io/forge-proposal-simulator/) to make proposals reviewable in a forked environment before they are submitted on-chain.

Olympus uses [forge-proposal-simulator](https://solidity-labs.gitbook.io/forge-proposal-simulator/), an open-source framework designed to structure proposals effectively and streamline the proposal verification process. On a high-level, this framework allows anyone to execute proposals in a forked environment and develop integration tests to examine the new system's behavior in a controlled sandbox.
A proposal PR should show:

:::warning Warning
Due to the importance of this framework in ensuring transparency and security, **Emergency MS will immediately veto any proposals not satisfying the two requirements**. This stance is based on the belief that bypassing the framework indicates an attempt to pass a harmful proposal by obfuscating its review process.
- The proposal contract in `src/proposals/`
- Any new or updated addresses in `src/proposals/addresses.json`
- Tests in `src/test/proposals/`
- Simulation output proving the proposal can execute successfully
- Validation assertions proving the intended post-state

:::warning
Emergency MS may veto proposals that bypass the proposal simulator or make calldata difficult to review. The simulator is part of Olympus' governance safety process.
:::

## Instructions

To successfully submit a proposal, the proposer must do the following:

1. Submit code as a pull request to [olympus-v3 repo](https://github.com/OlympusDAO/olympus-v3)
2. Reach out in Discord to request a review from the community
3. Acquire, or be delegated to, `proposalThreshold` percent of gOHM supply. Proposer must maintain that amount of gOHM until conclusion of proposal
4. Call `propose()` on Governor Bravo contract, including the PR link in the description

To submit a successful PR with integration tests, begin by creating a new contract in `src/proposals/` named after its corresponding OIP (e.g., `OIP_XXX.sol`). The contract should inherit `GovernorBravoProposal`, and use [OIP_XXX.sol](./OIP_XXX.sol) as a template.

Declare all necessary dependencies in [address registry](https://github.com/OlympusDAO/olympus-v3/blob/master/src/proposals/addresses.json). Follow this naming convention:

- Use lowercase.
- Separate words with dashes.
- Start with the source: `"olympus"` or `"external"`.
- Indicate the address/contract type: e.g., `"token"`, `"multisig"`, `"policy"`.
- Include the address/contract name.
- Exceptions: `"proposer"`, `"olympus-governor"`, `"olympus-kernel"`.

Examples:

- Olympus policies: `"olympus-policy-xxx"`
- Olympus modules: Use the following pattern instead of importing module addresses:
```solidity
Kernel kernel = Kernel(addresses.getAddress("olympus-kernel"));
address TRSRY = address(kernel.getModuleForKeycode(toKeycode(bytes5("TRSRY"))));
```
- External tokens (DAI, sDAI, etc): `external-tokens-xxx`
- External contracts: `external-coolers-factory`
- Olympus multisigs: `olympus-multisig-dao` or `olympus-multisig-emergency`
- Olympus legacy contracts (OHM, gOHM, staking): `"olympus-legacy-xxx"`

Deploy the smart contracts by running `_deploy()`. If the contract is already deployed, import addresses from [address registry](https://github.com/OlympusDAO/olympus-v3/blob/master/src/proposals/addresses.json) instead. If necessary, use `_afterDeploy()` hook to cache balances or other values to be used in `validate()`.

Construct the proposal actions by using `_build()`. Use the following functions:

````solidity
// @dev push an action to the proposal
function \_pushAction(
uint256 value,
address target,
bytes memory data,
string memory \_description
) internal {
actions.push(
Action({
value: value,
target: target,
arguments: data,
description: \_description
})
## Recommended Workflow

### 1. Define the Proposal Intent

Write down the plain-English intent before writing Solidity. Include:

- What protocol state should change
- Which contracts will be called
- Why the change is needed
- Expected risk, reversibility, and operational follow-up
- Forum, Snapshot, OIP, or RFC links if they exist

This context should also appear in the proposal description or linked PR.

### 2. Prepare the Proposal Contract

Create a proposal contract in `src/proposals/`, named after the proposal or OIP. The contract should inherit `GovernorBravoProposal`.

Use the proposal contract to:

- Declare dependencies through `addresses.json`
- Deploy or reference contracts in `_deploy()`
- Build actions in `_build()`
- Simulate execution in `_run()`
- Assert the expected post-state in `_validate()`

Use `_pushAction()` for each Governor action:

```solidity
_pushAction(
target,
abi.encodeWithSelector(TargetContract.setParameter.selector, newValue),
"Set target parameter"
);
}
```

When referencing addresses, prefer the registry over hardcoded addresses. Use lowercase, dash-separated keys such as `olympus-policy-example`, `external-token-usds`, or `olympus-multisig-dao`.

For current Olympus Kernel contracts, use the [Protocol Visualizer](https://protocol-visualizer.olympusdao.finance/) or its GraphQL API to verify the active contract set before adding addresses. The visualizer reflects installed Kernel modules and policies, which helps reviewers confirm that proposal actions target the intended contracts.

### 3. Add Tests

Create tests in `src/test/proposals/`. The tests should import the proposal and run it against a mainnet fork.

// @dev push an action to the proposal with a value of 0
function _pushAction(
address target,
bytes memory data,
string memory _description
) internal {
_pushAction(0, target, data, _description);
}
```

Simulate the proposal execution by calling `_run()`. Use the provided code:

```solidity
// Executes the proposal actions.
function _run(Addresses addresses, address) internal override {
// Simulates actions on TimelockController
_simulateActions(
addresses.getAddress("olympus-governor"),
addresses.getAddress("olympus-legacy-gohm"),
addresses.getAddress("proposer")
);
}
```

Perform validations and assertions through `_validate_()`. Validations should definitely demonstrate to the community that the proposal is secure and achieves the intended outcomes without putting the protocol at risk.

Create tests in [src/test/proposals/](https://github.com/OlympusDAO/olympus-v3/tree/master/src/test/proposals) named after the OIP (e.g., `OIP_XXX.t.sol` for unit tests and `testProposal_xxx` for integration tests). Use [OIP_XXX.t.sol](https://github.com/OlympusDAO/olympus-v3/tree/master/src/test/proposals/OIP_XXX.t.sol) as a template. Import your proposal, and its dependencies. Modify `setUp()` in `OIP_XXX.sol` and include this test to ensure `setUp()` is executed correctly:
A minimal test should prove setup works:

```solidity
// [DO NOT DELETE] Dummy test to ensure that `setUp` is executed
function testProposal() public {
assertTrue(true);
}
````
```

Useful tests go further and verify the post-proposal state that tokenholders care about. For example, assert changed parameters, permissions, enabled flags, balances, or market configuration.

### 4. Open a Review PR

Open a pull request against [olympus-v3](https://github.com/OlympusDAO/olympus-v3/) with the proposal contract, tests, and any address registry changes.

The PR should include:

- Summary of actions
- Governance/forum context
- Local test command and result
- Any assumptions or operational dependencies
- Expected on-chain proposer address, if known

### 5. Check Proposer Readiness

Before submission, verify:

- Proposer has enough delegated gOHM voting power
- Delegation was mined at least one block ago
- Proposer has enough ETH for gas
- Proposer can sign from the intended wallet or keystore
- Proposal description includes the PR link or equivalent review artifact

### 6. Dry-Run Submission

Use the repository's proposal scripts or Foundry directly to dry-run the exact submission path before broadcasting.

For script-based proposals, submit through the wrapper script contract, not the proposal contract directly. The wrapper is what prepares the proposal script environment for `forge script`.

Example shape:

```bash
src/scripts/proposals/submitProposal.sh \
--file src/proposals/MyProposal.sol \
--contract MyProposalScript \
--chain "$RPC_URL" \
--env .env.proposer \
--broadcast false
```

Review the printed targets, values, calldata, description, proposer address, and any simulation output before broadcasting.

### 7. Broadcast

After review, broadcast the same command with broadcasting enabled:

```bash
src/scripts/proposals/submitProposal.sh \
--file src/proposals/MyProposal.sol \
--contract MyProposalScript \
--chain "$RPC_URL" \
--env .env.proposer \
--broadcast true
```

Capture the transaction hash, block number, proposal id, and proposer address. Add them to the PR or governance discussion so reviewers can follow the proposal through activation, voting, queueing, and execution.

## Operator-Assisted Submissions

A proposer does not need to be the same person who authored the proposal. A contributor can work with a trusted proposer, multisig, or agent operator that acts as middleware between the human intent and Governor Bravo.

This can lower friction for contributors who understand the desired protocol change but do not maintain local Foundry tooling, proposer infrastructure, or enough delegated gOHM voting power.

The operator should still keep the process transparent:

- Use a dedicated proposer address where possible
- Hold only the gas ETH needed for submission
- Receive delegated gOHM voting power explicitly for the proposal
- Run the same fork tests and dry-runs as a self-submitter
- Share the exact PR, calldata, proposer address, and transaction hash
- Never broadcast until the proposal author and relevant reviewers approve the final payload

Once you feel comfortable, open a pull request in [olympus-v3 repo](https://github.com/OlympusDAO/olympus-v3) in the format `OIP-XXX: proposal simulation`.
For a contract-aware prompt and implementation template that an agent can use to
start proposal work, see [Agent Proposal Prompt](./agent_proposal_prompt).
Loading