Skip to content
Open
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
2 changes: 1 addition & 1 deletion .claude/skills/organize-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ If the file uses a custom section name that is clear and descriptive, keep it. O
**Do not reorganize** if any of these conditions hold:

- The file is **not** a `*.t.sol` file under `contracts/tests/`.
- The file is a **production contract** (`contracts/` outside `tests/`), a deploy script, or a Hardhat JS/TS test.
- The file is a **production contract** (`contracts/` outside `tests/`), a deploy script, or a legacy JS/TS test.
- The file contains inline assembly (`assembly { ... }`) interleaved with state variable declarations — moving variables could change storage layout.
- The file is **auto-generated** or clearly marked as such.
- The reorganization would produce a diff affecting **more than 60%** of the file's lines — this makes review impractical. In this case, do the smallest safe subset or nothing.
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/verify-deployment-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ details block, and a confidence (High/Med/Low). Absence of evidence is ⚠️/
(nothing to check); ❌ args differ (show the diff).

**5 — Governance proposal matches the deploy script**
- Read the on-chain proposal directly via ethers — do NOT use `npx hardhat proposal --id`,
- Read the on-chain proposal directly via ethers — do NOT use `pnpm ops proposal --id`,
it parses the id as a float and overflows on real (77-digit) proposalIds. Use the
GovernorSix (`addresses.mainnet.GovernorSix`) ABI with the id as a `BigNumber`:
`g.state(id)` and `g.getActions(id) -> (targets, values, signatures, calldatas)`
Expand Down
2 changes: 1 addition & 1 deletion .codex/skills/organize-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ If the file uses a custom section name that is clear and descriptive, keep it. O
**Do not reorganize** if any of these conditions hold:

- The file is **not** a `*.t.sol` file under `contracts/tests/`.
- The file is a **production contract** (`contracts/` outside `tests/`), a deploy script, or a Hardhat JS/TS test.
- The file is a **production contract** (`contracts/` outside `tests/`), a deploy script, or a legacy JS/TS test.
- The file contains inline assembly (`assembly { ... }`) interleaved with state variable declarations — moving variables could change storage layout.
- The file is **auto-generated** or clearly marked as such.
- The reorganization would produce a diff affecting **more than 60%** of the file's lines — this makes review impractical. In this case, do the smallest safe subset or nothing.
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ contracts/artifacts/
contracts/deployments/localhost*
contracts/deployments/ganache_*
contracts/deployments/fork_*
contracts/deployments/hardhat*

contracts/coverage/
contracts/coverage.json
contracts/build/*
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ A skill is a set of local instructions to follow that is stored in a `SKILL.md`
- skill-creator: Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Codex's capabilities with specialized knowledge, workflows, or tool integrations. (file: /Users/clement/.codex/skills/.system/skill-creator/SKILL.md)
- skill-installer: Install Codex skills into `$CODEX_HOME/skills` from a curated list or a GitHub repo path. Use when a user asks to list installable skills, install a curated skill, or install a skill from another repo (including private repos). (file: /Users/clement/.codex/skills/.system/skill-installer/SKILL.md)
- commit: Handle git commits with auto-staging, targeted pre-commit formatting, and Conventional Commit messages. Use when the user asks to commit changes, save changes in git, or similar commit requests. (file: /Users/clement/Documents/Travail/Origin/2-SC/origin-dollar-foundry/.codex/skills/commit/SKILL.md)
- unit-test: Generate Foundry unit tests for a contract using this repository's conventions, structure, and naming. Use when the user asks for unit tests, Foundry tests, concrete tests, fuzz tests, or to port Hardhat tests into Foundry unit tests. (file: /Users/clement/Documents/Travail/Origin/2-SC/origin-dollar-foundry/.codex/skills/unit-test/SKILL.md)
- fork-test: Generate Foundry fork tests for contracts that need real on-chain integration coverage. Use when the user asks for fork tests, mainnet or chain fork coverage, integration tests against live protocol state, or to port Hardhat fork tests into Foundry. (file: /Users/clement/Documents/Travail/Origin/2-SC/origin-dollar-foundry/.codex/skills/fork-test/SKILL.md)
- unit-test: Generate Foundry unit tests for a contract using this repository's conventions, structure, and naming. Use when the user asks for unit tests, Foundry tests, concrete tests, fuzz tests, or to port legacy JavaScript tests into Foundry unit tests. (file: /Users/clement/Documents/Travail/Origin/2-SC/origin-dollar-foundry/.codex/skills/unit-test/SKILL.md)
- fork-test: Generate Foundry fork tests for contracts that need real on-chain integration coverage. Use when the user asks for fork tests, mainnet or chain fork coverage, integration tests against live protocol state, or to port legacy JavaScript fork tests into Foundry. (file: /Users/clement/Documents/Travail/Origin/2-SC/origin-dollar-foundry/.codex/skills/fork-test/SKILL.md)

### How to use skills
- Discovery: The list above is the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
Expand Down
34 changes: 17 additions & 17 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Deployed on Ethereum Mainnet, Base, Arbitrum, Sonic, Plume, Hoodi, and HyperEVM.

## Toolchain

**Foundry is the contract toolchain**: `forge` (driven by the `Makefile`) builds the contracts, runs the contract test suite, executes deployments, and generates the `@origin/defi` npm ABI package. Hardhat remains only for the ops task CLI (`tasks/*.js`, wired up in `hardhat.config.js`) and the local fork node. The committed `deployments/` descriptors are the address/ABI registry consumed by both toolchains; there are no Hardhat deployment scripts.
**Foundry is the contract toolchain**: `forge` (driven by the `Makefile`) builds the contracts, runs the contract test suite, executes deployments, and generates the `@origin/defi` npm ABI package. `pnpm ops` runs operational commands and Anvil provides local forks. The committed `deployments/` descriptors are the shared address/ABI registry.

## Setup

Expand All @@ -23,7 +23,7 @@ cp dev.env .env # Set MAINNET_PROVIDER_URL to an Alchemy/Infura endpoin
make install # foundryup (v1.8.1), forge soldeer install, install-deps.sh, pnpm i
```

Key `.env` variables: `MAINNET_PROVIDER_URL` (required), `BASE_PROVIDER_URL`, `ARBITRUM_PROVIDER_URL`, `SONIC_PROVIDER_URL`, `HYPEREVM_PROVIDER_URL`, `BEACON_PROVIDER_URL` (beacon-proof fork tests), and optional `FORK_BLOCK_NUMBER_<CHAIN>` pins for Foundry fork tests (unset = latest block; refresh with `make update-fork-blocks`). The Hardhat task CLI resolves its mainnet RPC from `MAINNET_PROVIDER_URL` as well (legacy fallback: `PROVIDER_URL`).
Key `.env` variables: `MAINNET_PROVIDER_URL` (required), `BASE_PROVIDER_URL`, `ARBITRUM_PROVIDER_URL`, `SONIC_PROVIDER_URL`, `HYPEREVM_PROVIDER_URL`, `BEACON_PROVIDER_URL` (beacon-proof fork tests), and optional `FORK_BLOCK_NUMBER_<CHAIN>` pins for Foundry fork tests (unset = latest block; refresh with `make update-fork-blocks`). The ops CLI also resolves Ethereum from `MAINNET_PROVIDER_URL`.

Real deployments additionally need `DEPLOYER_ADDRESS` and the encrypted `deployerKey` keystore (`cast wallet import deployerKey --interactive`).

Expand All @@ -33,7 +33,7 @@ Real deployments additionally need `DEPLOYER_ADDRESS` and the encrypted `deploye
as an *optional peer dependency* so that CI and external contributors can install
this repo without GitHub Packages credentials.

**Nothing local needs it.** `hardhat`, `tsx tasks/run.ts <action>` and the tests
**Nothing local needs it.** `pnpm ops`, `tsx tasks/run.ts <action>` and the tests
all load and run without it. Only the runner image does: `runner.ts` (container
entrypoint) imports it statically, and `tasks/lib/signer.ts` `require()`s it
lazily for the Postgres nonce queue, which is gated on `DATABASE_URL` — unset
Expand All @@ -48,7 +48,7 @@ Keep it that way. Two regressions to avoid:
fail with `Cannot find module '@oplabs/talos-client'`.
- Do not import it from `tasks/lib/network.ts`. That used to drag the
requirement up through `utils/resolvers.js` → `utils/morpho.js` →
`tasks/tasks.js` → `hardhat.config.js` and broke the Hardhat ops CLI for
`tasks/tasks.js` and broke the ops CLI for
installs without GitHub Packages auth. #2954 replaced it with local
`CHAIN_IDS` / `RPC_ENV_VARS` maps.

Expand Down Expand Up @@ -87,16 +87,16 @@ The `make test-*` targets rebuild the relevant trees first. Bare `forge test` do

Fork and smoke tests fork the chain from the `*_PROVIDER_URL` variables and pin blocks via `FORK_BLOCK_NUMBER_<CHAIN>`. Test conventions (unit vs fork vs smoke, concrete vs fuzz, interface-only testing) are documented in `tests/README.md` — read it before writing tests.

### Hardhat task tests (`tasks/test/`)
### Ops task tests (`tasks/test/`)

`pnpm test:tasks` validates the ops task implementation. Smart-contract tests belong exclusively in the Foundry suite under `tests/`.

### Hardhat (ops task CLI only)
### Ops CLI and local forks
```bash
npx hardhat <task> --network mainnet # ops tasks from tasks/*.js
pnpm run node # Hardhat fork node, for running tasks against a local fork
pnpm node:anvil # anvil mainnet fork (used by make deploy-local); node:anvil:base etc.
pnpm action <name> # Talos actions runner (tsx tasks/run.ts — viem, not Hardhat)
pnpm ops <task> --network mainnet # ops tasks from tasks/*.js
pnpm run node # Anvil mainnet fork
pnpm node:base # equivalent per-network fork commands
pnpm action <name> # Talos actions runner
```

### Useful Options
Expand Down Expand Up @@ -214,7 +214,7 @@ tests/ # Foundry suite (canonical)
invariant/
mocks/
utils/ # Addresses.sol, shared helpers
tasks/test/ # Mocha tests for Hardhat ops tasks only
tasks/test/ # Mocha tests for standalone ops tasks
```

Layout mirrors `<type>/<chain>/<area>/<Contract>/{concrete,fuzz}/<Behaviour>.t.sol`, with a per-contract `shared/Shared.t.sol` base. Tests interact with contracts **through interfaces** (`IVault`, `IOToken`, `IWOToken`, `IProxy`) and deploy implementations with `vm.deployCode` — never import a concrete contract into a test file; it drags the whole dependency tree into the test's compilation unit and destroys build caching. Full rules and gotchas: `tests/README.md`.
Expand All @@ -231,15 +231,15 @@ make deploy-mainnet # real deploy + Etherscan verify; also: deploy-ba
make deploy-local # against a running pnpm node:anvil
```

For upgrades, call `_assertStorageSafe(type(X).name)` before `new X()` — see Storage Layout Checks below. After a real deploy the make target regenerates the Hardhat-format descriptors in `deployments/<network>/` (see the Talos section at the bottom). Framework internals: `scripts/deploy/README.md` and `scripts/deploy/ARCHITECTURE.md`.
For upgrades, call `_assertStorageSafe(type(X).name)` before `new X()` — see Storage Layout Checks below. After a real deploy the make target regenerates the descriptors in `deployments/<network>/` (see the Talos section at the bottom). Framework internals: `scripts/deploy/README.md` and `scripts/deploy/ARCHITECTURE.md`.

Hardhat deployment scripts are no longer part of the repository. Do not recreate a `deploy/` tree; use the Foundry framework above.
JavaScript deployment scripts are no longer part of the repository. Do not recreate a `deploy/` tree; use the Foundry framework above.

## Storage Layout Checks

**The baseline is the descriptor.** `deployments/<network>/<Name>.json` holds
`{address, abi, storageLayout}` — the layout is what is deployed at that address
right now, written by `scripts/create-hardhat-format-descriptors.js` in the same
right now, written by `scripts/create-deployment-descriptors.js` in the same
step that records the address, so the write side cannot silently stop.

**The gate runs per deployed contract, before broadcast.** Call
Expand Down Expand Up @@ -311,12 +311,12 @@ tests nothing.
## Roles & Access Control

Four key roles used across all contracts:
- **Deployer** - deploys contracts. Foundry deploys sign with the `deployerKey` keystore + `DEPLOYER_ADDRESS`; the legacy Hardhat CLI reads `DEPLOYER_PK`
- **Deployer** - deploys contracts. Foundry deploys sign with the `deployerKey` keystore + `DEPLOYER_ADDRESS`; the ops CLI reads `DEPLOYER_PK`
- **Governor** - timelock-controlled governance address
- **Strategist** - multisig for day-to-day operations
- **Guardian** - emergency pause capability

Foundry fork and smoke tests impersonate these with `vm.prank`. For Hardhat tasks against a running fork node, set `IMPERSONATE=0x...` to run as any account.
Foundry fork and smoke tests impersonate these with `vm.prank`. For ops tasks against a running Anvil fork, set `IMPERSONATE=0x...` to run as any account.

## Contract Verification

Expand All @@ -338,7 +338,7 @@ log("something happened");

The Talos ops automation (`contracts/tasks/actions/**` — harvest, rebases, `doAccounting`, validator ops, cross-chain relays, etc.) resolves the contracts it operates on from the descriptors in `contracts/deployments/<network>/<Name>.json` (addresses) and pinned entries in `contracts/utils/addresses.js` / action-local `*_BY_CHAIN_ID` maps. Foundry's own broadcast state lives in `contracts/build/deployments-<chainId>.json`.

The `make deploy-mainnet|base|hyperevm` targets close most of this gap automatically: after the broadcast they run `scripts/create-hardhat-format-descriptors.js`, which rewrites `deployments/<network>/<Name>.json` for whatever was just deployed (even when verification fails; `make deploy-local` deliberately skips it). What is **not** automatic — and can silently point a live Talos action at a stale address or ABI:
The `make deploy-mainnet|base|hyperevm` targets close most of this gap automatically: after the broadcast they run `scripts/create-deployment-descriptors.js`, which rewrites `deployments/<network>/<Name>.json` for whatever was just deployed (even when verification fails; `make deploy-local` deliberately skips it). What is **not** automatic — and can silently point a live Talos action at a stale address or ABI:
- a raw `forge script` run outside the make targets never updates descriptors;
- pinned addresses in `utils/addresses.js` and action-local `*_BY_CHAIN_ID` maps;
- the curated `abi/<Interface>.json` files when a callable interface changes.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ The `origin-dollar` project is a repo that hosts the smart contracts of some Ori

### Eth Node

The smart contracts and their associated code are located in the `<project-root>/contracts` directory. [Foundry](https://book.getfoundry.sh/) builds, tests, and deploys the contracts. [Hardhat](https://hardhat.org/) remains available for operational tasks and the local forked EVM node.
The smart contracts and their associated code are located in the `<project-root>/contracts` directory. [Foundry](https://book.getfoundry.sh/) builds, tests, and deploys the contracts. The standalone `pnpm ops` CLI runs operational commands and Anvil provides the local forked EVM node.

A variety of Hardhat [tasks](https://hardhat.org/guides/create-task.html) are available to interact with the contracts. Additional information can be found by running `npx hardhat` from the `contracts/` directory.
A variety of operational commands are available to interact with the contracts. Run `pnpm ops help` from the `contracts/` directory to list them.
<br/><br/>

---

## Running the node

The dapp interacts with many 3rd party contracts (Uniswap, Curve, Sushiswap) and it would be too cumbersome to initialize all those contracts in a fresh node environment and set them to a state that mimics the Mainnet. For that reason we are using Hardhat's forked mode. By setting the `BLOCK_NUMBER` environment variable, the node will download part of the mainnet state that it requires to fulfill the requests. It is less reliable since the node isn't as stable in forked mode (and sometimes requires restarts), but mimicking the mainnet is a huge benefit. We used to develop with fresh state node, but the behavior discrepancies between fresh node and mainnet have started to become too large. For that reason, we have deprecated the fresh state development.
The dapp interacts with many third-party contracts (Uniswap, Curve, Sushiswap), so local development uses an Anvil fork rather than recreating Mainnet state. Set `BLOCK_NUMBER` to pin the fork; unset means latest.
<br/><br/>

Rename `contracts/dev.env` to `.env` and set `PROVIDER_URL` to a valid one (sign up for an Alchemy or Infura account, create an API key, and use the URL they provide). To pin the fork to a specific mainnet block, set `BLOCK_NUMBER`. Open a separate terminal to run the Hardhat node.
Rename `contracts/dev.env` to `.env` and set `MAINNET_PROVIDER_URL` to a valid endpoint. To pin the fork to a specific mainnet block, set `BLOCK_NUMBER`. Open a separate terminal to run Anvil.
<br/><br/>

Run the node:
Expand Down
5 changes: 2 additions & 3 deletions brownie/runlogs/2023_12_strategist.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,8 @@ def main():
)
)

# below line works on hardhat's default node (brownie console --network mainnet-fork)
# but not on our default node running in a separate terminal and attaching
# to it: (brownie console --network hardhat)
# The line below worked with an in-process fork but not when Brownie attached
# to a node running in a separate terminal.
#print(txs[-1].call_trace(True))

# After
Expand Down
2 changes: 1 addition & 1 deletion brownie/runlogs/2026_01_strategist.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def main():
)

# use the following command to get cluster info:
# pnpm hardhat getClusterInfo --operatorids 752,753,754,755 --network mainnet --owner 0x4685dB8bF2Df743c861d71E6cFb5347222992076
# pnpm ops getClusterInfo --operatorids 752,753,754,755 --network mainnet --owner 0x4685dB8bF2Df743c861d71E6cFb5347222992076

txs.append(
native_staking_2_strat.depositSSV(
Expand Down
4 changes: 2 additions & 2 deletions brownie/runlogs/2026_02_strategist.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def main():
)

# use the following command to get cluster info:
# pnpm hardhat getClusterInfo --operatorids 752,753,754,755 --network mainnet --owner 0x4685dB8bF2Df743c861d71E6cFb5347222992076
# pnpm ops getClusterInfo --operatorids 752,753,754,755 --network mainnet --owner 0x4685dB8bF2Df743c861d71E6cFb5347222992076

txs.append(
native_staking_2_strat.depositSSV(
Expand All @@ -65,7 +65,7 @@ def main():
)

# use the following command to get cluster info:
# pnpm hardhat getClusterInfo --operatorids 338,339,340,341 --network mainnet --owner 0xE98538A0e8C2871C2482e1Be8cC6bd9F8E8fFD63
# pnpm ops getClusterInfo --operatorids 338,339,340,341 --network mainnet --owner 0xE98538A0e8C2871C2482e1Be8cC6bd9F8E8fFD63

txs.append(
native_staking_3_strat.depositSSV(
Expand Down
4 changes: 2 additions & 2 deletions contracts/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ DEPLOY_BASE := --account deployerKey --sender $(DEPLOYER_ADDRESS) --broadcas
DEPLOY_BUILD := contracts/ scripts/deploy/

# Rewrites deployments/<network>/<Name>.json for whatever the broadcast just
# deployed, so Talos crons and the hardhat task CLI keep resolving live
# deployed, so Talos crons and the standalone ops CLI keep resolving live
# addresses. Deliberately NOT wired into deploy-local: that target also
# broadcasts, but against a throwaway node.
#
Expand All @@ -22,7 +22,7 @@ DEPLOY_BUILD := contracts/ scripts/deploy/
# the descriptor update and leave live crons pointing at the old contract — the
# exact silent drift this step exists to prevent. A descriptor failure still
# fails the target (`|| exit 1`); only forge's own exit code is deferred.
DESCRIPTORS := node scripts/create-hardhat-format-descriptors.js
DESCRIPTORS := node scripts/create-deployment-descriptors.js

# Clearing the chain's broadcast dir first is what makes "a run file exists"
# mean "this run deployed something". forge leaves the PREVIOUS run-latest.json
Expand Down
Loading
Loading