Skip to content

Conversation

@mpsc0x
Copy link
Collaborator

@mpsc0x mpsc0x commented Sep 2, 2025

No description provided.

@mpsc0x mpsc0x self-assigned this Sep 2, 2025
@mpsc0x mpsc0x force-pushed the feat/gho-direct-minter branch 10 times, most recently from de27cba to d2332c4 Compare September 2, 2025 22:31
@mpsc0x mpsc0x force-pushed the feat/gho-direct-minter branch 2 times, most recently from 9ece938 to 571b250 Compare September 3, 2025 12:15
@mpsc0x mpsc0x force-pushed the feat/gho-direct-minter branch from 571b250 to 5b55f32 Compare September 3, 2025 13:21
@mpsc0x mpsc0x force-pushed the feat/gho-direct-minter branch from f2f59b3 to 5105a57 Compare September 4, 2025 10:41
@codecov
Copy link

codecov bot commented Sep 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.55%. Comparing base (822a67e) to head (5ec5926).
⚠️ Report is 3 commits behind head on main.

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     
Flag Coverage Δ
move 96.55% <100.00%> (-0.54%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mpsc0x mpsc0x force-pushed the feat/gho-direct-minter branch from d7a950c to dbcbca0 Compare September 4, 2025 11:23
@mpsc0x mpsc0x force-pushed the feat/gho-direct-minter branch from 24a65fd to 5ec5926 Compare September 5, 2025 08:58
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);
Copy link
Collaborator

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);
Copy link
Collaborator

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;
Copy link
Collaborator

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(
Copy link
Collaborator

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);
Copy link
Collaborator

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);
Copy link
Collaborator

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);
}
Copy link
Collaborator

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().

Copy link
Collaborator

@matchv matchv left a 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

Comment on lines +25 to +29
// 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";
Copy link
Collaborator

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();
Copy link
Collaborator

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);
Copy link
Collaborator

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,
Copy link
Collaborator

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?

Comment on lines +298 to +307
/// @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()
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defined but unused

Comment on lines +280 to +286
/// @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()
);
}
Copy link
Collaborator

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).

@mpsc0x mpsc0x closed this Sep 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants