Repository navigation
Conversation
Onboarding a risk agent is two calls a proposal cannot land separately: granting the agent the ACL role it injects with, and registering it in the AgentHub. Every proposal so far has re-implemented both, along with the assertions that check they worked. `AgentRegistrationInput` has thirteen fields, seven of which have been identical in every onboarding. Spelling all thirteen out per proposal puts `isAgentPermissioned` and `restrictedMarkets` in front of an author with no reason to change them, in a struct where a wrong flag is a permission granted rather than a compile error. `AgentHubOnboarding` fixes those seven and takes the six that vary. `setAbsoluteRange` covers the other half. A fresh agent id inherits no range config, and the module reads a missing config as a zero bound, so an agent registered without one is registered and permanently unable to inject. Bounds are absolute because a relative bound is measured against a last injected value a fresh id does not have. `AgentHubTestBase` carries the assertions that follow from the above: that the stored configuration matches what the payload declared, that the ACL role was granted, and that every update type an agent writes was bounded rather than only some. `IAgentHub` and `IRangeValidationModule` are narrowed locally, as `IChainlinkAggregator` and `IPermissionedPayloadsController` already are, rather than taking `aave-agent-hub` as a dependency. Tested against a pinned mainnet fork rather than mocks, so the struct is filled the way the live hub reads it back: 7 passed.
The onboarding parameters are the other half of what every PT proposal copies: two update types, two delays, two expiration periods and two bounds, none of which vary per asset. Onboarding a second PT changes the market address and the eMode ids and nothing here. The update type strings are the ones worth moving. The RiskOracle is constructed with them, the agents publish under them, and the AgentHub registers against them, so the three layers agree only if the same literal is written in three repositories. Reading them from one place makes that structural rather than a matter of typing carefully: a rename currently breaks the agreement silently, since registration still succeeds and nothing published is ever matched. `eModeBoundedUpdateTypes` is a function rather than three constants because the eMode agent registers under one update type and injects three fields, each bounded separately. A field omitted from that list is a field left unbounded, which the module reads as a zero bound and uses to reject every update. Values verified against the live stack rather than copied from the proposal: the RiskOracle at 0x8346170dcE5455A1205f55A0b5448E67e42CD270 reports exactly ["PendleDiscountRateUpdate", "EModeCategoryUpdate"]. 13 passed across the agent-hub suites.
The previous versions were hand narrowed to the calls the helpers make, which left a copy that had to be re-read against upstream to be trusted: `AgentRegistrationInput` is ABI encoded by position, so a field reordered while trimming would compile and encode wrongly. Copied unmodified from aave-dao/aave-agent-hub @ 71e6dbb instead, so the files diff clean against their source and a bump is a re-copy rather than a re-narrowing. `IAgentConfigurator` comes with them, since `IAgentHub` inherits it there. Verified identical: sha256 of each file matches its source, and prettier leaves all three unchanged, so CI will not reformat them out of sync. 13 passed.
samtin0x
force-pushed
the
feat/agent-hub-onboarding-helpers
branch
from
August 20, 2026 12:10
c2347d8 to
edb7923
Compare
zekiblue
requested changes
Aug 20, 2026
Member
There was a problem hiding this comment.
This is not necessary, please remove
Collaborator
Author
There was a problem hiding this comment.
removing - q; changeset only added if the npm package is affected? or how does it work?
Member
There was a problem hiding this comment.
we can imwhy do we need these interfaces?
| /// | ||
| /// A proposal that genuinely needs a non-default flag should call `registerAgent` directly and | ||
| /// say why, rather than this library growing a parameter for it. | ||
| /// @author Aave Labs |
Member
There was a problem hiding this comment.
lets keep the natspecs not too long and focussed,
Collaborator
Author
|
Config logic stays in the proposal rather than moving to aave-helpers, interfaces get vendored in aave-proposals-v3 under src/interfaces (which the AIP already does), and the address book only needs the deployed addresses. Leaving the branch up as a follow-up for the agents-repo abstraction |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Extracts the agent onboarding logic, parameters and assertions that a proposal currently re-implements, so onboarding the next asset is a market address and an eMode id rather than a copy of the last proposal.
Taken from
AaveV3Ethereum_Onboard_PTsrUSDe22OCT2026_Oracle_20260817(aave-dao/aave-proposals-v3#1184), the first agent onboarding, which carries all of it inline.Additive: seven new files, nothing existing modified.
What a proposal repeats today
The payload is 143 lines: 13 constants, a 44-line
execute(), and two internal functions. Nine of those constants and both internal functions have nothing to do with the asset being onboarded — onboarding a second PT changes the market address and the eMode ids, and copies the rest verbatim. Its test file is another 302 lines, of which five of seven tests assert properties of the onboarding rather than of the asset.Before — every proposal writes this:
After — the proposal keeps the agent addresses, the market and the eModes, and nothing else:
Both internal functions and nine constants leave the proposal. The test file inherits
AgentHubTestBaseand keeps only the asset-specific assertions.Why it is worth extracting rather than copying
Two of the repeated pieces fail quietly when copied wrong.
AgentRegistrationInputhas 13 fields and 7 have been identical in every onboarding, among themisAgentPermissionedandrestrictedMarkets. Spelling all 13 out per proposal puts two permission-bearing flags in front of an author with no reason to touch them, in a struct where a wrong value is a permission granted rather than a compile error.A fresh agent id inherits no range config, and
RangeValidationModulereads a missing config as a zero bound. An agent registered without one is registered and permanently unable to inject — nothing reverts at execution, and the failure only surfaces when the first update is rejected.setAbsoluteRangesexists because the eMode agent registers under one update type but injects three fields, each bounded separately, so bounding three of four is the same silent failure.The update type strings have a third version of this problem. The RiskOracle is constructed with them, the agents publish under them, and the AgentHub registers against them, so three layers agree only because the same literal is written in three repositories. A rename breaks that silently: registration still succeeds and nothing published is ever matched.
What this adds
src/agent-hub/AgentHubOnboarding.sol—registerAgentAndGrantRiskAdmingrantsRISK_ADMINand registers in one call, fixing the 7 defaults and taking the 6 fields that vary, returning the id the hub allocated rather than letting a caller assume it.setAbsoluteRange/setAbsoluteRangesset symmetric absolute bounds; absolute because a relative bound is a percentage of a last injected value a fresh id does not have.src/agent-hub/AgentHubTestBase.sol— the assertions that are not asset-specific: stored configuration matches what the payload declared, the ACL role was granted, and every update type an agent writes was bounded rather than only some.src/agent-hub/LlamaRiskPTAgentConfig.sol— the onboarding parameters shared across the PT agents.src/interfaces/IAgentHub.sol,IAgentConfigurator.sol,IRangeValidationModule.sol— copied unmodified from aave-dao/aave-agent-hub @71e6dbb.Interfaces
aave-helpersdoes not depend onaave-agent-hub, so these signatures do not exist here and the helpers cannot compile without them. The files are vendored verbatim rather than narrowed: each is byte-identical to its source, so they diff clean against upstream and a version bump is a re-copy rather than a re-narrowing.AgentRegistrationInputis ABI encoded by position, so a copy trimmed by hand could reorder a field, compile, and encode wrongly against the live hub. Prettier leaves all three unchanged, so CI will not reformat them out of sync.Declared rather than added as a submodule, for three reasons:
src/interfaces/IChainlinkAggregator.solandIPermissionedPayloadsController.solare copies of interfaces from non-dependencies.aave-proposals-v3already made this call for these exact contracts. Itssrc/interfaces/holds its ownIAgentHub.solandIAgentConfigurator.sol, alongside equivalents for Umbrella, GHO and the Chainlink robots — and it does pull submodules, so the alternative was available and not taken.forge-stdandaave-address-book;aave-v3-origin,aave-v4, OpenZeppelin andsolidity-utilsall arrive through the address book. A third would be inherited by every consumer, includingaave-proposals-v3, for two calls and a handful of getters, and would pin this repository to a version of one still moving.The argument for a submodule is drift. The tests address it more directly: they run against the live AgentHub rather than a mock, so an upstream struct change fails them immediately, where a pin held at an older commit would keep passing while being wrong.
If maintainers would rather take the submodule, the interfaces delete cleanly and the helpers are unaffected.
Testing
Helpers run against a pinned mainnet fork rather than mocks, so the struct is filled the way the live hub reads it back. Config values are checked against the deployed stack rather than the proposal source: the RiskOracle at
0x8346170dcE5455A1205f55A0b5448E67e42CD270reports exactly["PendleDiscountRateUpdate", "EModeCategoryUpdate"].Notes for review
AaveV3Ethereum.ACL_ADMIN, verified on mainnet as both the ACL default admin and the AgentHub owner.EXECUTOR_LVL_1is neither.startPrankrather thanprank.registerAgentdirectly and say why, rather than this library growing a parameter for it.LlamaRiskPTAgentConfigis provider-specific configuration, which nothing in this repository currently holds. It is here because the values are shared across proposals rather than across providers, but placement and naming are worth a maintainer's view — it sits equally well inaave-risk-agents.Follow-ups
Once released, aave-dao/aave-proposals-v3#1184 drops its nine constants and both internal functions and inherits the test base.