Imported from OasisDEX/summer-earn-protocol (
AGENTS.md). Install upstream withnpx skills add OasisDEX/summer-earn-protocol. Copyright stays with the author.
Agents
This file references skills and knowledge available to AI agents working on this repository.
Skills
Skills are structured guides that encode domain-specific rules, patterns, and gotchas for common development tasks in this codebase.
Ark Development
Path:
summer-earn-protocol/packages/skills/ark-development/SKILL.md
Comprehensive guide for implementing new Ark contracts. Covers:
- Architecture decision (sync vs async, ERC4626 vs custom)
- Required overrides and implementation rules
- Gotchas: fork test setup order, diamond inheritance, decimal scaling, fee estimation
- Code style and file organization conventions
- Testing patterns and minimum test cases
- Reference implementations for each Ark pattern
Protocol Deployments
Path:
summer-earn-protocol/packages/skills/deployment/SKILL.md
Guide for adding new Arks and protocols to the deployment system using Hardhat Ignition and interactive scripts.
Documentation Pipeline
Full playbook (NatSpec gotchas, coverage/comment-only audit tooling, hand-written vs generated in
depth): docs/DOCS_PLAYBOOK.md. Summary below.
Published docs live in gitbook/ and are split into generated and hand-written
trees. Knowing which is which is the single biggest gotcha — editing a generated page is wasted
work.
- Generated (do NOT hand-edit): every
reference/subtree plus the two library subtrees, i.e.gitbook/contracts/{core,dutch-auction,access,config,rewards,oracles}/reference/,gitbook/contracts/libraries/{percentage,price,math}/,gitbook/governance/reference/, andgitbook/governance/voting-decay/. Each is produced byscripts/docs/assemble.mjsfromforge docoutput and is wiped and rewritten on every build — along with its_nav.jsonandREADME.md. To change one of these pages, edit the Solidity NatSpec in the source contract and rerun the build.gitbook/SUMMARY.mdis likewise generated fromscripts/docs/summary.template.md(only the template is hand-edited). The package→output mapping lives inscripts/docs/docs.config.json. - Hand-written (edit directly): everything else —
gitbook/introduction/,concepts/,apis/,data/,security/,internal/, the prosegovernance/pages (overview.md,sip-process.md,staking-and-rewards.md,sumr-token.md,vesting.md), andcontracts/architecture.md/contracts/ark-catalog.md.
Commands (root package.json): pnpm docs:gen (per-package forge doc via turbo →
docs/generated/), pnpm docs:assemble (clean + assemble into gitbook/), pnpm docs:build
(both), pnpm docs:check (build then git diff --exit-code -- gitbook/). After ANY NatSpec edit,
run pnpm docs:build and commit the regenerated gitbook/ in the same change.
CI (.github/workflows/docs.yaml): PRs run a drift check (pnpm docs:build must leave
gitbook/ clean); pushes to main auto-regenerate and commit [skip ci] as a safety net. Foundry
is pinned (v1.5.1) because forge doc output format can shift between versions and flap the drift
check.
NatSpec & audit tooling (scripts/audit/):
- Each documented contract package needs
extra_output = ["devdoc", "userdoc"]and a[doc]block in itsfoundry.toml, plus adocs:genscript — without theseforge doc/ the coverage auditor silently miss content. natspec-coverage.pycross-references ABI vs solc devdoc/userdoc (resolves@inheritdoc); reports land underdocs/audit/<period>/.check-comment-only.pyverifies a NatSpec-only edit is bytecode-identical modulo CBOR metadata (compare twoout/dirs). Use it to prove a doc wave changed no executable code.
Adding a new documented contract package: add it to scripts/docs/docs.config.json
({package, section, out}) AND give its foundry.toml the [doc] + extra_output + docs:gen
scaffolding, then pnpm docs:build.
Cross-Package Change Checklists
Ordered, file-level checklists for changes that ripple across packages. Address propagation in this
repo is almost entirely manual — packages/deployment/config/index.json is the source of truth, but
subgraph configs and app configs are hand-maintained copies.
Package Map
- Contracts (
core-contracts,gov-contracts,access-contracts,config-contracts,rewards-contracts,dutch-auction,rwa-oracles) — Foundry packages;core-contractsholds Arks/FleetCommander,pnpm buildrunsforge build+ an ABI-copy step. - Deployment (
deployment) — Hardhat + Ignition. Deploy scripts write addresses back intoconfig/index.json(prod) /config/index.test.json(staging "bummer") viascripts/helpers/update-json.ts. Institution configs live inconfig/institutions/<Name>/. - Subgraphs (
summer-earn-protocol-subgraph,-rates-,-protocol-gov-,-institutions-,-institutions-v2-,-auctions-,-dca-subgraph) — each has per-chainconfig/<network>.json(addresses + start blocks, hand-copied from deployment outputs), mustache-templatedsubgraph.template.yaml, andprepare:/build:/deploy:<network>scripts deploying to Goldsky with$npm_package_versionas the deployment tag. - Apps/services (
summer-earn-interface,summer-earn-rwa-app,institution-inspector,summer-earn-dca-app,summer-earn-auctions-frontend,summer-earn-gov-validator,summer-earn-gov-alert-bot,oracle-cli,oracle-dashboard,ark-rebalancer) — consume deployment addresses either via apnpm sync-configscript (gov-validator, interface, gov-alert-bot copy frompackages/deployment) or via fully hand-maintained config files (rwa-app, dca-app, auctions-frontend, oracle-dashboard). Subgraph Goldsky slugs are hand-listed per chain in each app's config. - Shared libs (
percentage,price-utils,math-utils,voting-decay,constants,external-dependencies,legacy-dependencies,eslint-config,jest-config,typescript-config,tenderly-utils) — Solidity/TS libraries and base configs consumed by the above.
Add a new Ark type (new contract + deploy support)
skills— readpackages/skills/ark-development/SKILL.mdFIRST. It mandates the base-contract choice (Ark / ERC4626Ark / ArkWithWithdrawalRequest / BasePendleArk), conservative_withdrawableTotalAssets(), split interface/error/event files, and fork-test-first setup order. Do not re-derive these rules.core-contracts— implementsrc/contracts/arks/<New>Ark.sol(~40 existing siblings, AaveV3Ark…WisdomTreeArk), with split files undersrc/interfaces/,src/errors/,src/events/(patternI<X>Events.sol). Fork tests undertest/use<CHAIN>_RPC_URLviafoundry.toml[rpc_endpoints]. Build:pnpm build; runpnpm format:fixafter edits.deployment/types/config-types.ts— register the type in TWO places in the same file: theArkTypeenum (line ~15) AND thearkTypesprompt-choices array (line ~53). Forgetting the array silently hides the type from the interactivedeploy:arkmenu.deployment/types/ark-params.ts— add the ark's constructor-params type.deployment/ignition/modules/arks/<new>-ark.ts— add the Ignition module (36 existing examples).deployment/scripts/arks/deploy-<new>-ark.ts— add the per-type deploy script (34 existing siblings).deployment/scripts/common/ark-deployment.ts— add the newArkTypeto BOTH switch statements:deployArk()(config-driven) anddeployArkInteractive()(prompt-driven). These are independent dispatchers.deployment/scripts/helpers/zod-schemas.ts(+validation.ts, which importsArkDetailsSchema) — extend validation if the ark needs new config fields.deployment/config/index.json— add protocol-specific addresses under<network>.protocolSpecific; mirror inindex.test.jsonfor staging. Reference the ark type inconfig/fleets/<chain>-<TOKEN>-N.json(and.bummer.jsonvariants /config/institutions/<Name>/fleets/*.json).deployment— deploy:NETWORK=<chain> pnpm deploy:ark(interactivescripts/deploy-ark.ts); optionally add a dedicateddeploy:<new>-arkpackage.json script (pattern:deploy:aavev3-ark,deploy:pendle-pt-ark). Add a curation row inconfig/curation/arks.json(and the institution-scoped copy) — consumed by curation/governance proposal scripts.summer-earn-rates-subgraph— if no existing product fits the underlying protocol: addsrc/products/<New>Product.ts(22 existing, e.g.AaveV3Product.ts), register it in theinit<Network>()method(s) insrc/config/protocolConfig.ts, add addresses tosrc/constants/addresses.ts, new ABIs underabis/+subgraph.template.yamlabis list if needed. Bump version inpackage.json, set grafting fields inconfig/<network>.json, thenpnpm deploy:<network>.summer-earn-protocol-subgraph— usually no change (arks are indexed generically via templates). Only updateabis/Ark.abi.json/src/utils/ark.ts+ redeploy if the ark'sdetails()JSON or event interface differs from the template ABI.gitbook— add a row togitbook/contracts/ark-catalog.md(category tables link tocontracts/core/reference/contracts/arks/) and update the "Supported Ark types" count/list ingitbook/internal/deployment.md(it hard-codes the ArkType member count).
Deploy a new Ark or Fleet (existing type)
- On-chain wiring is the trigger for most indexing:
summer-earn-protocol-subgraph's HarborCommand data source handlesFleetCommanderEnlisted(indexed address)and spawnsFleetCommanderTemplate, which handlesArkAdded(indexed address)and spawnsArkTemplate(subgraph.template.yaml). No subgraph change needed for a new fleet/ark unless the HarborCommand address itself changed (config/<network>.jsonharbor-command-address). summer-earn-rates-subgraph/src/config/protocolConfig.ts— required if the ark targets a protocol/pool not yet tracked: add aProductin the correctinit<Network>()method. The Product id${groupName}-${assetAddress}-${poolAddress}-${chainId}(src/models/Product.ts) MUST match the ark's on-chaindetails()JSON (parsed bygetArkProductIdinsummer-earn-protocol-subgraph/src/utils/ark.ts, keys protocol/pool/vault/siUSDVault/chainId) or rate correlation silently misses. New token addresses go insrc/constants/addresses.ts. Bumppackage.jsonversion (convention1.23.4-<change-slug>), setgrafting-base/grafting-blockinconfig/<network>.jsonto avoid a full resync, thenpnpm deploy:<network>.summer-earn-auctions-subgraph— nothing for the ark itself: Raft data sources spawn from ConfigManagerRaftUpdated/ registryInstitutionAddedevents; per-ark auction params arrive viaArkAuctionParametersSet.summer-earn-institutions-subgraph/-v2-— nothing in the manifests (ArkAddedhandled by templates), but the same ark-details-to-rates-product-id coupling applies for institutional arks (see step 2).summer-earn-gov-validatorandsummer-earn-interface— re-runpnpm sync-configin each sosrc/config/...picks up the new addresses frompackages/deployment. Interface fleet discovery at runtime goes throughHARBOR_COMMAND_ADDRESSESinsrc/config/environments.tsplus the subgraphs.summer-earn-dca-app— fleets are discovered on-chain via the hand-copied HarborCommand address; only extendsrc/config/addresses.ts(KNOWN_TOKEN_ADDRESSES,FEED_BY_ASSET_ADDRESS) if the fleet uses a new underlying token.ark-rebalancer— point theFLEET_COMMANDER_ADDRESSenv var at the new fleet; arks are enumerated on-chain viafleetCommander.arks()(ark_rebalancer.py, ABIs inlined).
Enable a new chain
Repo plumbing:
turbo.json— add<CHAIN>_RPC_URLtoglobalEnv(currently MAINNET/ARBITRUM/BASE/SONIC/OPTIMISM/HYPERLIQUID). Without this, turbo strips the var from task environments and cache keys..github/workflows/build-unit-test.yaml— add<CHAIN>_RPC_URL: ${{ secrets.<CHAIN>_RPC_URL }}to the env block and create the GitHub secret.- Repo-root
.env(untracked) — add<CHAIN>_RPC_URL. Subgraph deploy scriptssource ../../.env;deployment/hardhat.config.tsloads it via dotenv. core-contracts/foundry.toml— add<chain> = "${<CHAIN>_RPC_URL}"to[rpc_endpoints]for fork tests; same fordeployment/foundry.tomlif needed (currently only sepolia/base/mainnet there).
deployment package:
scripts/helpers/chain.ts— add to theSupportedChainenum AND the separate hand-maintainedSUPPORTED_CHAINSarray.scripts/common/chain-config-map.ts— addRPC_URL_MAPandCHAIN_CONFIG_MAPentries (usedefineChain()if not in viem/chains, like hyperliquid id 999).CHAIN_MAP_BY_IDis derived.scripts/helpers/chain-configs.ts— add a literal entry togetChainConfigs()({chain, config, rpcUrl}); not derived fromSUPPORTED_CHAINS.types/config-types.ts— add to theSupportedNetworksenum (a second chain enum, independent fromSupportedChain).config/index.json+config/index.test.json— add the top-level chain key with the standard shape (deployedContracts{gov, govV2, core, …}, tokens, common, protocolSpecific, bridge). Key sets already diverge between the two files.config/index.ts— add the chain to the exportedconfigmap; if it participates in LayerZero governance, add its endpoint id todstEidToChainIdMap. Add aconfig/adapters/layerzero.jsonchainConfig entry keyed by numeric chain id for cross-chain messaging.hardhat.config.ts— add thenetworksentry (RPC from env, chainId, accounts) andetherscan.customChainsif the explorer is not natively supported (pattern: sonic 146, hyperliquid 999).package.json— optionally adddeploy:status:<chain>; then run bootstrap deploys in order:deploy:gov/deploy:gov-v2,deploy:core(each writes back intoconfig/index.json), then fleets/arks. Downstream consumers are NOT auto-refreshed.
Subgraphs (each: per-chain config json + prepare:/build:/deploy:<network> scripts + deploy:all
list in package.json):
summer-earn-protocol-subgraph—config/<chain>.json(network, harbor-command-address + start block, governance-rewards-manager, summer-staking-v2 prod+staging, interval-handler-block-interval, grafting). Also hand-extendsrc/common/addressProvider.tsgetAddressesProvider()with a new network branch (~40 token/oracle addresses — the big gotcha). Goldsky slugsummer-protocol-<chain>.summer-earn-rates-subgraph—config/<chain>.json(entry_point_address + start block); network branch insrc/constants/addresses.ts(graph-node slugs:arbitrum-one,sonic-mainnet,hyperliquid/hyperevm) ANDsrc/utils/chainId.tsgetChainIdByNetworkName(unknown networks break product ids) AND a newinit<Chain>()insrc/config/protocolConfig.tswired into its constructor switch.summer-earn-protocol-gov-subgraph—config/<chain>.json(governor v1+v2, timelock, protocol-access-manager, harbor-command, governance token + start blocks; unused contracts zero-address as on hyperliquid/arbitrum).summer-earn-institutions-subgraph—config/<chain>.json(registry-address = InstitutionalVaultRegistry v1) + extend its own copy ofsrc/common/addressProvider.ts.summer-earn-institutions-v2-subgraph— TWO config files (<chain>.json+<chain>-staging.json; registry v2 + rounds-vault-registry addresses, zero-address placeholders allowed) and TWO script sets (deploy:<chain>,deploy:<chain>-staging) plusdeploy:all/deploy:all-staging. Extend its ownaddressProvider.tscopy.summer-earn-auctions-subgraph—config/<chain>.json(nestedconfig-manager{address,start-block},institutional-vault-registry{…}; zero-address if absent). Currently only mainnet/base/arbitrum/sonic — no hyperliquid.summer-earn-dca-subgraph— only base + mainnet exist.config/<chain>.jsonneeds dca-strategy-manager address/start-block, feed-start-block (~14d earlier), and usdc/eth Chainlink PROXY addresses (never impl addresses — the once-block handler resolves aggregators itself). Per its CLAUDE.md, update that file in the same commit.
Apps/services (subgraph Goldsky slugs and addresses are hand-listed per chain in each):
summer-earn-interface—src/config/chains.ts(CHAIN_NAMES, CHAIN_RPC_URLS, CHAIN_BLOCK_EXPLORERS, VIEM_CHAIN_ENTITIES + four subgraph URL maps: rates, institutions, protocol, governance);src/config/environments.ts(everyRecord<Environment, Record<number, Address>>map for both production and staging);scripts/sync-config.jsCHAIN_NAMES thenpnpm sync-config(writessrc/config/deployment/index.json+deployed/<chain>.json).summer-earn-rwa-app—src/types/chain.ts(ChainId union AND SUPPORTEDCHAIN_IDS array);src/config/chains.ts(FIVE records: CHAIN_NAMES, CHAIN_RPC_URLS, CHAIN_BLOCK_EXPLORERS, DEFAULT_INSTITUTIONS_V2_URLS production+staging, VIEM_CHAIN_ENTITIES);src/config/env.ts(NEXT_PUBLIC_INSTITUTIONS_V2_SUBGRAPH_URL override key).summer-earn-gov-validator—scripts/sync-config.jsCHAINNAMES (chains without a mapping are silently skipped with only a console warning) +pnpm sync-config;src/config/constants.ts(CHAINS array with LayerZero eID + hand-maintained CHAIN_CONFIG timelock/summerToken);src/config/rpc.ts(VIEM_CHAIN_ENTITIES, CHAIN_RPC_URLS); also chain-keyedtokenLists.ts,treasuryWallets.ts,src/services/subgraph.ts(per-chain NEXT_PUBLIC*_SUBGRAPH_URL defaults), and CHAIN_THEMES in chains.ts.summer-earn-gov-alert-bot—scripts/sync-config.jsCHAIN_NAMES (currently missing hyperliquid 999 — already drifted from the other two sync scripts) + sync;src/config.tsviemChains/SupportedNetworks;src/config/rpc.ts(near-duplicate of gov-validator's).summer-earn-dca-app— currently Base-only:src/config/chains.ts+src/config/addresses.ts(DCA_STRATEGY_MANAGER_ADDRESSES, HARBOR_COMMAND_ADDRESSES, KNOWN_TOKEN_ADDRESSES, FEED_BY_ASSET_ADDRESS).summer-earn-auctions-frontend—src/lib/config.tsCHAIN_CONFIGS entry (subgraphEndpoint, raftAddress hand-copied,<CHAIN>_RPC_URLenv var).oracle-cli—src/config.ts(DeployNetwork union, VIEM_CHAINS, RPC_ENV_KEYS/CANDIDATES; currently only base/arbitrum/mainnet/sonic).oracle-dashboard—config/chains.tsCHAIN_RPC_URLS + hand-copydeployments.json/yield-deployments.jsonfrom oracle-cli intolib/.gitbook/internal/deployment.md— update the internal deployment docs (deploy commands take--network $NETWORK; fleet config naming<chain>-<TOKEN>-N.json).
Deploy / onboard a new institution (whitelist flow)
deployment/config/institutions/<InstitutionId>/index.json(index.test.jsonfor staging/"bummer") — create with per-network governor[], curators[], guardian[], superKeeper, whitelistManagers[], and a MANDATORYtimelockblock {governorDelay, curatorDelay, treasuryDelay} in seconds (0 = immediate; max 365 days viaMAX_TIMELOCK_DELAY_SECONDSinscripts/helpers/zod-schemas.ts). Schemas are strict zod — unknown keys are rejected, and any new contract key MUST be added toInstitutionNetworkDeployedContractsSchemaor its recorded address is stripped on the next index write.deployment— runNETWORK=<net> pnpm deploy:institution(scripts/deploy-institution-whitelist.ts). Prompts prod vs bummer + institution id, validates config, then deploys viaignition/modules/institution-whitelist.ts: ProtocolAccessManagerV2 + THREE RwaTimelocks (Governor/Curator/Treasury — the treasury RwaTimelock IS the ConfigurationManager treasury) + ConfigurationManagerWhitelist + TipJar + HarborCommand + Raft (linked DutchAuctionLibrary) + AdmiralsQuartersWhitelist. Registers the institution in InstitutionalVaultRegistry V2, grants roles, grants GOVERNOR_ROLE to the governor timelock, and writes all addresses back into the institution index. Re-running is idempotent (missing timelocks deployed directly, recorded ones verified withassertTimelockUsable).deployment/config/institutions/<InstitutionId>/fleets/<network>-<ASSET>-<n>.json— add per-fleet definition (fleetName, symbol, assetSymbol, depositCap, curator = the institution's curatorTimelock, operatorType e.g. roundsVaults, arks[]), thenpnpm deploy:institution-fleet(scripts/deploy-whitelisted-fleet.ts). Refuses to deploy unless the institution is registered; writes fleet addresses (fleetCommander, bufferArk, arks, roundsVaultInput/Output) into the institution index.deployment— run ONCE at the end:pnpm deploy:institution-handover(scripts/handover-institution-timelock.ts). Verifies all three timelocks, ensures the governor timelock holds GOVERNOR_ROLE and the curator timelock holds CURATOR_ROLE on at least one fleet, then revokes the deployer's WHITELIST_MANAGER_ROLE and renounces its GOVERNOR_ROLE. Until this runs, the deployer keeps its bootstrap GOVERNOR_ROLE.- Subgraphs — normally zero changes:
summer-earn-institutions-v2-subgraphspawns templates fromInstitutionAdded(indexed bytes32,address,address,address)andRoundsVaultPairRegistered; v1 likewise via its registry. Only hand-editconfig/<network>.json(registry-address / rounds-vault-registry-address) + version bump + redeploy if the registry contracts themselves change. Staging vs prod use separate registries viaconfig/<network>-staging.jsonand-stagingGoldsky slugs. summer-earn-auctions-subgraph— auto viaInstitutionAdded/RaftUpdated, BUT on chains whereinstitutional-vault-registryis still 0x0 inconfig/<network>.json(base/mainnet today), institutional auction Rafts will NOT be picked up until the address is hand-filled and the subgraph redeployed.summer-earn-rwa-app/src/config/institutions.ts— HAND-MAINTAINED: add a full Institution entry (slug, chainId, protocolAccessManager, timelocks + delays, configurationManager, harborCommand, admiralsQuarters, raft, tipJar, treasury, role arrays, every fleet's fleetCommander/bufferArk/arks/roundsVaults) to STAGING_INSTITUTIONS or PRODUCTION_INSTITUTIONS. The file header says it mirrorspackages/deployment/config/institutions/<name>/index(.test).json; there is NO sync script. Adding an ark to an institution fleet also requires editing the fleet'sarks[]here. Confirm the chain's institutions-v2 subgraph slug insrc/config/chains.ts(production vs-staging).summer-earn-interface— institutions surface throughCHAIN_INSTITUTIONS_SUBGRAPH_URLSinsrc/config/chains.tsplus the synced deployment config; re-runpnpm sync-configafter the deployment config changes.
Governance contract changes (governor/timelock redeploy or govV2)
summer-earn-protocol-gov-subgraph— add the new governor/timelock addresses toconfig/<network>.jsonand redeploy; consumers depend on it indexing the new contracts.summer-earn-gov-validator— re-runpnpm sync-config(pulls gov/govV2 fromdeployment/config/index.json); update hand-maintainedCHAIN_CONFIG(timelock, summerToken) insrc/config/constants.tsand the ABIs insrc/config/abis/if the interface changed; gov subgraph endpoints insrc/services/subgraph.tsmust index the new governor.summer-earn-gov-alert-bot— re-runpnpm sync-config;getGovernorAddresses()readsdeployedContracts.gov.summerGovernorandgovV2.summerGovernor,getTimelockAddress()readsgov.timelockfrom the syncedsrc/config/index.json— a renamed config key breaks the bot silently.summer-earn-interface—CHAIN_GOVERNANCE_SUBGRAPH_URLSinsrc/config/chains.tsmust point at a gov subgraph indexing the new governor; re-runpnpm sync-configfor addresses.
New RWA oracle / yield token deployed
oracle-cli—deploy.ts/deploy-yield.tsrecord oracleRegistry + per-ticker oracle/asset addresses insrc/deployments.jsonand yield-token/pocket addresses insrc/yield-deployments.json.oracle-dashboard— HAND-COPIED:lib/deployments.jsonandlib/yield-deployments.jsonare byte-identical copies oforacle-cli/src/*.json; no sync script exists in either package.json — copy manually after each oracle deployment.
Beads Issue Tracker
Use Beads (bd) for durable task tracking in repositories that include it. Use the beads skill at
.agents/skills/beads/SKILL.md (project install) or ~/.agents/skills/beads/SKILL.md (global
install) for Beads workflow guidance, then use the bd CLI for issue operations.
Quick Reference
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work
bd close <id> # Complete work
bd prime # Refresh Beads context
Rules
- Use
bdfor all task tracking; do not create markdown TODO lists. - Run
bd primewhen Beads context is missing or stale. - Keep persistent project memory in Beads via
bd remember; do not create ad hoc memory files.
