Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
46d991a
fix(clear-signing): format native currency amounts correctly (#2323)
gvladika Sep 7, 2026
46d18f4
fix(ci): rename clear-signing sync branch to open a fresh upstream PR…
gvladika Sep 7, 2026
41b2f5b
feat(immutables): per-immutable expectation registry + gate (EXSC-869…
0xDEnYO Sep 7, 2026
db0f814
feat(deploy): verify .foundry-version on the deploy path (EXSC-932) (…
0xDEnYO Sep 7, 2026
95fdb62
feat(deploy): evaluate the production deploy gate in the proposal fun…
0xDEnYO Sep 7, 2026
c652d30
fix(rpc): stop auto-promoting new endpoints, add per-chain failover (…
0xDEnYO Sep 8, 2026
fef844a
fix(test): skip selector FFI cases without out/ artifacts (EXSC-946) …
gvladika Sep 8, 2026
0f83f13
feat(deploy): gate the zk, direct-build and Tron toolchains (EXSC-933…
0xDEnYO Sep 8, 2026
ade6a6b
fix(ci): diff changed files against the merge base in PR gates (EXSC-…
0xDEnYO Sep 8, 2026
b2daeaf
feat(deploy): record branch, scoped dirty tree and actor on deploymen…
0xDEnYO Sep 8, 2026
7c3bbd2
fix(health-check): safe-config asserts the Safe owner set both ways (…
0xDEnYO Sep 8, 2026
b0d050b
fix(security): update dependencies (#2346)
aikido-autofix[bot] Sep 9, 2026
00aa6cb
feat(deploy): bound the deployer key's power to a documented inventor…
0xDEnYO Sep 9, 2026
d92a610
feat(deploy): gate Safe deployment on config, not on its own argument…
0xDEnYO Sep 9, 2026
75b71eb
fix(tron): pre-flight the fee limit on the three direct-EOA send path…
0xDEnYO Sep 9, 2026
d2347f8
feat(codehash): the sign-time gate's decision modules (EXSC-906) (#2329)
0xDEnYO Sep 9, 2026
add9f2b
fix(ci): corroborate a stale cron verdict before alerting (EXSC-939) …
0xDEnYO Sep 9, 2026
d897455
feat(safe): refuse a delegatecall proposal on the operation field alo…
0xDEnYO Sep 9, 2026
c9a89c0
fix: whitelist Composer VM proxy on Plasma diamond allowlist (#2352)
H3xept Sep 9, 2026
73f2ace
feat(safe): check ledger with per-anchor results and a rolled-up repo…
0xDEnYO Sep 9, 2026
e1f502c
fix(safe): resolve repo root without Bun-only import.meta.dir (#2358)
gvladika Sep 10, 2026
c4648f0
feat(safe): pinned expected state — grade the target state against or…
0xDEnYO Sep 10, 2026
3f72408
feat(codehash): the attestation key and the audit bridge (EXSC-952) (…
0xDEnYO Sep 10, 2026
c7025ef
feat(safe): RPC quorum for integrity reads (EXSC-870) (#2343)
0xDEnYO Sep 10, 2026
18e5e23
feat(safe): resolve calldata addresses against the deployment record …
0xDEnYO Sep 10, 2026
3323744
feat(timelock): cancel-decision matrix for the pre-execute check (EXS…
0xDEnYO Sep 10, 2026
18be7f5
feat(safe): tell a signer whether a proposal would revert if executed…
0xDEnYO Sep 10, 2026
392806d
fix(safe): preview the hash mode Ledger screens, and stop routing eve…
0xDEnYO Sep 10, 2026
442f914
feat(codehash): bridge an audit to the build that was deployed (EXSC-…
0xDEnYO Sep 10, 2026
efe2f20
feat(script): gate the bash sendOrPropose direct-broadcast route (EXS…
0xDEnYO Sep 10, 2026
157e10a
feat(safe): confirm-time integrity assertions on a pending proposal (…
0xDEnYO Sep 10, 2026
8f69909
feat(safe): route every Safe proposal through one blessed wrapper (EX…
0xDEnYO Sep 10, 2026
b7fc074
feat(deploy): record the deployed codehash, and assert commit presenc…
0xDEnYO Sep 10, 2026
6eabbcf
fix(security): redact RPC endpoints in script logs (EXSC-965) (#2365)
0xDEnYO Sep 10, 2026
06f79f8
feat(audit): gate on audited content instead of commit provenance (EX…
0xDEnYO Sep 10, 2026
412dfa9
fix(test): withhold credentials from spawned children by setting, not…
0xDEnYO Sep 10, 2026
f9d075f
feat(safe): make the ticket channel uniform across the funnels that c…
0xDEnYO Sep 10, 2026
426a1f1
feat(codehash): grade a codehash match by who built what it matched (…
0xDEnYO Sep 10, 2026
3b592f6
feat(immutables): annotate 26 config-backed immutable getters after a…
0xDEnYO Sep 10, 2026
4d8b4eb
feat(codehash): false-refusal budget in shadow mode (EXSC-977) (#2366)
0xDEnYO Sep 11, 2026
4a97de8
feat(tests): withhold endpoints, provider keys and webhooks from spaw…
0xDEnYO Sep 11, 2026
78533f7
refactor(codehash): price the masked immutables per slot instead of r…
0xDEnYO Sep 11, 2026
faffe1b
docs(safe): fallback runbook for the pinned escape commit (EXSC-976) …
0xDEnYO Sep 11, 2026
390a513
feat(safe): re-land the signer-display routes with #2349's open holes…
0xDEnYO Sep 11, 2026
ab7e5f3
feat(safe): make the check ledger the sign-time composition point (EX…
0xDEnYO Sep 11, 2026
73cad8d
docs(facets): acknowledge unrefunded swap surplus (EXSC-927) (#2373)
gvladika Sep 11, 2026
ba03e2b
fix(healthcheck): honour allowToDeployWithZeroAddress in the immutabl…
0xDEnYO Sep 11, 2026
c97e841
chore: sync upstream contracts@ba03e2bd
gvladika Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions .agents/commands/add-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ This command processes a PDF audit report and automatically:
- **ALWAYS** warn user to verify by clicking the URL
- Display as clickable link in summary output
- If commit hash is "n/a", skip URL generation
- **Post-remediation ([CONV:AUDIT-PIN])**:
- `auditCommitHash` = scope commit **A** from the report (what was sent).
- If findings were fixed afterwards, also set `finalCommitHash` = post-remediation
commit **D** the auditor signed off on (same report, addendum, or confirmation).
- The content gate pins to `finalCommitHash` when present, else `auditCommitHash`.
- Ask the user for D when the PDF only names A and remediations happened.
- Always a **new** log entry — never edit a pre-remediation row. Same PDF path may be reused.

### QA checklist (audit-log PRs)

When reviewing a PR that only (or mainly) adds audit log/report entries, verify and
**quote evidence** (page/snippet/sha) — do not rubber-stamp:

1. Report scope commit equals `auditCommitHash` (or documented `n/a` reason).
2. If remediations exist: report/addendum/`Fixed in #` support `finalCommitHash` = D.
3. If the report only has A and finding fixes B/C/D: `finalCommitHash` is the tree that
contains all fixes (not “latest by clock” unless that is also the signed-off tree).
4. Fail closed if remediations happened and `finalCommitHash` is missing from the log.

## Execution Steps

Expand Down
23 changes: 13 additions & 10 deletions .agents/commands/interact-tron.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ usage: /interact-tron <call|send|address|code> ...

Tron has its own address format, RPC surface, and resource model, so `cast` does not work against it (`eth_getTransactionCount` and `debug_traceTransaction` aren't supported on TronGrid public endpoints). All read/write contract interaction on Tron goes through `troncast` (`script/troncast/`), a Cast-like TronWeb wrapper — never hand-roll a TronWeb script for something `troncast` already covers.

**Repo: stay in `contracts`.** Unlike `/deploy-contract-tron`, this skill needs no `contracts-tron` fork checkout — `troncast` and the deployed contract's on-chain state are both reachable from the normal `contracts` session. See `docs/TronFork.md` for why the fork exists at all and when it *does* matter (deploys and any change to `LibAsset`/`WithdrawablePeriphery`).
**Repo: stay in `contracts`.** Unlike `/deploy-contract-tron`, this skill needs no `contracts-tron` fork checkout — `troncast` and the deployed contract's on-chain state are both reachable from the normal `contracts` session. See `docs/TronFork.md` for why the fork exists at all and when it _does_ matter (deploys and any change to `LibAsset`/`WithdrawablePeriphery`).

## When to reach for this vs other skills

| Situation | Skill |
|---|---|
| Deploying a new contract to Tron | `/deploy-contract-tron` |
| Read a Tron contract's state, or send a simple write (transfer, approve, single admin call) | **this skill** |
| Move gas (TRX) from our own deployer wallet | this skill (`troncast send ... --value`) |
| Situation | Skill |
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Deploying a new contract to Tron | `/deploy-contract-tron` |
| Read a Tron contract's state, or send a simple write (transfer, approve, single admin call) | **this skill** |
| Move gas (TRX) from our own deployer wallet | this skill (`troncast send ... --value`) |
| Production governance change (whitelist sync, ownership transfer, anything needing the Timelock) | route to `propose-to-safe-tron.ts` conventions — this needs Safe-quorum + `scheduleBatch`/`executeBatch` sequencing, not a one-off `troncast send`. Flag it and get explicit direction rather than improvising. |
| Analyzing a past Tron transaction/trace | `/analyze-tx` (already has a Tron-specific section) |
| Analyzing a past Tron transaction/trace | `/analyze-tx` (already has a Tron-specific section) |

## Address handling ([CONV:TRON-ADDRESS], see `202-tron-scripts.md`)

Expand All @@ -29,7 +29,7 @@ bun troncast address to-hex TLPh66vQ2QMb64rG3WEBV5qnAhefh2kcdw # -> 0x7252af
bun troncast address to-base58 0x7252afce04856eaac8f8a8beb5ae29621a1ca49b # -> TLPh66...
```

`troncast` itself accepts either format for `call`/`send`/`code` target addresses, so conversion is only needed when the user needs the *other* format back, or when composing calldata by hand.
`troncast` itself accepts either format for `call`/`send`/`code` target addresses, so conversion is only needed when the user needs the _other_ format back, or when composing calldata by hand.

**RPC env var required even for offline conversions.** `troncast address to-hex`/`to-base58` always requires `ETH_NODE_URI_TRON` in `.env` — the codec hardcodes the mainnet TronWeb instance, so `ETH_NODE_URI_TRONSHASTA` does not satisfy it and `address` takes no `--env` flag — even though the conversion itself is a pure offline codec operation; the CLI aborts without it. If it's not set, prefix a dummy value for that one invocation rather than editing `.env`.

Expand Down Expand Up @@ -77,7 +77,9 @@ Key flags: `--value` (`0.1tron` / `100000sun` / raw sun), `--fee-limit` (TRX cap

## Energy & fee-limit awareness

Tron pays for execution in Energy (≈ gas), not just bandwidth. `--fee-limit` is a TRX ceiling on what the transaction may consume if it must buy Energy — set it deliberately for anything beyond a cheap call rather than trusting the 1000 TRX default. If a write repeatedly fails with an out-of-energy-style error, that's a fee-limit or energy-limit problem, not a revert — raise `--fee-limit`/`--energy-limit` and retry rather than assuming the call itself is wrong.
Tron pays for execution in Energy (≈ gas), not just bandwidth. `--fee-limit` is a TRX ceiling on what the transaction may consume if it must buy Energy — set it deliberately for anything beyond a cheap call rather than trusting the 1000 TRX default.

`troncast send` pre-flights that ceiling on every contract call: it estimates the Energy, prices it, and **refuses before broadcasting** when the estimate costs more than `--fee-limit` covers, or when the estimate cannot be obtained at all. So an out-of-energy problem now surfaces as a refusal naming the required `--fee-limit`, not as a half-applied transaction. A failed _estimate_ usually means the call would revert — investigate that before reaching for a bigger limit. `ALLOW_GAS_ESTIMATE_FALLBACK=<network>` broadcasts anyway; scope it to the network, because `true` disables the guard for every network in the run. Native TRX transfers are exempt — they run no VM code.

Ongoing higher-volume Tron operations (the Timelock's `scheduleBatch`/`executeBatch`) run off **delegated** Energy from staked TRX on `deployerWallet`/`devWallet` rather than burning TRX per call — that delegation is a separate, human-arranged concern (ping Max) and out of scope for a one-off `troncast` interaction.

Expand All @@ -89,6 +91,7 @@ No ABI auto-fetch, no contract verification, no wallet management, limited gas e

- `cast` used against a Tron network → will fail on RPC methods Tron doesn't support; switch to `troncast`.
- Function call reverts with no clear reason → dry-run first (`--dry-run`), then check the target address is in the form `troncast` expects (base58 or 0x-hex, not a malformed hybrid).
- Transaction fails after broadcast with an energy-related error → raise `--fee-limit`/`--energy-limit`, don't assume the calldata was wrong.
- `troncast send` refuses with `exceeds the fee limit` → re-run with the `--fee-limit` the message names; the call was never broadcast.
- `troncast send` refuses with `Energy estimation failed` → the call would most likely revert; diagnose that rather than raising the limit. The same message is used when TronGrid returns 429 / the RPC dies after the estimator's retries — wait and re-run, or set `ALLOW_GAS_ESTIMATE_FALLBACK=tron` if the send (including a break-glass pause) must proceed without a figure.
- Request turns out to need Safe/Timelock sequencing (multi-step, quorum, or anything touching production governance) → stop and hand off; this skill is for direct one-off calls only.
- Command fails with `proto is not defined` → known TronWeb/Bun compatibility hiccup; retry the same command once before investigating further.
7 changes: 5 additions & 2 deletions .agents/commands/multisig-rollout.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ Run from the repo root. Check and report (don't fix silently) the lifecycle prer
- `SAFE_PROPOSAL_TICKET` exported with the Linear issue this rollout belongs to,
e.g. `export SAFE_PROPOSAL_TICKET="EXSC-812"` (a full issue URL works too).
**Mandatory** — no proposal is created without it, so an unset value fails every
network in Phase 2. Export it **before Phase 2**; only `propose-to-safe.ts` has a
`--ticket` flag, every other funnel reads this variable.
network in Phase 2. Export it **before Phase 2** — every route this playbook
takes into `propose-to-safe.ts` is a bash one (`diamondUpdateFacet` /
`diamondUpdatePeriphery` for propose-only, the `sendOrPropose` chokepoint
elsewhere) and none of them passes `--ticket`, so the variable is the channel
here.
- `SAFE_PROPOSAL_REASON` exported with a one-line rationale for this rollout, e.g.
`export SAFE_PROPOSAL_REASON="roll out AcrossFacetV4 v1.2.0 (EXSC-812)"`. Every
proposal stored during the run records it, and `confirm-safe-tx` shows it to the
Expand Down
2 changes: 1 addition & 1 deletion .agents/rules/004-config-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Decide per key whether `address(0)` is a valid value, and shape the config accor
- **Required (`address(0)` is invalid).** The parameter must be a real, code-bearing contract on every supported chain (e.g. `hop`, `htlcRegistry`, `airlift`). **List every network explicitly** and read it with the strict overload `_getConfigContractAddress(path, key)`, which reverts on zero or non-contract. A missing or zero entry must fail the deploy loudly.
- **Optional (`address(0)` is the normal value).** The parameter is meaningful on only a few chains and legitimately zero everywhere else (e.g. Tempo's `tipFeeManager` / `pathUsd`, non-zero only on Tempo). **List only the non-zero networks** — do not enumerate dozens of `0x0000…0000` entries. Read these with `_getOptionalConfigContractAddress(path, key)`, which returns `address(0)` when the key is absent, so a chain omitted from the map deploys with the zero default instead of reverting.

Rationale: `_getConfigContractAddress` reverts on a **missing** key (an absent `readAddress` decodes empty bytes), so an all-zero-but-listed map is not just noise — combined with the fact that `/add-network` does not touch bridge configs, it turns the first deploy on any future chain into a revert on a value that was always meant to be zero. Sparse config for optional keys removes that footgun; explicit listing for required keys keeps a genuinely missing address failing closed. (The bash `checkDeployRequirements` layer already tolerates a missing key when `allowToDeployWithZeroAddress` is `"true"` — set it so for optional keys.)
Rationale: `_getConfigContractAddress` reverts on a **missing** key (an absent `readAddress` decodes empty bytes), so an all-zero-but-listed map is not just noise — combined with the fact that `/add-network` does not touch bridge configs, it turns the first deploy on any future chain into a revert on a value that was always meant to be zero. Sparse config for optional keys removes that footgun; explicit listing for required keys keeps a genuinely missing address failing closed. (The bash `checkDeployRequirements` layer already tolerates a missing key when `allowToDeployWithZeroAddress` is `"true"` — set it so for optional keys.) That flag is also what lets `immutable-bindings-match-config` verify an optional binding rather than skip it: on a chain the map omits, it asserts the deployed getter reads `address(0)`, so padding the map with zero entries is unnecessary for coverage as well as forbidden here.

### Consistency

Expand Down
2 changes: 1 addition & 1 deletion .agents/rules/099-finish.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ alwaysApply: true
- **Conventions**: Verify `[CONV:LICENSE]`/`[CONV:NATSPEC]`/`[CONV:BLANKLINES]`/`[CONV:NAMING]` satisfied; avoid interface/storage changes unless requested.
- **Testing**: After Solidity changes → `forge test` (or note suites remaining); after TS → lint/tests with Bun; after Bash → check execution flags/sourcing. State explicitly if anything not run.
- **Linting**: Run the relevant linter on **all files you created or edited** (e.g. `bunx eslint` for TS/JS, or the project’s lint command) and fix all reported issues before finalizing. Do not claim the code is free of lint errors unless the linter has been run on those files and exited successfully.
- **Health-check invariants**: If you added, removed, or changed a facet or periphery contract, confirm `script/deploy/healthCheckInvariants.ts` was reviewed for needed invariant changes (see `601-healthcheck-invariants`).
- **Health-check invariants**: If you added, removed, or changed a facet or periphery contract, confirm `script/deploy/healthCheckInvariants.ts` was reviewed for needed invariant changes (see `601-healthcheck-invariants`). If it binds an external protocol address immutably, confirm the constructor arg carries a `getter` annotation in `deployRequirements.json`.
- **PR review feedback**: The local CodeRabbit pre-flight (`/pr-ready`) has been removed — the CLI was hitting rate limits and wasting review cycles across the team. Cloud CodeRabbit still runs in GitHub CI on every PR, unaffected; treat its comments as the review backstop. The `self-review-pass` skill (mechanical/semantic/executable sweep, unrelated to CodeRabbit) remains the mandatory step before pinging for review.
- **After opening a PR**: don't abandon it — watch CI checks and bot reviewers (cloud CodeRabbit, Aikido) and resolve failures or actionable comments before requesting human review. Once the PR is handed off for review, clean up the session workspace: remove the worktree (if one was used) and any stray uncommitted files so the next session starts from a clean bench.
- **Summary format**: Start with applied rules (filename/anchor), include tests/lints run, call out follow-ups/gaps.
3 changes: 2 additions & 1 deletion .agents/rules/100-solidity-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ paths:
- `@author LI.FI (https://li.fi)`
- `@notice` - must describe what the contract/interface contains and its purpose
- `@custom:version X.Y.Z`
- NatSpec must appear directly above the `contract`/`interface` keyword — no blank line between the closing `*/` and the declaration.
- Use **`///` line NatSpec** (and `//` for non-NatSpec). Do **not** use `/* … */` or `/** … */` in LI.FI-owned contracts (`src/Facets/**`, owned periphery/helpers/libraries/errors). Vendor/copied interfaces may keep upstream block comments.
- NatSpec must appear directly above the `contract`/`interface` keyword — no blank line between the last `///` line and the declaration.
- Public/external functions require NatSpec including params/returns.
- For pure test/script scaffolding keep headers minimal but retain SPDX/pragma.

Expand Down
1 change: 1 addition & 0 deletions .agents/rules/101-solidity-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ paths:
- Apply Diamond patterns with existing libs (LibAsset/LibSwap/LibAllowList, Validatable/SwapperV2); prefer parameters over `msg.sender` for refund addresses.
- Events: In Solidity 0.8.17, you **must not** emit events using `ContractName.EventName` syntax (e.g., `emit SomeContract.SomeEvent(...)`). Events must be defined in the same contract where they're emitted, or defined in an interface that the contract uses, and then emitted using just the event name (e.g., `emit SomeEvent(...)`). This prevents compilation errors and ensures 0.8.17 compatibility.
- When adding a new deployable contract that has constructor arguments, add/update its entry in `script/deploy/resources/deployRequirements.json` so deploy scripts validate the presence of required constructor args.
- Constructor args that bind an **external protocol address** (spoke pool, vault, router, portal) must be stored in a `public immutable` and annotated with `getter` in that same `deployRequirements.json` entry, so `immutable-bindings-match-config` compares the bound address against config on every chain. A `private`/`internal` immutable generates no getter, so unless the contract adds an explicit public accessor the binding cannot be read on chain at all — and only a redeploy can correct it. The `verify-immutable-registry` CI job fails on a public immutable address getter that is neither annotated nor recorded as exempt in `script/deploy/immutables/getter-exemptions.json`; it detects compiler-generated getters, so a hand-written accessor over a non-public immutable must be annotated deliberately. See `601-healthcheck-invariants`.
- When adding a new deployable contract, create a corresponding documentation page in `docs/` (mirroring existing docs conventions for new integrations/contracts).
Loading
Loading