Skip to content

Latest commit

 

History

History
514 lines (380 loc) · 23.2 KB

File metadata and controls

514 lines (380 loc) · 23.2 KB

AGENTS.md

This file provides guidance to LLM tools when working with code in this repository.

Project Overview

This is Olympus V3 (aka Bophades), a complete rewrite of the Olympus protocol using the Default Framework. It's a modular DeFi protocol built on Solidity that separates concerns through a Kernel-Module-Policy architecture.

Build and Development Commands

Installation and Build:

  • Node.js >=24 is required for local development and CI
  • pnpm install - Install all dependencies (runs postinstall script)
  • pnpm build or forge build - Build all files
  • forge build --contracts path/to/contract.sol - Build a specific contract

Testing:

  • pnpm run test - Run all tests (runs ./shell/test_all.sh)
  • pnpm run test:unit - Run unit tests only (excludes fork tests and proposals)
  • pnpm run test:fork - Run fork tests (requires ALCHEMY_API_KEY env var)
  • pnpm run test:proposal - Run OCG proposal tests
  • pnpm run test:coverage - Generate test coverage report
  • forge test -vvv --match-contract ContractTest - Run a specific test contract

For detailed test debugging guidance (verbosity levels, setUp() issues, trace output), use the /test-debug skill.

Linting:

  • pnpm run lint - Format and lint code (prettier + solhint + markdownlint)
  • pnpm run lint:check - Check formatting and linting without fixing
  • pnpm run prettier - Format code (runs quicker than linting)

For detailed linter note resolution guidance (deployed vs in-development contracts, suppression templates), use the /lint-fix skill.

Note: Always build, test and lint updated files. Use project-wide build and test commands sparingly.

Safety and Permissions

Allowed without prompt:

  • Read files, list files
  • Build single file
  • Formatting and linting
  • Test single file

Ask first:

  • Installing dependencies
  • Full build
  • Git push
  • Deleting files
  • Changing file permissions
  • Full test suites (test, test:unit, test:fork or test:proposal)

Repo Workflow

Use the repo's existing commands and tooling for commit, push, and PR work. Do not add scripts for this workflow.

Validation Gates

  • Before every commit, run the repo's existing lint/format command: pnpm run lint.
  • Before pushing, run the repo's existing full validation commands:
    • pnpm build or forge build
    • pnpm run lint:check
    • pnpm run test when the change is ready for full-suite validation or touches protocol behavior, tests, deployment, dependencies, or shared tooling
    • Include other existing focused validation commands when they are directly relevant to the changed files
  • Keep focused tests and single-file builds available during implementation, but do not substitute them for the push gate when the change is ready to publish.
  • CodeRabbit is a sparse pre-push review tool. Run it before pushing or when explicitly requested, not before every commit.
  • When using CodeRabbit, run coderabbit review --agent --base <PR base>. Verify the PR base and diff scope before trusting the result. Treat an instant "0 issues" response as suspicious until the base and reviewed diff have been confirmed.

Approval Gates

Batch approval into two explicit gates. If the user approves a gate, do not ask separately for lint, git add, git commit, git push, or PR creation steps covered by that gate.

  1. Commit approval:

    • Show the current branch.
    • Show the exact files that will be staged.
    • Show the validation command that will run before commit.
    • Show the exact commit message.
    • After approval, run validation, stage only the listed files, and commit.
  2. Publish approval:

    • Show the current branch.
    • Show the remote and PR base.
    • Show the push validation commands, including full tests when appropriate.
    • Show the CodeRabbit command when it applies.
    • Show the PR action: create or update.
    • Show the PR title and a summary of the PR body.
    • After approval, validate, push, and create or update the PR.

Never stage unrelated files. If the worktree has unrelated modifications, leave them unstaged and call them out in the approval summary.

Worktree Environment

  • For worktrees, check whether .env exists before running fork tests or full validation that needs RPC credentials.
  • If .env is missing, bootstrap it from the documented shared/local env source. If the source is unknown, ask once for the source and reuse that answer for the current worktree.
  • Do not repeatedly rediscover or re-report the same missing env issue after the source has been identified.

Audit and Review Intake

  • For audit issues or manually pasted CodeRabbit comments, first verify applicability against the current code.
  • State whether each issue applies, does not apply, or partially applies.
  • Identify the red test or smallest validation that demonstrates the issue before implementing, when a test is practical.
  • Implement only applicable fixes, then run focused validation before the normal commit or publish gate.
  • Include audit issue IDs in PR bodies when relevant.

Agent Behaviour

Communication Style

  • Be direct and concise
  • Avoid conversational fillers ("Let me...", "I'm going to...", "Now I'll...").
  • State what you're doing, not what you're about to do. Example: Instead of "Let me read the file to understand what's happening", simply read the file and report findings.

Approach

  • Make reasonable assumptions based on codebase conventions. Only ask when:
    • Multiple valid approaches exist with significant trade-offs
    • Security implications are unclear
    • The choice affects external integrations
  • Document assumptions in comments when you proceed without asking
  • When stuck, ask a clarifying question

Scope Management

  • Fix the specific issue, not the surrounding code unless requested
  • Don't refactor "just because" - prefer ugly-but-working over clean-but-risky
  • Exception: If you spot a critical security bug, flag it immediately

Information Gathering

  • Read the actual implementation before making changes. Never guess.
  • For cross-cutting concerns (e.g., "all places that use X"), use grep/glob to find all instances first
  • When exploring, cast a wide net initially, then narrow down

Failure Handling

  • If a build/test fails, report the actual error, not a summary
  • Include file:line references for all errors
  • Attempt to fix before reporting, but explain what you tried

Context Retention

  • Remember user preferences stated earlier in the conversation
  • When a pattern is established (e.g., "always use 18 decimals"), apply it consistently
  • If the user corrects you, update your mental model and acknowledge

Tool Selection

  • Prefer specialized tools (Read, Grep, Glob) over bash equivalents
  • Use Bash only for: git, npm/pnpm, forge commands, or actual terminal operations
  • Never use bash for file operations (cat, sed, awk, echo redirections)

Progress Reporting

  • For multi-step tasks, use TodoWrite proactively
  • Mark tasks in_progress immediately when starting
  • Mark completed after finishing - not in batches
  • One task in_progress at a time

Testing Discipline

  • Write or update tests before implementation
  • If tests don't exist for a function you're modifying, create them first
  • Run the specific test file after changes, not the full suite
  • Only run full test suites when explicitly requested or at major milestones

Commit and Git Hygiene

  • Use the Repo Workflow approval gates for commits, pushes, and PRs. Do not commit or push outside those gates unless the user explicitly requests a different flow.
  • The git commit message should follow the format of: <type>(<scope>): <description>
    • type may be one of:
      • feat: Introduces a new feature.
      • fix: Patches a bug.
      • docs: Documentation-only changes.
      • style: Changes that do not affect the meaning of the code (white-space, formatting, etc).
      • refactor: A code change that neither fixes a bug nor adds a feature.
      • perf: Improves performance.
      • test: Adds missing tests or corrects existing tests.
      • chore: Changes to the build process or auxiliary tools and libraries such as documentation generation.
    • scope can refer to the area of code (such as the feature) where the change has taken place
    • description is a concise summary of the changes

Git Workflow

  • When starting work on a new branch or feature, use git worktree instead of switching branches
  • List existing worktrees before creating new ones: git worktree list
  • Create a worktree for a branch: git worktree add ../olympus-v3-<feature-name> <branch-name>
  • When done with a worktree, remove it: git worktree remove ../olympus-v3-<feature-name>
  • Be aware of worktree locations when running commands—use absolute paths if the worktree is outside the main repo

Available Skills

Claude skills are located in .claude/skills/ and can be invoked by name:

Skill Purpose
/test-write Test writing guidance (file structure, modifiers, naming, error handling)
/test-debug Test debugging guidance (verbosity levels, setUp() issues, trace output)
/lint-fix Linter note resolution (deployed vs in-development contracts, suppression templates)

Invoke a skill by name, e.g., /test-write for test writing guidance.

MCP Servers

The project includes Model Context Protocol (MCP) servers for enhanced AI capabilities:

Server Purpose
CodeRabbit Automated code review via coderabbitai-mcp
Context7 Context-aware assistance via context7.com
Etherscan On-chain contract verification via etherscan.io
GitHub Repository integration via githubcopilot.com

Configuration is in .mcp.json. API keys are set via environment variables:

  • MCP_GITHUB_PAT - GitHub personal access token (for CodeRabbit and GitHub)
  • MCP_CONTEXT7_API_KEY - Context7 API key
  • MCP_ETHERSCAN_API_KEY - Etherscan API key

Architecture Overview

Default Framework Components

The protocol follows the Default Framework pattern with a three major components:

  1. Kernel (src/Kernel.sol) - Central governance and access control system

    • Manages installation/upgrading of modules and activation/deactivation of policies
    • Implements role-based access control with 5-byte keycodes
    • Executes governance actions: InstallModule, UpgradeModule, ActivatePolicy, DeactivatePolicy, ChangeExecutor, MigrateKernel
  2. Modules (src/modules/) - Shared state storage with minimal dependencies

    • Each module has a 5-byte keycode identifier (e.g., TRSRY, MINTR, PRICE)
    • Define roles for policies to access module functions
    • Can be upgraded by installing new versions with same keycode
    • Key modules: TRSRY (Treasury), MINTR (Minter), PRICE (Price Oracle), RANGE (Range-Bound Stability), ROLES (Access Control)
  3. Policies (src/policies/) - User-facing contracts with business logic

    • Request permissions from kernel to call module functions
    • Contain isolated state and handle user interactions
    • Examples: Operator (RBS), Heart (Auction), BondCallback, Cooler lending

Key Policy Categories

  • Core Protocol: Operator, Heart, Distributor, Emergency
  • Lending: Cooler V2 (MonoCooler), LoanConsolidator
  • Cross-Chain: CCIPBurnMintTokenPool, CrossChainBridge
  • Deposits: ConvertibleDepositFacility, YieldDepositFacility
  • Boosted Liquidity: BLVault implementations for Lido and LUSD

Directory Structure

src/
├── Kernel.sol                # Central control system
├── modules/                  # Shared state storage
│   ├── TRSRY/                # Treasury management
│   ├── MINTR/                # Token minting
│   ├── PRICE/                # Price oracles
│   ├── RANGE/                # Range-bound stability
│   └── ROLES/                # Access control
├── policies/                 # Business logic contracts
│   ├── cooler/               # Lending protocols
│   ├── deposits/             # Deposit facilities
│   ├── BoostedLiquidity/     # Liquidity mining
│   └── bridge/               # Cross-chain functionality
├── external/                 # External contract dependencies
├── interfaces/               # Standard interfaces
├── libraries/                # Utility libraries
└── test/                     # Test files and mocks
└── scripts/                  # Deployment and configuration scripts
└── proposals/                # On-Chain Governance (OCG) proposals

Development Guidelines

Testing

Test Commands:

  • pnpm run test - Run all tests (runs ./shell/test_all.sh)
  • pnpm run test:unit - Run unit tests only (excludes fork tests and proposals)
  • pnpm run test:fork - Run fork tests (requires ALCHEMY_API_KEY env var)
  • pnpm run test:proposal - Run OCG proposal tests
  • forge test -vvv --match-contract <Contract> - Run a specific test contract

Test Organization:

  • Unit tests exclude fork tests and proposals: --no-match-contract '(Fork)' --no-match-path 'src/test/proposals/*.t.sol'
  • Proposal tests are in src/test/proposals/ and require fork environment
  • Coverage reports generated in coverage/ directory

For detailed test writing guidance (file structure, modifiers, naming, error handling), use the /test-write skill.

Key standards summary:

  • One test file per contract external function (internal functions are tested indirectly via their callers)
  • Use given* modifiers for state setup
  • Follow branching tree naming: test_given<Condition>_<Action>_<ExpectedResult>()
  • Always use error selectors, never string messages: abi.encodeWithSelector(Error.selector)
  • All assertions must have descriptive messages

Deployment

  • Chain-specific env files: .env.[chain] (copy from .env.deploy.example)
  • Deployment scripts in src/scripts/deploy/
  • Saved deployments in src/scripts/deploy/savedDeployments/
  • See src/scripts/DEPLOY.md and src/scripts/DEPLOY_L2.md for detailed steps

Code Standards

  • Solidity version: >= 0.8.24 (with some on 0.8.15 for historical reasons)
  • Optimizer runs: 10,000 (except for some contracts that require specific runs to meet bytecode limits, see foundry.toml)
  • Follow existing patterns for module/policy development
  • Use Default Framework conventions for access control and state management
  • Dependencies are installed using soldeer (forge soldeer) and kept in dependencies/
  • Follow best-case practices for writing Solidity code, e.g. https://dev.to/truongpx396/solidity-limitations-solutions-best-practices-and-gas-optimization-27cb
  • Running forge build will output the forge tool's linting output. For linter note resolution, use the /lint-fix skill for guidance on deployed vs in-development contracts.
  • Internal state variables MUST use underscore prefix: uint256 internal _counter;
  • For commit and push validation, follow the Repo Workflow section.
  • When completing a major milestone, the unit tests should pass: pnpm run test:unit
  • Between milestones, run a build (forge build) and prettier (pnpm run prettier)
  • Do not use require() for assertions. Instead, preference custom errors. Custom errors should be defined in the contract's parent interface (where available), or else in the contract itself.
  • Do not revert with a blank message, use a custom error instead.
  • Contracts should have a separate interface that is defined in a separate file, to allow for easy integration. All interfaces are MIT-licensed, and should avoid using internal types. Interfaces should also use NatSpec to define functions and types, and any expectations for implementation contracts.
  • Contracts that implement interfaces should use the @inheritdoc NatSpec tag in function documentation to reference the parent interface's function.
  • Function documentation should outline the behaviour of the function, including any conditions that would result in a revert.

Access Control Pattern

  • Policies request permissions via requestPermissions() function
  • Kernel grants/revokes roles based on governance actions
  • Use onlyRole() modifiers (or onlyAdminRole() etc if using the PolicyAdmin mix-in) for function access control

Key Contracts to Understand

  • Kernel.sol - Core governance and module/policy management
  • policies/Operator.sol - Range-Bound Stability mechanism
  • policies/Heart.sol - Auction system for stability operations
  • modules/TRSRY/ - Treasury operations and asset management
  • policies/cooler/MonoCooler.sol - Primary lending protocol

Common Patterns

  • Modules inherit from Module base contract with keycode and version
  • Policies inherit from Policy base contract with dependency/permission requests
  • Use the IVersioned interface to standardise versioning of Modules and Policies
  • Contracts that implement interfaces should implement the supportsInterface() function from ERC165
  • Policies can use the PolicyEnabler mix-in to inherit common functionality around enabling/disabling contracts. Periphery contracts can use PeripheryEnabler.
  • Error handling with custom errors following naming conventions
  • When planning a new feature, to write the plan to disk in Markdown format, and always include a TODO list that can be checked off. When working on that new feature, regularly update the status in the task list of that feature plan.

Imports

  • When importing dependencies, use a versioned import path, e.g. @solmate-6.2.0 instead of solmate. Refer to remappings.txt for the aliases.
  • Imports must be at the top of the file, below the license and pragma.
  • Imports should be grouped under headings of: interface, libraries, contracts
  • Within each grouping, keep the imports sorted by the dependency path
  • Do NOT do global imports, import "src/Kernel.sol"
  • Instead, import individual contracts from a file, e.g. import {Kernel} from "src/Kernel.sol"
  • The codebase has different approaches to imports. Ignore those and implement the prescribed approach.

Solidity Math Guidelines

When working with Solidity code involving mathematical operations, follow these principles:

Core Principles

  1. No Floating-Point: Solidity has no floating-point numbers - all numbers are integers.

  2. Decimal Representation:

    • Decimal numbers are represented as integers with an associated decimal scale
    • Example: 1.0 with 18 decimals = 1000000000000000000 (1e18)
    • Always track and document the decimal scale of each variable
  3. Multiplication & Division Order:

    • When multiplying/dividing numbers with different scales, order matters
    • General pattern: multiply first, then divide to maintain precision
    • Example: result = a * scaleB / scaleC where result has scaleB decimals
    • Always calculate and verify the resulting decimal scale
    • Phantom overflows can occur, where a * scaleB (from the example above) overflows the maximum value of uint256. For that reason, it is advisable to use the FullMath library in src/libraries/FullMath.sol.
  4. Rounding Behavior:

    • Solidity rounds DOWN by default (floor division)
    • Use mulDiv() for standard rounding down
    • Use mulDivUp() when rounding up is needed
    • Be explicit about rounding direction in comments
    • Suggested approach: when involving values going to an external user, round down. This favours the protocol.
    • Ask the developer for desired behaviour
  5. Precision Loss:

    • Be aware of precision loss in division operations
    • Consider the order of operations to maximize precision
    • Document any intentional precision trade-offs

When Writing Code

  • Always comment the decimal scale of variables involved in calculations
  • Show the arithmetic reasoning: input scales → operation → output scale
  • Use descriptive variable names that hint at their scale when possible
  • Explicitly state rounding behavior when it matters

When Writing Tests

  • Document the mathematical working in comments above each assertion

  • Show step-by-step calculation with decimal scales

  • Include the expected value derivation

  • Example format:

    // deposit = 5e18 (18 decimals)
    // ohmScale = 1e9 (9 decimals)
    // price = 2e18 (18 decimals)
    // Expected: (5e18 * 1e9) / 2e18 = 5e27 / 2e18 = 2.5e9 (9 decimals)
    // Rounds down to 2e9
    assertEq(convertibleAmount, 2e9, "Convertible amount does not equal 2e9");

When Reviewing Code

  • Verify decimal scale consistency across operations
  • Check for potential overflow/underflow
  • Validate that the order of operations preserves precision
  • Confirm rounding behavior matches requirements
  • Look for off-by-one errors due to rounding

Common Patterns - Math

Converting between scales:

// Convert from 18 decimals to 9 decimals
value9 = value18 / 1e9;

// Convert from 9 decimals to 18 decimals
value18 = value9 * 1e9;

Price calculations:

// amount (18 dec) * price (18 dec) / 1e18 = value (18 dec)
value = amount.mulDiv(price, 1e18);

Proportion calculations:

// part (X dec) * total (Y dec) / denominator (Z dec) = result (X+Y-Z dec)
result = part.mulDiv(total, denominator);

Always think through the decimal arithmetic step-by-step and make the reasoning explicit in your responses.

Tools

Code Reviews with CodeRabbit

Use CodeRabbit through the Repo Workflow section, which defines timing, base verification, and the standard review command. Run coderabbit -h for CLI details if the installed command syntax is unclear.

IMPORTANT: When running CodeRabbit to review code changes, don't run it more than 3 times in a given set of changes.

Linting

For detailed linter note resolution guidance (deployed vs in-development contracts, suppression templates, common fixes), use the /lint-fix skill.

Check for linting issues:

pnpm run lint:check # Check all linting rules
forge build         # Output forge-lint notes

The following commands show detailed linting rule breakdown:

# List all linting rules with notes/warnings
forge lint 2>&1 | grep -E "^warning\[|^note\[" | grep -v "src/test" | sed 's/^.*\[\([^]]*\)\].*/\1/' | sort | uniq | cat

# Show linting rules grouped by file
forge lint 2>&1 | sed -n '
/^note\[/ { s/^note\[\([^]]*\)\].*/NOTE:\1/p; h; d; }
/^warning\[/ { s/^warning\[\([^]]*\)\].*/WARNING:\1/p; h; d; }
/^error\[/ { s/^error\[\([^]]*\)\].*/ERROR:\1/p; h; d; }
/^  --> / { s/^  --> \([^:]*\):.*/\1/p; }
' | awk '
/^(NOTE|WARNING|ERROR):/ { rule = $0; next }
rule { print rule "|" $0 }
' | sort -u | awk -F'|' '
{
    if ($1 != last) {
        if (last) print "";
        print $1;
        last = $1
    }
    print "  " $2
}'

Git Worktrees

Git worktrees allow multiple branches to be checked out simultaneously without stashing or committing changes. This is useful for:

  • Working on multiple features concurrently
  • Testing code on different branches
  • Reviewing PRs while preserving current work

Common commands:

  • git worktree list - Show all worktrees
  • git worktree add <path> <branch> - Create new worktree for a branch
  • git worktree remove <path> - Delete a worktree