-
Notifications
You must be signed in to change notification settings - Fork 12
feat: added gho direct minter #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
de27cba to
d2332c4
Compare
9ece938 to
571b250
Compare
571b250 to
5b55f32
Compare
f2f59b3 to
5105a57
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #57 +/- ##
==========================================
- Coverage 97.09% 96.55% -0.54%
==========================================
Files 16 16
Lines 516 523 +7
==========================================
+ Hits 501 505 +4
- Misses 15 18 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
d7a950c to
dbcbca0
Compare
fix: add token to git config for actions workflow
efe6ef7 to
24a65fd
Compare
24a65fd to
5ec5926
Compare
| let gho_reserve_entity = signer::address_of(&minter_signer); | ||
|
|
||
| // check: gho_reserve_entity must have RISK ADMIN ROLE for the pool | ||
| is_risk_admin(gho_reserve_entity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Critical]
is_risk_admin() returns a bool
we need an assert here:
assert!(is_risk_admin(gho_reserve_entity), error_config::get_ecaller_not_risk_or_pool_admin())
| let gho_reserve_entity = signer::address_of(&minter_signer); | ||
|
|
||
| // check: gho_reserve_entity must have RISK ADMIN ROLE for the pool | ||
| is_risk_admin(gho_reserve_entity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need an assert to enforce "is_risk_admin()"
assert!(is_risk_admin(gho_reserve_entity), error_config::get_ecaller_not_risk_or_pool_admin());
| use aave_pool::emode_logic; | ||
| use aave_pool::pool; | ||
|
|
||
| friend aave_pool::gho_direct_minter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a questionable/problematic design choice.
We should keep pool_configurator the single, top level caller of all protocol configuration setters, which means:
We should let gho_direct_minter be a friend to pool_configurator
Not pool_configurator a friend to gho_direct_minter.
| /// @param account The account signer of the caller | ||
| /// @param asset The address of the underlying asset of the reserve | ||
| /// @param new_supply_cap The new supply cap of the reserve | ||
| public(friend) fun set_supply_cap_internal( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A questionable design choice related to the above one.
This extend the ACL check to another file gho_direct_minter.move, even if the ACL check is present there after adding the assert, this function is generic and can target any asset, nothing in this file constrains it is to GHO. If someone later refactors the gho_direct_minter with a weakened ACL, it can mutate a top level risk parameter from there, and the configurator can't stop it.
Again I suggest that we keep all the top level setters in this file, not in other files.
| is_risk_admin(gho_reserve_entity); | ||
|
|
||
| // check: the gho_reserve_entity must be registered as an entity with a non zero entity limit | ||
| let (_, _) = ensure_entity_gho_usage(gho_reserve_entity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's use these return values, after (limit, used), assert amount <= limit - used
| is_risk_admin(gho_reserve_entity); | ||
|
|
||
| // check: the gho_reserve_entity must be registered as a entity with a non zero limit | ||
| let (_, _) = ensure_entity_gho_usage(gho_reserve_entity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after (limit, used), assert amount <= limit - used
| metadata_address: address | ||
| ) acquires TokenMap { | ||
| transfer_on_liquidation(from, to, amount, index, metadata_address); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function can transfer aTokens from any address without signature when invoked by a friend. Today you only transfer from the minter’s own address, but the capability is unrestricted.
Consider to replace "friend" exposure with a narrower helper in a_token_factory, e.g. transfer_from_self_to(address to, ...) that internally asserts from == <this module’s signer address> or accepts only the minter’s signer address returned by get_direct_minter_signer_address(). Alternatively, keep friend but in transfer_excess_to_treasury assert that from == get_direct_minter_signer_address().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion we should put gho_direct_minter in the GHO repo, not in aptos-aave-v3.
Why this is better
-
Ownership/governance fit: The direct minter is a GHO facilitator component. Keeping it under the GHO codebase aligns upgrades and reviews with GHO governance, mirroring the Solidity design and deployment model described in the original docs and contract README and GhoDirectMinter.sol.
-
Security boundary: Aave Core should remain asset-agnostic. Housing GHO-specific facilitator logic in Aave forces “friend” surfaces or risk-parameter mutations that widen the trusted set. Keeping minter in GHO avoids adding Aave-internal hooks or friends.
-
Upgrade/dependency hygiene: Decouples GHO facilitator releases from protocol-core versioning. The minter can import the pool’s public APIs and evolve independently, without needing Aave to expose specialized internal setters.
-
1:1 with Ethereum architecture: On Ethereum, facilitators live outside the pool; the pool remains unchanged. This matches the cross-chain GSM/GHO strategy outlined in the explainer and minter repo.
| // Constants | ||
| /// @notice Name for the GHO direct minter object | ||
| const GHO_DIRECT_MINTER_NAME: vector<u8> = b"GHO_DIRECT_MINTER"; | ||
| /// @notice seed for deriving the GHO reserve address | ||
| const GHO_RESERVE_SEED: vector<u8> = b"GHO_RESERVE"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand our design correctly, we suppose to have a dedicated GhoReserve per facilitator, so we may want to move these seeds into GhoDirectMinterData instead of hardcoded global seeds for every deployment.
|
|
||
| // temporarily set the supply cap to 0 to disable it while supplying | ||
| let old_supply_cap = | ||
| pool::get_reserve_configuration(gho_direct_minter_data.gho_token_address).get_supply_cap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We typically use reserve_config::get_supply_cap(&map) in the core protocol, can we unify the method here?
| ); | ||
|
|
||
| // extract the entity level | ||
| let (_, used) = ensure_entity_gho_usage(gho_reserve_entity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will throw with a limit == 0, do we have such possible scenario? that DAO set the limit == 0 and someone still wants to transfer_excess_to_treasury?
| struct GhoDirectMinterData has key { | ||
| gho_token_address: address, | ||
| extend_ref: ObjExtendRef, | ||
| transfer_ref: ObjectTransferRef, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transfer_ref is defined and init, but not used anywhere, is it intended?
| /// @notice Ensures the caller is either the pool owner or the GHO Guardian | ||
| /// @dev Reverts otherwise | ||
| /// @param account The signer to check | ||
| fun only_owner_or_guardian(account: &signer) { | ||
| let caller = signer::address_of(account); | ||
| assert!( | ||
| caller == @aave_pool || is_gho_guardian(caller), | ||
| error_config::get_ecaller_not_gho_guardian() | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defined but unused
| /// @dev Asserts that the GHO direct minter resource exists | ||
| inline fun assert_gho_reserve_exists() { | ||
| assert!( | ||
| exists<GhoDirectMinterData>(gho_direct_minter_address()), | ||
| error_config::get_eresource_not_exist() | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename this function according to our intention?
gho_direct_minter_address(): the object address that stores GhoDirectMinterData (the minter’s own state).
gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED): the GHO Reserve’s address (where bridge-minted GHO is held and entity usage is tracked).
No description provided.