From 8881321872737229bbbf2805de0b9ad65d0b12e9 Mon Sep 17 00:00:00 2001 From: mpsc0x Date: Mon, 1 Sep 2025 21:07:56 +0300 Subject: [PATCH 1/9] feat: added gho direct minter --- .env.template | 5 + .github/workflows/doc.yml | 3 + .github/workflows/move-audit.yml | 3 + .github/workflows/move-v2.yml | 3 + .../typescript-integration-tests.yml | 3 + .github/workflows/unit_tests.yml | 3 + .pre-commit-config.yaml | 2 + .secrets.baseline | 4 +- Makefile | 6 +- aave-core/Move.toml | 4 + aave-core/aave-acl/sources/acl_manage.move | 37 + .../aave-acl/tests/acl_manage_tests.move | 10 + .../aave-config/sources/error_config.move | 16 + aave-core/aave-config/tests/error_tests.move | 21 +- .../sources/aave-pool/pool_configurator.move | 12 + .../gho-direct-minter/gho_direct_minter.move | 336 +++++++++ .../gho-direct-minter/gho_direct_minter.move | 648 ++++++++++++++++++ aave-test-kit/Dockerfile | 3 + aave-test-kit/docker-compose.yml | 3 + examples/Makefile | 4 +- 20 files changed, 1119 insertions(+), 7 deletions(-) create mode 100644 aave-core/sources/gho-direct-minter/gho_direct_minter.move create mode 100644 aave-core/tests/gho-direct-minter/gho_direct_minter.move diff --git a/.env.template b/.env.template index 9bed75d..6c33acf 100644 --- a/.env.template +++ b/.env.template @@ -29,3 +29,8 @@ TEST_ACCOUNT_2_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] TEST_ACCOUNT_3_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] TEST_ACCOUNT_4_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] TEST_ACCOUNT_5_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] + +# gho profiles private keys +GHO_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] +GHO_CONFIG_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] +GHO_ACL_PRIVATE_KEY=[YOUR PROFILE ACCOUNT PRIVATE KEY] diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 56e4b55..eda375c 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -27,6 +27,9 @@ env: AAVE_DATA_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_PRIVATE_KEY }} AAVE_DATA_FEEDS_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_FEEDS_PRIVATE_KEY }} AAVE_PLATFORM_PRIVATE_KEY: ${{ secrets.GH_AAVE_PLATFORM_PRIVATE_KEY }} + GHO_PRIVATE_KEY: ${{ secrets.GH_GHO_PRIVATE_KEY }} + GHO_CONFIG_PRIVATE_KEY: ${{ secrets.GH_GHO_CONFIG_PRIVATE_KEY }} + GHO_ACL_PRIVATE_KEY: ${{ secrets.GH_GHO_ACL_PRIVATE_KEY }} jobs: build-docs: diff --git a/.github/workflows/move-audit.yml b/.github/workflows/move-audit.yml index be8caa5..a78d662 100644 --- a/.github/workflows/move-audit.yml +++ b/.github/workflows/move-audit.yml @@ -26,6 +26,9 @@ env: AAVE_DATA_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_PRIVATE_KEY }} AAVE_DATA_FEEDS_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_FEEDS_PRIVATE_KEY }} AAVE_PLATFORM_PRIVATE_KEY: ${{ secrets.GH_AAVE_PLATFORM_PRIVATE_KEY }} + GHO_PRIVATE_KEY: ${{ secrets.GH_GHO_PRIVATE_KEY }} + GHO_CONFIG_PRIVATE_KEY: ${{ secrets.GH_GHO_CONFIG_PRIVATE_KEY }} + GHO_ACL_PRIVATE_KEY: ${{ secrets.GH_GHO_ACL_PRIVATE_KEY }} jobs: unit-tests: diff --git a/.github/workflows/move-v2.yml b/.github/workflows/move-v2.yml index 859cc8f..fb47d7b 100644 --- a/.github/workflows/move-v2.yml +++ b/.github/workflows/move-v2.yml @@ -26,6 +26,9 @@ env: AAVE_DATA_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_PRIVATE_KEY }} AAVE_DATA_FEEDS_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_FEEDS_PRIVATE_KEY }} AAVE_PLATFORM_PRIVATE_KEY: ${{ secrets.GH_AAVE_PLATFORM_PRIVATE_KEY }} + GHO_PRIVATE_KEY: ${{ secrets.GH_GHO_PRIVATE_KEY }} + GHO_CONFIG_PRIVATE_KEY: ${{ secrets.GH_GHO_CONFIG_PRIVATE_KEY }} + GHO_ACL_PRIVATE_KEY: ${{ secrets.GH_GHO_ACL_PRIVATE_KEY }} jobs: unit-tests: diff --git a/.github/workflows/typescript-integration-tests.yml b/.github/workflows/typescript-integration-tests.yml index 64bcb57..057deed 100644 --- a/.github/workflows/typescript-integration-tests.yml +++ b/.github/workflows/typescript-integration-tests.yml @@ -27,6 +27,9 @@ env: AAVE_DATA_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_PRIVATE_KEY }} AAVE_DATA_FEEDS_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_FEEDS_PRIVATE_KEY }} AAVE_PLATFORM_PRIVATE_KEY: ${{ secrets.GH_AAVE_PLATFORM_PRIVATE_KEY }} + GHO_PRIVATE_KEY: ${{ secrets.GH_GHO_PRIVATE_KEY }} + GHO_CONFIG_PRIVATE_KEY: ${{ secrets.GH_GHO_CONFIG_PRIVATE_KEY }} + GHO_ACL_PRIVATE_KEY: ${{ secrets.GH_GHO_ACL_PRIVATE_KEY }} # test profiles TEST_ACCOUNT_0_PRIVATE_KEY: ${{ secrets.GH_TEST_ACCOUNT_0_PRIVATE_KEY }} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 43765a8..6c720f5 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -27,6 +27,9 @@ env: AAVE_DATA_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_PRIVATE_KEY }} AAVE_DATA_FEEDS_PRIVATE_KEY: ${{ secrets.GH_AAVE_DATA_FEEDS_PRIVATE_KEY }} AAVE_PLATFORM_PRIVATE_KEY: ${{ secrets.GH_AAVE_PLATFORM_PRIVATE_KEY }} + GHO_PRIVATE_KEY: ${{ secrets.GH_GHO_PRIVATE_KEY }} + GHO_CONFIG_PRIVATE_KEY: ${{ secrets.GH_GHO_CONFIG_PRIVATE_KEY }} + GHO_ACL_PRIVATE_KEY: ${{ secrets.GH_GHO_ACL_PRIVATE_KEY }} jobs: unit-tests: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e780d42..23114ad 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,6 +53,8 @@ repos: args: - --baseline - .secrets.baseline + - --exclude-lines + - '\bgho_[a-z_]+\b' exclude: | (?x)^( \.secrets\.baseline| diff --git a/.secrets.baseline b/.secrets.baseline index eb28d5a..46f7836 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -133,7 +133,7 @@ "filename": "aave-core/aave-config/sources/error_config.move", "hashed_secret": "ba29d6bfac4cd7b866a3d05103c38031a921ba88", "is_verified": false, - "line_number": 1160 + "line_number": 1176 } ], "aave-core/aave-config/tests/error_tests.move": [ @@ -11151,5 +11151,5 @@ } ] }, - "generated_at": "2025-08-22T08:39:13Z" + "generated_at": "2025-09-02T11:33:59Z" } diff --git a/Makefile b/Makefile index be80ebf..406431c 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,10 @@ AAVE_BASE_PROFILES_KEY_MAP = aave_acl=$(AAVE_ACL_PRIVATE_KEY) \ aave_pool=$(AAVE_POOL_PRIVATE_KEY) \ aave_large_packages=$(AAVE_LARGE_PACKAGES_PRIVATE_KEY) \ aave_mock_underlyings=$(AAVE_MOCK_UNDERLYING_TOKENS_PRIVATE_KEY) \ - aave_data=$(AAVE_DATA_PRIVATE_KEY) + aave_data=$(AAVE_DATA_PRIVATE_KEY) \ + gho=$(GHO_PRIVATE_KEY) \ + gho_config=$(GHO_CONFIG_PRIVATE_KEY) \ + gho_acl=$(GHO_ACL_PRIVATE_KEY) ifeq ($(APTOS_NETWORK), local) AAVE_PROFILES_KEY_MAP = $(AAVE_BASE_PROFILES_KEY_MAP) data_feeds=$(AAVE_DATA_FEEDS_PRIVATE_KEY) platform=$(AAVE_PLATFORM_PRIVATE_KEY) @@ -694,7 +697,6 @@ compile-pool: cd aave-core && aptos move compile \ --included-artifacts $(ARTIFACTS_LEVEL) \ --save-metadata \ - --skip-fetch-latest-git-deps \ --language-version "$(MOVE_VERSION)" \ --compiler-version "$(COMPILER_VERSION)" \ --named-addresses "${AAVE_NAMED_ADDRESSES}" diff --git a/aave-core/Move.toml b/aave-core/Move.toml index ade8ef0..986773d 100644 --- a/aave-core/Move.toml +++ b/aave-core/Move.toml @@ -6,6 +6,9 @@ authors = [] [addresses] aave_pool = '_' +gho = "_" +gho_config = "_" +gho_acl = "_" [dev-addresses] @@ -15,6 +18,7 @@ AaveAcl = { local = "./aave-acl" } AaveConfig = { local = "./aave-config" } AaveMath = { local = "./aave-math" } AaveOracle = { local = "./aave-oracle" } +Gho_Token = { git = "https://github.com/aave/aptos-aave-gho", subdir = "Gho", rev = "main" } [dev-dependencies] AaveMockUnderlyings = { local = "./aave-mock-underlyings" } diff --git a/aave-core/aave-acl/sources/acl_manage.move b/aave-core/aave-acl/sources/acl_manage.move index 5611ede..c396a94 100644 --- a/aave-core/aave-acl/sources/acl_manage.move +++ b/aave-core/aave-acl/sources/acl_manage.move @@ -53,6 +53,7 @@ module aave_acl::acl_manage { const EMISSION_ADMIN_ROLE: vector = b"EMISSION_ADMIN"; const ADMIN_CONTROLLED_ECOSYSTEM_RESERVE_FUNDS_ADMIN_ROLE: vector = b"ADMIN_CONTROLLED_ECOSYSTEM_RESERVE_FUNDS_ADMIN"; const REWARDS_CONTROLLER_ADMIN_ROLE: vector = b"REWARDS_CONTROLLER_ADMIN"; + const GHO_GUARDIAN_ROLE: vector = b"GHO_GUARDIAN"; // Structs #[event] @@ -217,6 +218,14 @@ module aave_acl::acl_manage { has_role(get_rewards_controller_admin_role(), admin) } + #[view] + /// @notice Checks if the address is a GHO guardian role + /// @param admin Address to check + /// @return Boolean indicating if the address is a GHO guardina role + public fun is_gho_guardian(admin: address): bool acquires Roles { + has_role(get_gho_guardian_role(), admin) + } + #[view] /// @notice Returns the pool admin role string /// @return Pool admin role as a String @@ -280,6 +289,13 @@ module aave_acl::acl_manage { string::utf8(REWARDS_CONTROLLER_ADMIN_ROLE) } + #[view] + /// @notice Returns the gho guardina role string + /// @return GHO guardian role as a String + public fun get_gho_guardian_role(): String { + string::utf8(GHO_GUARDIAN_ROLE) + } + // Public entry functions /// @notice Sets `admin_role` as ``role``'s admin role /// @param admin Signer with permissions to set role admin @@ -504,6 +520,20 @@ module aave_acl::acl_manage { revoke_role(admin, get_rewards_controller_admin_role(), user); } + /// @notice Adds a gho guardian role to the specified address + /// @param admin Signer with permissions to grant roles + /// @param user Address to grant the gho guardian role to + public entry fun add_gho_guardian(admin: &signer, user: address) acquires Roles { + grant_role(admin, get_gho_guardian_role(), user); + } + + /// @notice Removes the gho guardina role from the specified address + /// @param admin Signer with permissions to revoke roles + /// @param user Address to revoke the gho guardina role from + public entry fun remove_gho_guardian(admin: &signer, user: address) acquires Roles { + revoke_role(admin, get_gho_guardian_role(), user); + } + // Private/Internal functions /// @dev Initializes the module and grants the default admin role to the admin signer /// @param admin Signer that will be granted the default admin role @@ -677,4 +707,11 @@ module aave_acl::acl_manage { public fun get_rewards_controller_admin_role_for_testing(): String { string::utf8(REWARDS_CONTROLLER_ADMIN_ROLE) } + + #[test_only] + /// @dev Returns the gho guardian role string for testing + /// @return Gho guardian role as a String + public fun get_gho_guardian_role_for_testing(): String { + string::utf8(GHO_GUARDIAN_ROLE) + } } diff --git a/aave-core/aave-acl/tests/acl_manage_tests.move b/aave-core/aave-acl/tests/acl_manage_tests.move index aaf5fa0..9330bab 100644 --- a/aave-core/aave-acl/tests/acl_manage_tests.move +++ b/aave-core/aave-acl/tests/acl_manage_tests.move @@ -33,6 +33,8 @@ module aave_acl::acl_manage_tests { get_rewards_controller_admin_role_for_testing, get_risk_admin_role, get_risk_admin_role_for_testing, + get_gho_guardian_role, + get_gho_guardian_role_for_testing, grant_role, has_role, is_admin_controlled_ecosystem_reserve_funds_admin, @@ -97,6 +99,14 @@ module aave_acl::acl_manage_tests { assert!(get_risk_admin_role() == get_risk_admin_role_for_testing(), TEST_SUCCESS); } + #[test] + fun test_get_gho_guardian_role() { + assert!( + get_gho_guardian_role() == get_gho_guardian_role_for_testing(), + TEST_SUCCESS + ); + } + #[test] fun test_get_emergency_admin_role() { assert!( diff --git a/aave-core/aave-config/sources/error_config.move b/aave-core/aave-config/sources/error_config.move index c3ec126..3efe0cc 100644 --- a/aave-core/aave-config/sources/error_config.move +++ b/aave-core/aave-config/sources/error_config.move @@ -179,6 +179,10 @@ module aave_config::error_config { const EINVALID_FREEZE_FLAG: u64 = 99; /// @notice Below a certain threshold liquidators need to take the full position const EMUST_NOT_LEAVE_DUST: u64 = 103; + /// @notice The caller of the function is not a gho guardian + const ECALLER_NOT_GHO_GUARDIAN: u64 = 104; + /// @notice Zero entity limit is not valid + const EZERO_ENTITY_LIMIT: u64 = 105; // Aptos has introduced a new business logic error code range from 1001 to 2000. @@ -375,6 +379,18 @@ module aave_config::error_config { ECALLER_NOT_EMERGENCY_ADMIN } + /// @notice Returns the error code for caller not being a gho guardian + /// @return Error code as u64 + public fun get_ecaller_not_gho_guardian(): u64 { + ECALLER_NOT_GHO_GUARDIAN + } + + /// @notice Returns the error code for zero entity limit + /// @return Error code as u64 + public fun get_ezero_entity_limit(): u64 { + EZERO_ENTITY_LIMIT + } + /// @notice Returns the error code for caller not being a pool or emergency admin /// @return Error code as u64 public fun get_ecaller_not_pool_or_emergency_admin(): u64 { diff --git a/aave-core/aave-config/tests/error_tests.move b/aave-core/aave-config/tests/error_tests.move index a02041c..a8a4c66 100644 --- a/aave-core/aave-config/tests/error_tests.move +++ b/aave-core/aave-config/tests/error_tests.move @@ -170,7 +170,9 @@ module aave_config::error_tests { get_einvalid_snapshot_delay, get_einvalid_snapshot_ratio, get_einvalid_snapshot_timestamp, - get_ecustom_price_above_price_cap + get_ecustom_price_above_price_cap, + get_ecaller_not_gho_guardian, + get_ezero_entity_limit }; const TEST_SUCCESS: u64 = 1; @@ -358,6 +360,10 @@ module aave_config::error_tests { /// Below a certain threshold liquidators need to take the full position const EMUST_NOT_LEAVE_DUST: u64 = 103; + /// @notice The caller of the function is not a gho guardian + const ECALLER_NOT_GHO_GUARDIAN: u64 = 104; + /// @notice Zero entity limit is not valid + const EZERO_ENTITY_LIMIT: u64 = 105; // Aptos has introduced a new business logic error code range from 1001 to 2000. @@ -555,6 +561,19 @@ module aave_config::error_tests { assert!(get_ecaller_not_pool_admin() == ECALLER_NOT_POOL_ADMIN, TEST_SUCCESS); } + #[test] + fun test_get_ecaller_not_gho_guardian() { + assert!(get_ecaller_not_gho_guardian() == ECALLER_NOT_GHO_GUARDIAN, TEST_SUCCESS); + } + + #[test] + fun test_get_ezero_entity_limit() { + assert!( + get_ezero_entity_limit() == EZERO_ENTITY_LIMIT, + TEST_SUCCESS + ); + } + #[test] fun test_get_ecaller_not_emergency_admin() { assert!( diff --git a/aave-core/sources/aave-pool/pool_configurator.move b/aave-core/sources/aave-pool/pool_configurator.move index d140dfd..80b9aed 100644 --- a/aave-core/sources/aave-pool/pool_configurator.move +++ b/aave-core/sources/aave-pool/pool_configurator.move @@ -25,6 +25,8 @@ module aave_pool::pool_configurator { use aave_pool::emode_logic; use aave_pool::pool; + friend aave_pool::gho_direct_minter; + // Structs /// @notice Internal module data struct InternalData has key { @@ -874,7 +876,17 @@ module aave_pool::pool_configurator { only_risk_or_pool_admins(signer::address_of(account)), error_config::get_ecaller_not_risk_or_pool_admin() ); + set_supply_cap_internal(asset, new_supply_cap); + } + /// @notice Updates the supply cap of a reserve + /// @dev Emits the SupplyCapChanged event + /// @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( + asset: address, new_supply_cap: u256 + ) { let reserve_config_map = pool::get_reserve_configuration(asset); let old_supply_cap: u256 = reserve_config::get_supply_cap(&reserve_config_map); diff --git a/aave-core/sources/gho-direct-minter/gho_direct_minter.move b/aave-core/sources/gho-direct-minter/gho_direct_minter.move new file mode 100644 index 0000000..5956042 --- /dev/null +++ b/aave-core/sources/gho-direct-minter/gho_direct_minter.move @@ -0,0 +1,336 @@ +/// @title Gho Direct Minter Module +/// @author Aave +/// @notice Implements the GHO direct minter functionality for minting/burning via the reserve entity +module aave_pool::gho_direct_minter { + // Std imports + use std::signer; + use aptos_framework::dispatchable_fungible_asset; + use aptos_framework::primary_fungible_store; + use aptos_framework::fungible_asset::Metadata; + use aptos_framework::object::{ + Self, + ExtendRef as ObjExtendRef, + Object, + ObjectGroup, + TransferRef as ObjectTransferRef + }; + // Aave imports + use aave_acl::acl_manage; + use aave_config::error_config; + use aave_pool::a_token_factory; + use aave_pool::collector; + use aave_pool::pool; + use aave_pool::supply_logic; + use aave_pool::pool_configurator; + use aave_pool::fungible_asset_manager; + use gho::gho_reserve; + + // Constants + /// @notice Name for the GHO direct minter object + const GHO_DIRECT_MINTER_NAME: vector = b"GHO_DIRECT_MINTER"; + + // Structs + #[resource_group_member(group = ObjectGroup)] + /// @notice On-chain state for the GHO direct minter + /// @dev Stores the GHO token address, object refs used for actions, and the treasury (collector) address + struct GhoDirectMinterData has key { + gho_token_address: address, + extend_ref: ObjExtendRef, + transfer_ref: ObjectTransferRef, + collector_address: address + } + + // Module initialization + /// @notice Initializes the GHO direct minter + /// @dev Creates the minter object under @aave_pool + /// @param sender The signer that initializes the module (must be @aave_pool) + fun init_module(sender: &signer) { + only_admin(sender); + + let state_object_constructor_ref = + &object::create_named_object(sender, GHO_DIRECT_MINTER_NAME); + let state_object_signer = &object::generate_signer(state_object_constructor_ref); + + move_to( + state_object_signer, + GhoDirectMinterData { + gho_token_address: @gho, + transfer_ref: object::generate_transfer_ref(state_object_constructor_ref), + extend_ref: object::generate_extend_ref(state_object_constructor_ref), + collector_address: collector::collector_address() + } + ); + } + + // Public entry functions + /// @notice Uses (mints) GHO from the reserve entity and supplies it to the protocol + /// @dev Caller must be the GHO Guardian; the reserve entity must have Risk Admin role and a non-zero usage limit + /// Temporarily disables supply cap during the supply, then restores it + /// @param account The transaction signer (must be a GHO Guardian) + /// @param amount The amount of GHO to use and supply + public entry fun use_and_supply(account: &signer, amount: u256) acquires GhoDirectMinterData { + assert_gho_reserve_exists(); + // check caller is the gho guardian + only_gho_guardian(signer::address_of(account)); + let gho_direct_minter_data = + borrow_global(gho_direct_minter_address()); + let minter_signer = + object::generate_signer_for_extending(&gho_direct_minter_data.extend_ref); + 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); + + // 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); + + // use GHO from the gho reserve + gho_reserve::use_gho(&minter_signer, amount); + + // 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(); + pool_configurator::set_supply_cap_internal( + gho_direct_minter_data.gho_token_address, 0 + ); + + // supply GHO from the minter_signer to the protocol + supply_logic::supply( + &minter_signer, + gho_direct_minter_data.gho_token_address, + amount, + signer::address_of(&minter_signer), + 0 + ); + + // reset the supply cap + pool_configurator::set_supply_cap_internal( + gho_direct_minter_data.gho_token_address, old_supply_cap + ); + } + + /// @notice Withdraws GHO from the protocol back to the reserve entity and restores it (burns against usage) + /// @dev Caller must be the GHO Guardian; the reserve entity must have Risk Admin role and a non-zero usage limit + /// @param account The transaction signer (must be a GHO Guardian) + /// @param amount The amount of GHO to withdraw and restore + public entry fun withdraw_and_restore(account: &signer, amount: u256) acquires GhoDirectMinterData { + assert_gho_reserve_exists(); + // check caller is gho guardian + only_gho_guardian(signer::address_of(account)); + let gho_direct_minter_data = + borrow_global(gho_direct_minter_address()); + let minter_signer = + object::generate_signer_for_extending(&gho_direct_minter_data.extend_ref); + 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); + + // check: the gho_reserve_entity must be registered as a Facilitator with a non zero bucket capacity + let (_, _) = ensure_entity_gho_usage(gho_reserve_entity); + + // withdraw GHO underlying asset i.e. burn Atokens and send back the underlying to the primary store of the gho_reserve_entity + supply_logic::withdraw( + &minter_signer, + gho_direct_minter_data.gho_token_address, + amount, + signer::address_of(&minter_signer) + ); + + // restore the gho amount back to the gho reserve (i.e. the burning) + gho_reserve::restore(&minter_signer, amount); + } + + /// @notice Transfers any excess GHO (over current usage) from the reserve entity to the treasury + /// @dev Computes excess = aToken balance − used; if zero, no-ops. Uses dispatchable FA transfer + public entry fun transfer_excess_to_treasury() acquires GhoDirectMinterData { + assert_gho_reserve_exists(); + let gho_direct_minter_data = + borrow_global_mut(gho_direct_minter_address()); + let minter_signer = + object::generate_signer_for_extending(&gho_direct_minter_data.extend_ref); + let gho_reserve_entity = signer::address_of(&minter_signer); + + // get the GHO atoken data + let gho_token_reserve_data = + pool::get_reserve_data(gho_direct_minter_data.gho_token_address); + let a_token_address = pool::get_reserve_a_token_address(gho_token_reserve_data); + assert!( + a_token_factory::is_atoken(a_token_address), + error_config::get_ecaller_not_atoken() + ); + + // extract the entity level + let (_, used) = ensure_entity_gho_usage(gho_reserve_entity); + let atoken_balance = + a_token_factory::balance_of(gho_reserve_entity, a_token_address); + // compute the excess level + let excess_level = + if (atoken_balance > used) { + atoken_balance - used + } else { 0 }; + + if (excess_level == 0) { + return; + }; + + // transfer the excess to the treasury + let a_token_metadata = object::address_to_object(a_token_address); + let store_from = + primary_fungible_store::primary_store(gho_reserve_entity, a_token_metadata); + let store_to = + primary_fungible_store::ensure_primary_store_exists( + gho_direct_minter_data.collector_address, a_token_metadata + ); + + dispatchable_fungible_asset::transfer( + &minter_signer, + store_from, + store_to, + (excess_level as u64) + ); + } + + /// @notice Sets the GHO token address used by the minter + /// @dev Only callable by @aave_pool admin; asserts the token exists + /// @param account The signer of the caller (must be @aave_pool) + /// @param gho_address The new GHO token address + public entry fun set_gho_address( + account: &signer, gho_address: address + ) acquires GhoDirectMinterData { + assert_gho_reserve_exists(); + only_admin(account); + assert!(gho_address != @0x0, error_config::get_ezero_address_not_valid()); + fungible_asset_manager::assert_token_exists(gho_address); + let gho_direct_minter_data = + borrow_global_mut(gho_direct_minter_address()); + gho_direct_minter_data.gho_token_address = gho_address; + } + + // Public view functions + #[view] + /// @notice Address of the GHO direct minter object + /// @return The object address derived from @aave_pool and the name seed + public fun gho_direct_minter_address(): address { + object::create_object_address(&@aave_pool, GHO_DIRECT_MINTER_NAME) + } + + #[view] + /// @notice Returns the GHO direct minter object handle + /// @return The object for accessing `GhoDirectMinterData` + public fun gho_direct_minter_object(): Object { + object::address_to_object(gho_direct_minter_address()) + } + + #[view] + /// @notice Gets the configured GHO token address + /// @return The address of the GHO token used by the minter + public fun get_gho_token_address(): address acquires GhoDirectMinterData { + assert_gho_reserve_exists(); + let gho_direct_minter_data = + borrow_global(gho_direct_minter_address()); + gho_direct_minter_data.gho_token_address + } + + #[view] + /// @notice Gets the treasury (collector) address + /// @return The address of the protocol collector + public fun get_collector_address(): address acquires GhoDirectMinterData { + assert_gho_reserve_exists(); + let gho_direct_minter_data = + borrow_global(gho_direct_minter_address()); + gho_direct_minter_data.collector_address + } + + #[view] + /// @notice Returns whether an address has the Risk Admin role in the pool + /// @param account The address to check + /// @return True if the address is a Risk Admin, false otherwise + public fun is_risk_admin(account: address): bool { + acl_manage::is_risk_admin(account) + } + + #[view] + /// @notice Returns whether an address is a GHO Guardian + /// @param account The address to check + /// @return True if the address is a GHO Guardian, false otherwise + public fun is_gho_guardian(account: address): bool { + acl_manage::is_gho_guardian(account) + } + + #[view] + /// @notice Returns the address represented by the direct minter signer + /// @return The address of the signer generated for the minter object + public fun get_direct_minter_signer_address(): address acquires GhoDirectMinterData { + signer::address_of(&get_direct_minter_signer()) + } + + // Private functions + /// @notice Checks if the caller is the pool admin + /// @dev Reverts if the caller is not @aave_pool + /// @param account The signer account to check + fun only_admin(account: &signer) { + assert!( + signer::address_of(account) == @aave_pool, + error_config::get_ecaller_must_be_pool() + ); + } + + /// @dev Asserts that the GHO direct minter resource exists + inline fun assert_gho_reserve_exists() { + assert!( + exists(gho_direct_minter_address()), + error_config::get_eresource_not_exist() + ); + } + + /// @notice Checks if an address has the GHO Guardian role + /// @dev Reverts if not a GHO Guardian + /// @param account The address to check + fun only_gho_guardian(account: address) { + assert!( + is_gho_guardian(account), + error_config::get_ecaller_not_gho_guardian() + ); + } + + /// @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() + ); + } + + /// @notice Ensures the entity has a non-zero GHO usage limit and returns (limit, used) + /// @dev Reverts if the entity limit is zero + /// @param entity The reserve entity address + /// @return (limit, used) usage tuple + fun ensure_entity_gho_usage(entity: address): (u256, u256) { + let (limit, used) = gho_reserve::get_usage(entity); + assert!( + limit > 0, + error_config::get_ezero_entity_limit() + ); + (limit, used) + } + + /// @notice Returns a signer capable of operating the GHO direct minter object + /// @return The generated signer for extending the minter object + fun get_direct_minter_signer(): signer acquires GhoDirectMinterData { + let gho_direct_minter_data = + borrow_global(gho_direct_minter_address()); + object::generate_signer_for_extending(&gho_direct_minter_data.extend_ref) + } + + // Test-only functions + #[test_only] + /// @notice Initializes the module for testing + /// @param account The signer account for testing + public fun init_module_for_testing(account: &signer) { + init_module(account); + } +} diff --git a/aave-core/tests/gho-direct-minter/gho_direct_minter.move b/aave-core/tests/gho-direct-minter/gho_direct_minter.move new file mode 100644 index 0000000..8373f7d --- /dev/null +++ b/aave-core/tests/gho-direct-minter/gho_direct_minter.move @@ -0,0 +1,648 @@ +#[test_only] +module aave_pool::gho_direct_minter_tests { + // std + use std::signer; + use std::vector; + use std::option; + use std::string::utf8; + use aptos_framework::event::emitted_events; + use aptos_framework::timestamp; + // imports from the aave-core + use aave_pool::default_reserve_interest_rate_strategy; + use aave_pool::pool_token_logic; + use aave_pool::pool; + use aave_pool::variable_debt_token_factory; + use aave_pool::pool_configurator::Self; + use aave_pool::token_base; + use aave_pool::a_token_factory; + use aave_pool::collector; + use aave_pool::supply_logic::Self; + use aave_pool::borrow_logic::Self; + use aave_pool::pool_fee_manager; + use aave_pool::gho_direct_minter; + use aave_pool::token_helper; + use aave_config::reserve_config; + use aave_oracle::oracle_tests; + use aave_acl::acl_manage as pool_acl_manage; + use gho::gho_token; + use gho::gho_reserve; + use gho_acl::acl_manage as gho_acl_manage; + + const TEST_SUCCESS: u64 = 1; + const TEST_FAILED: u64 = 2; + + fun create_test_gho_reserve(aave_pool: &signer): address { + let test_symbol = b"TEST_GHO"; + let metadata_address = gho_token::get_metadata_address(); + pool_token_logic::test_init_reserve( + aave_pool, + metadata_address, + collector::collector_address(), + option::none(), + utf8(b"Test AGHO"), + utf8(test_symbol), + utf8(b"Test VGHO"), + utf8(test_symbol), + 400, + 100, + 200, + 300 + ); + + // Enable borrowing on the reserve + let reserve_config_new = + pool::get_reserve_configuration(gho_token::get_metadata_address()); + reserve_config::set_reserve_factor(&mut reserve_config_new, 1000); // NOTE: set reserve factor + reserve_config::set_ltv(&mut reserve_config_new, 8000); // NOTE: set ltv + reserve_config::set_debt_ceiling(&mut reserve_config_new, 0); // NOTE: set no debt_ceiling + reserve_config::set_borrowable_in_isolation(&mut reserve_config_new, false); // NOTE: no borrowing in isolation + reserve_config::set_siloed_borrowing(&mut reserve_config_new, false); // NOTE: no siloed borrowing + reserve_config::set_flash_loan_enabled(&mut reserve_config_new, true); // NOTE: enable flashloan + reserve_config::set_borrowing_enabled(&mut reserve_config_new, true); // NOTE: enable borrowing + reserve_config::set_liquidation_threshold(&mut reserve_config_new, 8500); // NOTE: enable liq. threshold + reserve_config::set_liquidation_bonus(&mut reserve_config_new, 10500); // NOTE: enable liq. bonus + pool::test_set_reserve_configuration( + gho_token::get_metadata_address(), reserve_config_new + ); + + a_token_factory::token_address(utf8(test_symbol)) + } + + #[test(account = @0x33)] + #[expected_failure(abort_code = 23, location = aave_pool::gho_direct_minter)] + fun test_init_module(account: &signer) { + gho_direct_minter::init_module_for_testing(account); + } + + #[ + test( + aave_pool = @aave_pool, + aave_acl = @aave_acl, + aave_std = @std, + gho_admin = @gho, + gho_acl_admin = @gho_acl, + gho_guardian = @0x222 + ) + ] + fun test_direct_minter_supply( + aave_pool: &signer, + aave_acl: &signer, + aave_std: &signer, + gho_admin: &signer, + gho_acl_admin: &signer, + gho_guardian: &signer + ) { + // Start the timer + timestamp::set_time_has_started_for_testing(aave_std); + + // Initialize the gho-related entities + gho_acl_manage::test_init_module(gho_acl_admin); + gho_token::test_init_module(gho_admin); + gho_reserve::test_init_module(gho_admin); + gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); + gho_acl_manage::add_facilitator_manager( + gho_acl_admin, signer::address_of(gho_admin) + ); + gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); + + // Init aave pool acl + pool_acl_manage::test_init_module(aave_acl); + // Add the aave_pool as pool admin + pool_acl_manage::add_pool_admin(aave_acl, signer::address_of(aave_pool)); + // Add the gho guardian to the pool acl + pool_acl_manage::add_gho_guardian(aave_acl, signer::address_of(gho_guardian)); + // Init the gho direct minter module + gho_direct_minter::init_module_for_testing(aave_pool); + let gho_reserve_entity = gho_direct_minter::get_direct_minter_signer_address(); + // Add the direct minter to the pool acl as risk admin + pool_acl_manage::add_risk_admin(aave_acl, gho_reserve_entity); + // Add the gho address to the gho direct minter + gho_direct_minter::set_gho_address(aave_pool, gho_token::get_metadata_address()); + + // Init the pool and its components + token_base::test_init_module(aave_pool); + a_token_factory::test_init_module(aave_pool); + variable_debt_token_factory::test_init_module(aave_pool); + pool::test_init_pool(aave_pool); + default_reserve_interest_rate_strategy::init_interest_rate_strategy_for_testing( + aave_pool + ); + pool_fee_manager::init_module_for_testing(aave_pool); + + // Simulate minting gho over the bridge to the gho reserve + let reserve_address = gho_reserve::get_reserve_address(); + let mint_amount = 10_000_000; // 10 gho tokens + gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); + + // Add gho_reserve_entity as an entity with a limit + let entity_limit = 8_000_000; // 8 gho tokens limit + gho_reserve::add_entity(gho_admin, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + + // Check the bucket capacity and level + let limit = gho_reserve::get_limit(gho_reserve_entity); + assert!(limit == entity_limit, TEST_SUCCESS); + + // Add gho as a reserve to the pool + let atoken_address = create_test_gho_reserve(aave_pool); + + // Check the atoken balance of the entity before minting + let gho_reserve_entity_atoken_balance_before = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_before == 0, TEST_SUCCESS); + + // Check the gho balance of the entity before the supply (should be zero) + let gho_reserve_entity_balance_before_supply = + gho_token::get_balance(gho_reserve_entity); + assert!(gho_reserve_entity_balance_before_supply == 0, TEST_SUCCESS); + + // Use and supply GHO + let supply_amount = 1_000_000; // 1 gho token + gho_direct_minter::use_and_supply(gho_guardian, supply_amount); + + let emitted_supply_events = emitted_events(); + assert!(vector::length(&emitted_supply_events) == 1, TEST_SUCCESS); + + // Check the atoken balance of the entity after minting + let gho_reserve_entity_atoken_balance_after = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_after == supply_amount, TEST_SUCCESS); + + // Check the gho balance of the entity after the supply (should be zero) + let gho_balance_after_supply = gho_token::get_balance(gho_reserve_entity); + assert!(gho_balance_after_supply == 0, TEST_SUCCESS); + + // Check the gho balance of the reserve after the withdraw (should be equal to the initial mint amount minus supply amount) + let gho_reserve_balance_after_withdraw = gho_token::get_balance(reserve_address); + assert!( + gho_reserve_balance_after_withdraw == mint_amount - supply_amount, + TEST_SUCCESS + ); + } + + #[ + test( + aave_pool = @aave_pool, + aave_acl = @aave_acl, + aave_std = @std, + gho_admin = @gho, + gho_acl_admin = @gho_acl, + gho_guardian = @0x222 + ) + ] + #[expected_failure(abort_code = 104, location = aave_pool::gho_direct_minter)] + fun test_direct_minter_supply_no_guardian( + aave_pool: &signer, + aave_acl: &signer, + aave_std: &signer, + gho_admin: &signer, + gho_acl_admin: &signer, + gho_guardian: &signer + ) { + // Start the timer + timestamp::set_time_has_started_for_testing(aave_std); + + // Initialize the gho-related entities + gho_acl_manage::test_init_module(gho_acl_admin); + gho_token::test_init_module(gho_admin); + gho_reserve::test_init_module(gho_admin); + gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); + gho_acl_manage::add_facilitator_manager( + gho_acl_admin, signer::address_of(gho_admin) + ); + gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); + + // Init aave pool acl + pool_acl_manage::test_init_module(aave_acl); + // Add the aave_pool as pool admin + pool_acl_manage::add_pool_admin(aave_acl, signer::address_of(aave_pool)); + // Init the gho direct minter module + gho_direct_minter::init_module_for_testing(aave_pool); + let gho_reserve_entity = gho_direct_minter::get_direct_minter_signer_address(); + // Add the direct minter to the pool acl as risk admin + pool_acl_manage::add_risk_admin(aave_acl, gho_reserve_entity); + // Add the gho address to the gho direct minter + gho_direct_minter::set_gho_address(aave_pool, gho_token::get_metadata_address()); + + // Init the pool and its components + token_base::test_init_module(aave_pool); + a_token_factory::test_init_module(aave_pool); + variable_debt_token_factory::test_init_module(aave_pool); + pool::test_init_pool(aave_pool); + default_reserve_interest_rate_strategy::init_interest_rate_strategy_for_testing( + aave_pool + ); + pool_fee_manager::init_module_for_testing(aave_pool); + + // Simulate minting gho over the bridge to the gho reserve + let reserve_address = gho_reserve::get_reserve_address(); + let mint_amount = 10_000_000; // 10 gho tokens + gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); + + // Add gho_reserve_entity as an entity with a limit + let entity_limit = 8_000_000; // 8 gho tokens limit + gho_reserve::add_entity(gho_admin, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + + // Check the bucket capacity and level + let limit = gho_reserve::get_limit(gho_reserve_entity); + assert!(limit == entity_limit, TEST_SUCCESS); + + // Add gho as a reserve to the pool + let atoken_address = create_test_gho_reserve(aave_pool); + + // Check the atoken balance of the entity before minting + let gho_reserve_entity_atoken_balance_before = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_before == 0, TEST_SUCCESS); + + // Check the gho balance of the entity before the supply (should be zero) + let gho_reserve_entity_balance_before_supply = + gho_token::get_balance(gho_reserve_entity); + assert!(gho_reserve_entity_balance_before_supply == 0, TEST_SUCCESS); + + // Use and supply GHO + let supply_amount = 1_000_000; // 1 gho token + gho_direct_minter::use_and_supply(gho_guardian, supply_amount); + } + + #[ + test( + aave_pool = @aave_pool, + aave_acl = @aave_acl, + aave_std = @std, + gho_admin = @gho, + gho_acl_admin = @gho_acl, + gho_guardian = @0x222 + ) + ] + #[expected_failure(abort_code = 105, location = aave_pool::gho_direct_minter)] + fun test_direct_minter_supply_0_bucket_capacity( + aave_pool: &signer, + aave_acl: &signer, + aave_std: &signer, + gho_admin: &signer, + gho_acl_admin: &signer, + gho_guardian: &signer + ) { + // Start the timer + timestamp::set_time_has_started_for_testing(aave_std); + + // Initialize the gho-related entities + gho_acl_manage::test_init_module(gho_acl_admin); + gho_token::test_init_module(gho_admin); + gho_reserve::test_init_module(gho_admin); + gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); + gho_acl_manage::add_facilitator_manager( + gho_acl_admin, signer::address_of(gho_admin) + ); + gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); + + // Init aave pool acl + pool_acl_manage::test_init_module(aave_acl); + // Add the aave_pool as pool admin + pool_acl_manage::add_pool_admin(aave_acl, signer::address_of(aave_pool)); + // Add the gho guardian to the pool acl + pool_acl_manage::add_gho_guardian(aave_acl, signer::address_of(gho_guardian)); + // Init the gho direct minter module + gho_direct_minter::init_module_for_testing(aave_pool); + let gho_reserve_entity = gho_direct_minter::get_direct_minter_signer_address(); + // Add the direct minter to the pool acl as risk admin + pool_acl_manage::add_risk_admin(aave_acl, gho_reserve_entity); + // Add the gho address to the gho direct minter + gho_direct_minter::set_gho_address(aave_pool, gho_token::get_metadata_address()); + + // Init the pool and its components + token_base::test_init_module(aave_pool); + a_token_factory::test_init_module(aave_pool); + variable_debt_token_factory::test_init_module(aave_pool); + pool::test_init_pool(aave_pool); + default_reserve_interest_rate_strategy::init_interest_rate_strategy_for_testing( + aave_pool + ); + pool_fee_manager::init_module_for_testing(aave_pool); + + // Simulate minting gho over the bridge to the gho reserve + let reserve_address = gho_reserve::get_reserve_address(); + let mint_amount = 10_000_000; // 10 gho tokens + gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); + + // Add gho_reserve_entity as an entity with a limit + let entity_limit = 0; // zero limit + gho_reserve::add_entity(gho_admin, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + + // Check the bucket capacity and level + let limit = gho_reserve::get_limit(gho_reserve_entity); + assert!(limit == entity_limit, TEST_SUCCESS); + + // Add gho as a reserve to the pool + let atoken_address = create_test_gho_reserve(aave_pool); + + // Check the atoken balance of the entity before minting + let gho_reserve_entity_atoken_balance_before = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_before == 0, TEST_SUCCESS); + + // Check the gho balance of the entity before the supply (should be zero) + let gho_reserve_entity_balance_before_supply = + gho_token::get_balance(gho_reserve_entity); + assert!(gho_reserve_entity_balance_before_supply == 0, TEST_SUCCESS); + + // Use and supply GHO + let supply_amount = 1_000_000; // 1 gho token + gho_direct_minter::use_and_supply(gho_guardian, supply_amount); + } + + #[ + test( + aave_pool = @aave_pool, + aave_acl = @aave_acl, + aave_std = @std, + gho_admin = @gho, + gho_acl_admin = @gho_acl, + gho_guardian = @0x222 + ) + ] + fun test_direct_minter_supply_withdraw( + aave_pool: &signer, + aave_acl: &signer, + aave_std: &signer, + gho_admin: &signer, + gho_acl_admin: &signer, + gho_guardian: &signer + ) { + // Start the timer + timestamp::set_time_has_started_for_testing(aave_std); + + // Initialize the gho-related entities + gho_acl_manage::test_init_module(gho_acl_admin); + gho_token::test_init_module(gho_admin); + gho_reserve::test_init_module(gho_admin); + gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); + gho_acl_manage::add_facilitator_manager( + gho_acl_admin, signer::address_of(gho_admin) + ); + gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); + + // Init aave pool acl + pool_acl_manage::test_init_module(aave_acl); + // Add the aave_pool as pool admin + pool_acl_manage::add_pool_admin(aave_acl, signer::address_of(aave_pool)); + // Add the gho guardian to the pool acl + pool_acl_manage::add_gho_guardian(aave_acl, signer::address_of(gho_guardian)); + // Init the gho direct minter module + gho_direct_minter::init_module_for_testing(aave_pool); + let gho_reserve_entity = gho_direct_minter::get_direct_minter_signer_address(); + // Add the direct minter to the pool acl as risk admin + pool_acl_manage::add_risk_admin(aave_acl, gho_reserve_entity); + // Add the gho address to the gho direct minter + gho_direct_minter::set_gho_address(aave_pool, gho_token::get_metadata_address()); + + // Init the pool and its components + token_base::test_init_module(aave_pool); + a_token_factory::test_init_module(aave_pool); + variable_debt_token_factory::test_init_module(aave_pool); + pool::test_init_pool(aave_pool); + default_reserve_interest_rate_strategy::init_interest_rate_strategy_for_testing( + aave_pool + ); + pool_fee_manager::init_module_for_testing(aave_pool); + + // Simulate minting gho over the bridge to the gho reserve + let reserve_address = gho_reserve::get_reserve_address(); + let mint_amount = 10_000_000; // 10 gho tokens + gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); + + // Add gho_reserve_entity as an entity with a limit + let entity_limit = 8_000_000; // 8 gho tokens limit + gho_reserve::add_entity(gho_admin, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + + // Check the bucket capacity and level + let limit = gho_reserve::get_limit(gho_reserve_entity); + assert!(limit == entity_limit, TEST_SUCCESS); + + // Add gho as a reserve to the pool + let atoken_address = create_test_gho_reserve(aave_pool); + + // Check the atoken balance of the entity before minting + let gho_reserve_entity_atoken_balance_before = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_before == 0, TEST_SUCCESS); + + // Check the gho balance of the entity before the supply (should be zero) + let gho_reserve_entity_balance_before_supply = + gho_token::get_balance(gho_reserve_entity); + assert!(gho_reserve_entity_balance_before_supply == 0, TEST_SUCCESS); + + // Use and supply GHO + let supply_amount = 1_000_000; // 1 gho token + gho_direct_minter::use_and_supply(gho_guardian, supply_amount); + + let emitted_supply_events = emitted_events(); + assert!(vector::length(&emitted_supply_events) == 1, TEST_SUCCESS); + + // Check the atoken balance of the facilitator after minting + let gho_reserve_entity_atoken_balance_after = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_after == supply_amount, TEST_SUCCESS); + + // Check the gho balance of the facilitator after the supply (should be zero) + let gho_balance_after_supply = gho_token::get_balance(gho_reserve_entity); + assert!(gho_balance_after_supply == 0, TEST_SUCCESS); + + // Check the gho balance of the reserve after the withdraw (should be equal to the initial mint amount minus supply amount) + let gho_reserve_balance_after_withdraw = gho_token::get_balance(reserve_address); + assert!( + gho_reserve_balance_after_withdraw == mint_amount - supply_amount, + TEST_SUCCESS + ); + + // withdaw the supplied GHO + gho_direct_minter::withdraw_and_restore(gho_guardian, supply_amount); + + // Check the atoken balance of the entity after the withdraw (should be zero) + let gho_reserve_entity_atoken_balance_after = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_after == 0, TEST_SUCCESS); + + // Check the gho balance of the entity after the withdraw (should be zero) + let gho_balance_after_withdraw = gho_token::get_balance(gho_reserve_entity); + assert!(gho_balance_after_withdraw == 0, TEST_SUCCESS); + + // Check the gho balance of the reserve after the withdraw (should be equal to the initial mint amount) + let gho_reserve_balance_after_withdraw = gho_token::get_balance(reserve_address); + assert!(gho_reserve_balance_after_withdraw == mint_amount, TEST_SUCCESS); + } + + #[ + test( + aave_pool = @aave_pool, + aave_acl = @aave_acl, + aave_std = @std, + aave_oracle = @aave_oracle, + data_feeds = @data_feeds, + platform = @platform, + gho_admin = @gho, + gho_acl_admin = @gho_acl, + gho_guardian = @0x222, + gho_borrower = @0x333 + ) + ] + fun test_direct_minter_supply_transfer_excess_to_treasury( + aave_pool: &signer, + aave_acl: &signer, + aave_std: &signer, + aave_oracle: &signer, + data_feeds: &signer, + platform: &signer, + gho_admin: &signer, + gho_acl_admin: &signer, + gho_guardian: &signer, + gho_borrower: &signer + ) { + // Start the timer + timestamp::set_time_has_started_for_testing(aave_std); + + // Initialize the gho-related entities + gho_acl_manage::test_init_module(gho_acl_admin); + gho_token::test_init_module(gho_admin); + gho_reserve::test_init_module(gho_admin); + gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); + gho_acl_manage::add_facilitator_manager( + gho_acl_admin, signer::address_of(gho_admin) + ); + gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); + + // Init aave pool acl + pool_acl_manage::test_init_module(aave_acl); + // Add the aave_pool as pool admin + pool_acl_manage::add_pool_admin(aave_acl, signer::address_of(aave_pool)); + pool_acl_manage::add_asset_listing_admin( + aave_acl, signer::address_of(aave_pool) + ); + // Add the gho guardian to the pool acl + pool_acl_manage::add_gho_guardian(aave_acl, signer::address_of(gho_guardian)); + // Init the gho direct minter module + gho_direct_minter::init_module_for_testing(aave_pool); + let gho_reserve_entity = gho_direct_minter::get_direct_minter_signer_address(); + // Add the direct minter to the pool acl as risk admin + pool_acl_manage::add_risk_admin(aave_acl, gho_reserve_entity); + // Add the gho address to the gho direct minter + gho_direct_minter::set_gho_address(aave_pool, gho_token::get_metadata_address()); + + // Init the pool and its components + token_base::test_init_module(aave_pool); + a_token_factory::test_init_module(aave_pool); + variable_debt_token_factory::test_init_module(aave_pool); + pool_fee_manager::init_module_for_testing(aave_pool); + collector::init_module_test(aave_pool); + pool_configurator::test_init_module(aave_pool); + + // Configure the oracle + oracle_tests::config_oracle(aave_oracle, data_feeds, platform); + // Set GHO price + token_helper::set_asset_price( + aave_acl, + aave_oracle, + gho_direct_minter::get_gho_token_address(), + 100 + ); + + // Simulate minting gho over the bridge to the gho reserve + let reserve_address = gho_reserve::get_reserve_address(); + let mint_amount = 10_000_000; // 10 gho tokens + gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); + + // Mint some tokens for the borrower + gho_token::test_mint_directly_to_user( + gho_admin, signer::address_of(gho_borrower), mint_amount + ); + + // Add gho_reserve_entity as an entity with a limit + let entity_limit = 8_000_000; // 8 gho tokens limit + gho_reserve::add_entity(gho_admin, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + + // Check the bucket capacity and level + let limit = gho_reserve::get_limit(gho_reserve_entity); + assert!(limit == entity_limit, TEST_SUCCESS); + + // Add gho as a reserve to the pool + let atoken_address = create_test_gho_reserve(aave_pool); + + // Check the atoken balance of the entity before minting + let gho_reserve_entity_atoken_balance_before = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_before == 0, TEST_SUCCESS); + + // Check the gho balance of the entity before the supply (should be zero) + let gho_reserve_entity_balance_before_supply = + gho_token::get_balance(gho_reserve_entity); + assert!(gho_reserve_entity_balance_before_supply == 0, TEST_SUCCESS); + + // Use and supply GHO + let supply_amount = 7_000_000; // 7 gho tokens + gho_direct_minter::use_and_supply(gho_guardian, supply_amount); + + let emitted_supply_events = emitted_events(); + assert!(vector::length(&emitted_supply_events) == 1, TEST_SUCCESS); + + // Check the atoken balance of the entity after minting + let gho_reserve_entity_atoken_balance_after = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!(gho_reserve_entity_atoken_balance_after == supply_amount, TEST_SUCCESS); + + // Check the gho balance of the entity after the supply (should be zero) + let gho_balance_after_supply = gho_token::get_balance(gho_reserve_entity); + assert!(gho_balance_after_supply == 0, TEST_SUCCESS); + + // Check the gho balance of the reserve after the withdraw (should be equal to the initial mint amount minus supply amount) + let gho_reserve_balance_after_withdraw = gho_token::get_balance(reserve_address); + assert!( + gho_reserve_balance_after_withdraw == mint_amount - supply_amount, + TEST_SUCCESS + ); + + // The borrower will supply some gho as collateral and borrow some to generate interest + supply_logic::supply( + gho_borrower, + gho_direct_minter::get_gho_token_address(), + 5_000_000, + signer::address_of(gho_borrower), + 0 + ); + + // Enable the gho as collateral for the borrower + supply_logic::set_user_use_reserve_as_collateral( + gho_borrower, gho_direct_minter::get_gho_token_address(), true + ); + + // Make a borrow to generate some interest + let borrow_amount = 1_000_000; // 1 gho tokens + borrow_logic::borrow( + gho_borrower, + gho_direct_minter::get_gho_token_address(), + borrow_amount, + 2, + 0, + signer::address_of(gho_borrower) + ); + + // Fast forward time by 1 year to accrue some interest + let seconds_per_year = 24 * 60 * 60 * 365; + timestamp::fast_forward_seconds(seconds_per_year); + + // Check the atoken balance of the entity after some interest has been accrued + let a_token_entity_balance_after = + a_token_factory::balance_of(gho_reserve_entity, atoken_address); + assert!( + a_token_entity_balance_after > supply_amount, + TEST_SUCCESS + ); + + // Transfer the excess to the treasury + // gho_direct_minter::transfer_excess_to_treasury(); // FIXME: the atoken stores are frozen + } +} diff --git a/aave-test-kit/Dockerfile b/aave-test-kit/Dockerfile index 00399d8..9c3a555 100644 --- a/aave-test-kit/Dockerfile +++ b/aave-test-kit/Dockerfile @@ -36,6 +36,9 @@ ENV APTOS_NETWORK=local \ DATA_FEEDS_PRIVATE_KEY=0x0 \ PLATFORM_PRIVATE_KEY=0x0 \ DEFAULT_PRIVATE_KEY=0x0 \ + GHO_PRIVATE_KEY=0x0 \ + GHO_CONFIG_PRIVATE_KEY=0x0 \ + GHO_ACL_PRIVATE_KEY=0x0 \ TEST_ACCOUNT_0_PRIVATE_KEY=0x0 \ TEST_ACCOUNT_1_PRIVATE_KEY=0x0 \ TEST_ACCOUNT_2_PRIVATE_KEY=0x0 \ diff --git a/aave-test-kit/docker-compose.yml b/aave-test-kit/docker-compose.yml index ca720ec..3b20f37 100644 --- a/aave-test-kit/docker-compose.yml +++ b/aave-test-kit/docker-compose.yml @@ -66,6 +66,9 @@ services: AAVE_POOL_PRIVATE_KEY: 0x0 AAVE_MOCK_UNDERLYING_TOKENS_PRIVATE_KEY: 0x0 AAVE_LARGE_PACKAGES_PRIVATE_KEY: 0x0 + GHO_PRIVATE_KEY: 0x0 + GHO_CONFIG_PRIVATE_KEY: 0x0 + GHO_ACL_PRIVATE_KEY: 0x0 networks: - aptos-local-testnet-network diff --git a/examples/Makefile b/examples/Makefile index 55021e8..3f0c980 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -10,7 +10,7 @@ compile-scripts-testnet: --skip-fetch-latest-git-deps \ --language-version "$(MOVE_VERSION)" \ --compiler-version "$(COMPILER_VERSION)" \ - --named-addresses "aave_acl=0xb23539ad6490a465e92e751943a3eaedf4b48d7d844ff59adf2ae66bcb09f53d,aave_config=0x4fb5d8348c8873295f97136bbe1c43d976fb18a4a966a85e21d16958eaecef99,aave_math=0xf6f896cefd7b1b1e85ff56033981cf92dcd5d6e93b1349a7ab5003761c52498d,aave_oracle=0xcb9eb79a52f41933192c2e1e37a9e72bfd726fdb9a687cd6cfe45527e52f4e41,aave_pool=0xbd7912c555a06809c2e385eab635ff0ef52b1fa062ce865c785c67694a12bb12,aave_large_packages=0x0e1ca3011bdd07246d4d16d909dbb2d6953a86c4735d5acf5865d962c630cce7,aave_mock_underlyings=0xe2b42cab2f84bf57edaf87bcaffee409c2b3d5243e3def00d9d2f7dec568d867,aave_data=0xb0ec364235f47ad2a8eb52d639c80579b11497d0711879840f1ce51c885b165f,data_feeds=0xf1099f135ddddad1c065203431be328a408b0ca452ada70374ce26bd2b32fdd3,platform=0x516e771e1b4a903afe74c27d057c65849ecc1383782f6642d7ff21425f4f9c99,aave_oracle_racc_address=0x3836084d178459c08f27620462d531f5b5b23d14ddbf4f47c4a72d404d87bcb4" + --named-addresses "aave_acl=0xb23539ad6490a465e92e751943a3eaedf4b48d7d844ff59adf2ae66bcb09f53d,aave_config=0x4fb5d8348c8873295f97136bbe1c43d976fb18a4a966a85e21d16958eaecef99,aave_math=0xf6f896cefd7b1b1e85ff56033981cf92dcd5d6e93b1349a7ab5003761c52498d,aave_oracle=0xcb9eb79a52f41933192c2e1e37a9e72bfd726fdb9a687cd6cfe45527e52f4e41,aave_pool=0xbd7912c555a06809c2e385eab635ff0ef52b1fa062ce865c785c67694a12bb12,aave_large_packages=0x0e1ca3011bdd07246d4d16d909dbb2d6953a86c4735d5acf5865d962c630cce7,aave_mock_underlyings=0xe2b42cab2f84bf57edaf87bcaffee409c2b3d5243e3def00d9d2f7dec568d867,aave_data=0xb0ec364235f47ad2a8eb52d639c80579b11497d0711879840f1ce51c885b165f,data_feeds=0xf1099f135ddddad1c065203431be328a408b0ca452ada70374ce26bd2b32fdd3,platform=0x516e771e1b4a903afe74c27d057c65849ecc1383782f6642d7ff21425f4f9c99,aave_oracle_racc_address=0x3836084d178459c08f27620462d531f5b5b23d14ddbf4f47c4a72d404d87bcb4,gho=0x111,gho_config=0x112,gho_acl=0x113" compile-scripts-mainnet: aptos move compile \ @@ -19,7 +19,7 @@ compile-scripts-mainnet: --skip-fetch-latest-git-deps \ --language-version "$(MOVE_VERSION)" \ --compiler-version "$(COMPILER_VERSION)" \ - --named-addresses "aave_acl=0x34c3e6af238f3a7fa3f3b0088cbc4b194d21f62e65a15b79ae91364de5a81a3a,aave_config=0x531069f4741cdead39d70b76e5779863864654fae6db8a752a244ff2f9916c15,aave_math=0xc0338eea778de2a5348824ddbfcec033c7f7cbe18da6da40869562906b63c78c,aave_oracle=0x249676f3faddb83d64fd101baa3f84a171ae02505d796e3edbf4861038a4b5cc,aave_pool=0x39ddcd9e1a39fa14f25e3f9ec8a86074d05cc0881cbf667df8a6ee70942016fb,aave_large_packages=0x0e1ca3011bdd07246d4d16d909dbb2d6953a86c4735d5acf5865d962c630cce7,aave_mock_underlyings=0x12b05c42ac3209a3c6ffadff4ebb6c3e983e5115f26031d56652815b49a14245,aave_data=0x5eb5cc775c5a446db0f3a1c944e11563b97e6a7e1387b9fb459aa26168f738dc,data_feeds=0x3f985798ce4975f430ef5c75776ff98a77b9f9d0fb38184d225adc9c1cc6b79b,platform=0x9976bb288ed9177b542d568fa1ac386819dc99141630e582315804840f41928a,aave_oracle_racc_address=0x3836084d178459c08f27620462d531f5b5b23d14ddbf4f47c4a72d404d87bcb4" + --named-addresses "aave_acl=0x34c3e6af238f3a7fa3f3b0088cbc4b194d21f62e65a15b79ae91364de5a81a3a,aave_config=0x531069f4741cdead39d70b76e5779863864654fae6db8a752a244ff2f9916c15,aave_math=0xc0338eea778de2a5348824ddbfcec033c7f7cbe18da6da40869562906b63c78c,aave_oracle=0x249676f3faddb83d64fd101baa3f84a171ae02505d796e3edbf4861038a4b5cc,aave_pool=0x39ddcd9e1a39fa14f25e3f9ec8a86074d05cc0881cbf667df8a6ee70942016fb,aave_large_packages=0x0e1ca3011bdd07246d4d16d909dbb2d6953a86c4735d5acf5865d962c630cce7,aave_mock_underlyings=0x12b05c42ac3209a3c6ffadff4ebb6c3e983e5115f26031d56652815b49a14245,aave_data=0x5eb5cc775c5a446db0f3a1c944e11563b97e6a7e1387b9fb459aa26168f738dc,data_feeds=0x3f985798ce4975f430ef5c75776ff98a77b9f9d0fb38184d225adc9c1cc6b79b,platform=0x9976bb288ed9177b542d568fa1ac386819dc99141630e582315804840f41928a,aave_oracle_racc_address=0x3836084d178459c08f27620462d531f5b5b23d14ddbf4f47c4a72d404d87bcb4,gho=0x111,gho_config=0x112,gho_acl=0x113" execute-flashloan-simple-dry-run: aptos move run-script \ From 5b55f32ab50dd2a93dbdae2c5d5d79a67427ff2c Mon Sep 17 00:00:00 2001 From: mpsc0x Date: Wed, 3 Sep 2025 14:19:30 +0300 Subject: [PATCH 2/9] feat: added atoken proper transfer functionality --- .../sources/aave-tokens/a_token_factory.move | 16 +++++ .../gho-direct-minter/gho_direct_minter.move | 31 ++++------ ...nter.move => gho_direct_minter_tests.move} | 58 +++++++++---------- 3 files changed, 57 insertions(+), 48 deletions(-) rename aave-core/tests/gho-direct-minter/{gho_direct_minter.move => gho_direct_minter_tests.move} (94%) diff --git a/aave-core/sources/aave-tokens/a_token_factory.move b/aave-core/sources/aave-tokens/a_token_factory.move index 9228730..91f7efb 100644 --- a/aave-core/sources/aave-tokens/a_token_factory.move +++ b/aave-core/sources/aave-tokens/a_token_factory.move @@ -32,6 +32,7 @@ module aave_pool::a_token_factory { friend aave_pool::supply_logic; friend aave_pool::borrow_logic; friend aave_pool::liquidation_logic; + friend aave_pool::gho_direct_minter; #[test_only] friend aave_pool::a_token_factory_tests; @@ -617,6 +618,21 @@ module aave_pool::a_token_factory { ); } + /// @notice Transfers out any token from an aToken's resource account to a receiver + /// @param token The address of the token to transfer + /// @param to The address of the recipient + /// @param amount The amount of token to transfer + /// @param metadata_address The address of the aToken + public(friend) fun transfer_atokens( + from: address, + to: address, + amount: u256, + index: u256, + metadata_address: address + ) acquires TokenMap { + transfer_on_liquidation(from, to, amount, index, metadata_address); + } + /// @notice Drops the a token associated data /// @dev Only callable by the pool_token_logic module /// @param metadata_address The address of the metadata object diff --git a/aave-core/sources/gho-direct-minter/gho_direct_minter.move b/aave-core/sources/gho-direct-minter/gho_direct_minter.move index 5956042..295acc1 100644 --- a/aave-core/sources/gho-direct-minter/gho_direct_minter.move +++ b/aave-core/sources/gho-direct-minter/gho_direct_minter.move @@ -4,9 +4,6 @@ module aave_pool::gho_direct_minter { // Std imports use std::signer; - use aptos_framework::dispatchable_fungible_asset; - use aptos_framework::primary_fungible_store; - use aptos_framework::fungible_asset::Metadata; use aptos_framework::object::{ Self, ExtendRef as ObjExtendRef, @@ -126,7 +123,7 @@ module aave_pool::gho_direct_minter { // check: gho_reserve_entity must have RISK ADMIN ROLE for the pool is_risk_admin(gho_reserve_entity); - // check: the gho_reserve_entity must be registered as a Facilitator with a non zero bucket capacity + // check: the gho_reserve_entity must be registered as a entity with a non zero limit let (_, _) = ensure_entity_gho_usage(gho_reserve_entity); // withdraw GHO underlying asset i.e. burn Atokens and send back the underlying to the primary store of the gho_reserve_entity @@ -173,21 +170,17 @@ module aave_pool::gho_direct_minter { if (excess_level == 0) { return; }; - - // transfer the excess to the treasury - let a_token_metadata = object::address_to_object(a_token_address); - let store_from = - primary_fungible_store::primary_store(gho_reserve_entity, a_token_metadata); - let store_to = - primary_fungible_store::ensure_primary_store_exists( - gho_direct_minter_data.collector_address, a_token_metadata - ); - - dispatchable_fungible_asset::transfer( - &minter_signer, - store_from, - store_to, - (excess_level as u64) + // get the underlying asset index + let underlying_asset = + a_token_factory::get_underlying_asset_address(a_token_address); + let index = pool::get_reserve_normalized_income(underlying_asset); + // Transfer the excess level of atokens from the gho_reserve_entity to the collector address + a_token_factory::transfer_atokens( + gho_reserve_entity, + gho_direct_minter_data.collector_address, + excess_level, + index, + a_token_address ); } diff --git a/aave-core/tests/gho-direct-minter/gho_direct_minter.move b/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move similarity index 94% rename from aave-core/tests/gho-direct-minter/gho_direct_minter.move rename to aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move index 8373f7d..c5afae1 100644 --- a/aave-core/tests/gho-direct-minter/gho_direct_minter.move +++ b/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move @@ -100,10 +100,6 @@ module aave_pool::gho_direct_minter_tests { gho_token::test_init_module(gho_admin); gho_reserve::test_init_module(gho_admin); gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); - gho_acl_manage::add_facilitator_manager( - gho_acl_admin, signer::address_of(gho_admin) - ); - gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); // Init aave pool acl pool_acl_manage::test_init_module(aave_acl); @@ -139,7 +135,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the bucket capacity and level + // Check the entity limit let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -207,10 +203,6 @@ module aave_pool::gho_direct_minter_tests { gho_token::test_init_module(gho_admin); gho_reserve::test_init_module(gho_admin); gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); - gho_acl_manage::add_facilitator_manager( - gho_acl_admin, signer::address_of(gho_admin) - ); - gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); // Init aave pool acl pool_acl_manage::test_init_module(aave_acl); @@ -244,7 +236,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the bucket capacity and level + // Check the enitty level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -277,7 +269,7 @@ module aave_pool::gho_direct_minter_tests { ) ] #[expected_failure(abort_code = 105, location = aave_pool::gho_direct_minter)] - fun test_direct_minter_supply_0_bucket_capacity( + fun test_direct_minter_supply_0_entity_limit( aave_pool: &signer, aave_acl: &signer, aave_std: &signer, @@ -293,10 +285,6 @@ module aave_pool::gho_direct_minter_tests { gho_token::test_init_module(gho_admin); gho_reserve::test_init_module(gho_admin); gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); - gho_acl_manage::add_facilitator_manager( - gho_acl_admin, signer::address_of(gho_admin) - ); - gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); // Init aave pool acl pool_acl_manage::test_init_module(aave_acl); @@ -332,7 +320,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the bucket capacity and level + // Check the enitty level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -380,10 +368,6 @@ module aave_pool::gho_direct_minter_tests { gho_token::test_init_module(gho_admin); gho_reserve::test_init_module(gho_admin); gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); - gho_acl_manage::add_facilitator_manager( - gho_acl_admin, signer::address_of(gho_admin) - ); - gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); // Init aave pool acl pool_acl_manage::test_init_module(aave_acl); @@ -419,7 +403,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the bucket capacity and level + // Check the enitty level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -443,12 +427,12 @@ module aave_pool::gho_direct_minter_tests { let emitted_supply_events = emitted_events(); assert!(vector::length(&emitted_supply_events) == 1, TEST_SUCCESS); - // Check the atoken balance of the facilitator after minting + // Check the atoken balance of the entity after minting let gho_reserve_entity_atoken_balance_after = a_token_factory::balance_of(gho_reserve_entity, atoken_address); assert!(gho_reserve_entity_atoken_balance_after == supply_amount, TEST_SUCCESS); - // Check the gho balance of the facilitator after the supply (should be zero) + // Check the gho balance of the entity after the supply (should be zero) let gho_balance_after_supply = gho_token::get_balance(gho_reserve_entity); assert!(gho_balance_after_supply == 0, TEST_SUCCESS); @@ -510,10 +494,6 @@ module aave_pool::gho_direct_minter_tests { gho_token::test_init_module(gho_admin); gho_reserve::test_init_module(gho_admin); gho_acl_manage::add_default_admin(gho_acl_admin, signer::address_of(gho_admin)); - gho_acl_manage::add_facilitator_manager( - gho_acl_admin, signer::address_of(gho_admin) - ); - gho_acl_manage::add_bucket_manager(gho_acl_admin, signer::address_of(gho_admin)); // Init aave pool acl pool_acl_manage::test_init_module(aave_acl); @@ -565,7 +545,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the bucket capacity and level + // Check the enitty level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -642,7 +622,27 @@ module aave_pool::gho_direct_minter_tests { TEST_SUCCESS ); + // Check the atoken balance of the collector before the transfer is exactly zero + let a_token_collector_balance_before = + a_token_factory::balance_of( + gho_direct_minter::get_collector_address(), atoken_address + ); + assert!( + a_token_collector_balance_before == 0, + TEST_SUCCESS + ); + // Transfer the excess to the treasury - // gho_direct_minter::transfer_excess_to_treasury(); // FIXME: the atoken stores are frozen + gho_direct_minter::transfer_excess_to_treasury(); + + // Check the atoken balance of the collector after the transfer is greater than zero + let a_token_collector_balance_after = + a_token_factory::balance_of( + gho_direct_minter::get_collector_address(), atoken_address + ); + assert!( + a_token_collector_balance_after > 0, + TEST_SUCCESS + ); } } From 5105a5782a6d1475e498c3d6a89320d1acfcaa41 Mon Sep 17 00:00:00 2001 From: mpsc0x Date: Thu, 4 Sep 2025 13:36:29 +0300 Subject: [PATCH 3/9] added git token for unit tests --- .github/workflows/unit_tests.yml | 12 ++++++++++++ .../gho-direct-minter/gho_direct_minter_tests.move | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 6c720f5..d70b3f3 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -41,6 +41,18 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + + - name: Auth for private HTTPS git deps + run: | + git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/" + - name: Install Aptos CLI uses: ./.github/actions/install-aptos-cli with: diff --git a/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move b/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move index c5afae1..8aad3ed 100644 --- a/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move +++ b/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move @@ -236,7 +236,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the enitty level + // Check the entity level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -320,7 +320,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the enitty level + // Check the entity level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -403,7 +403,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the enitty level + // Check the entity level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); @@ -545,7 +545,7 @@ module aave_pool::gho_direct_minter_tests { gho_reserve::add_entity(gho_admin, gho_reserve_entity); gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); - // Check the enitty level + // Check the entity level let limit = gho_reserve::get_limit(gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); From dbcbca0af64f949bf9d031ea9f78bcbebf940fe8 Mon Sep 17 00:00:00 2001 From: mpsc0x Date: Thu, 4 Sep 2025 14:11:17 +0300 Subject: [PATCH 4/9] added remaining gho workflow integrations --- .github/workflows/doc.yml | 9 +++++++++ .github/workflows/examples.yml | 8 ++++++++ .github/workflows/move-audit.yml | 9 +++++++++ .github/workflows/move-v2.yml | 9 +++++++++ .github/workflows/testnet-deployment.yml | 8 ++++++++ .github/workflows/typescript-integration-tests.yml | 8 ++++++++ examples/Move.toml | 1 + 7 files changed, 52 insertions(+) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index eda375c..874655d 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -38,6 +38,15 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v4 + + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + - name: Install Aptos CLI uses: ./.github/actions/install-aptos-cli with: diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 849338f..607e89f 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -24,6 +24,14 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + - name: Install Aptos CLI uses: ./.github/actions/install-aptos-cli with: diff --git a/.github/workflows/move-audit.yml b/.github/workflows/move-audit.yml index a78d662..07c86a1 100644 --- a/.github/workflows/move-audit.yml +++ b/.github/workflows/move-audit.yml @@ -39,6 +39,15 @@ jobs: DEFAULT_FUNDER_PRIVATE_KEY: ${{ secrets.GH_DEFAULT_FUNDER_PRIVATE_KEY }} steps: - uses: actions/checkout@v4 + + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + - name: Install latest Aptos CLI run: | curl -fsSL "https://aptos.dev/scripts/install_cli.py" | python3 diff --git a/.github/workflows/move-v2.yml b/.github/workflows/move-v2.yml index fb47d7b..2cfe1e1 100644 --- a/.github/workflows/move-v2.yml +++ b/.github/workflows/move-v2.yml @@ -39,6 +39,15 @@ jobs: DEFAULT_FUNDER_PRIVATE_KEY: ${{ secrets.GH_DEFAULT_FUNDER_PRIVATE_KEY }} steps: - uses: actions/checkout@v4 + + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + - name: Install latest Aptos CLI run: | curl -fsSL "https://aptos.dev/scripts/install_cli.py" | python3 diff --git a/.github/workflows/testnet-deployment.yml b/.github/workflows/testnet-deployment.yml index dd445de..304995e 100644 --- a/.github/workflows/testnet-deployment.yml +++ b/.github/workflows/testnet-deployment.yml @@ -57,6 +57,14 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + - uses: dtolnay/rust-toolchain@stable - name: Install dependencies diff --git a/.github/workflows/typescript-integration-tests.yml b/.github/workflows/typescript-integration-tests.yml index 057deed..c60af10 100644 --- a/.github/workflows/typescript-integration-tests.yml +++ b/.github/workflows/typescript-integration-tests.yml @@ -50,6 +50,14 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.GH_ACTIONS_APP_ID }} + private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "aptos-aave-gho" + - name: Install dependencies run: | sudo apt-get update -y diff --git a/examples/Move.toml b/examples/Move.toml index 220c2e5..a51839b 100644 --- a/examples/Move.toml +++ b/examples/Move.toml @@ -18,5 +18,6 @@ AaveConfig = { local = "../aave-core/aave-config" } AaveOracle = { local = "../aave-core/aave-oracle" } AaveData = { local = "../aave-core/aave-data" } AavePool = { local = "../aave-core" } +Gho_Token = { git = "https://github.com/aave/aptos-aave-gho", subdir = "Gho", rev = "main" } [dev-dependencies] From ea1af6e27e0ebe5e4e8a355cebe6f949bf2bbf99 Mon Sep 17 00:00:00 2001 From: Sam Skynner <44842711+skynnes@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:32:33 +0100 Subject: [PATCH 5/9] Update typescript-integration-tests.yml fix: add token to git config for actions workflow --- .github/workflows/typescript-integration-tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/typescript-integration-tests.yml b/.github/workflows/typescript-integration-tests.yml index c60af10..95833d6 100644 --- a/.github/workflows/typescript-integration-tests.yml +++ b/.github/workflows/typescript-integration-tests.yml @@ -58,6 +58,10 @@ jobs: owner: ${{ github.repository_owner }} repositories: "aptos-aave-gho" + - name: Auth for private HTTPS git deps + run: | + git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/" + - name: Install dependencies run: | sudo apt-get update -y From df76d394b74bfab589edc1591c0d361c170ee536 Mon Sep 17 00:00:00 2001 From: Sam Skynner <44842711+skynnes@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:34:08 +0100 Subject: [PATCH 6/9] fix: testnet-deployment action --- .github/workflows/testnet-deployment.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/testnet-deployment.yml b/.github/workflows/testnet-deployment.yml index 304995e..67cdece 100644 --- a/.github/workflows/testnet-deployment.yml +++ b/.github/workflows/testnet-deployment.yml @@ -65,6 +65,10 @@ jobs: owner: ${{ github.repository_owner }} repositories: "aptos-aave-gho" + - name: Auth for private HTTPS git deps + run: | + git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/" + - uses: dtolnay/rust-toolchain@stable - name: Install dependencies From ea738438e6fe9c407335a7a27718680d3908cee5 Mon Sep 17 00:00:00 2001 From: Sam Skynner <44842711+skynnes@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:36:23 +0100 Subject: [PATCH 7/9] fix: Build documentation Action --- .github/workflows/doc.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 874655d..b902069 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -47,6 +47,10 @@ jobs: owner: ${{ github.repository_owner }} repositories: "aptos-aave-gho" + - name: Auth for private HTTPS git deps + run: | + git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/" + - name: Install Aptos CLI uses: ./.github/actions/install-aptos-cli with: From 5ec5926a0fd7fb75d81bed7a850464049654ee2c Mon Sep 17 00:00:00 2001 From: Sam Skynner <44842711+skynnes@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:37:08 +0100 Subject: [PATCH 8/9] dfix: examples github action --- .github/workflows/examples.yml | 4 ++++ .pre-commit-config.yaml | 6 +++++- .secrets.baseline | 24 +++++++----------------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 607e89f..fbbb534 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -31,6 +31,10 @@ jobs: private-key: ${{ secrets.GH_ACTIONS_APP_PRIVATE_KEY }} owner: ${{ github.repository_owner }} repositories: "aptos-aave-gho" + + - name: Auth for private HTTPS git deps + run: | + git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/" - name: Install Aptos CLI uses: ./.github/actions/install-aptos-cli diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23114ad..e74e8b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -55,6 +55,7 @@ repos: - .secrets.baseline - --exclude-lines - '\bgho_[a-z_]+\b' + - 'get_e[a-z_]+\b' exclude: | (?x)^( \.secrets\.baseline| @@ -75,7 +76,10 @@ repos: \.git/.*| coverage/.*| build/.*| - dist/.* + dist/.*| + .*\.move$| + docker-compose.*\.yml$| + docker-compose.*\.yaml$ )$ # Note: TruffleHog runs in CI/CD pipeline via GitHub Actions diff --git a/.secrets.baseline b/.secrets.baseline index 46f7836..cf24bc9 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -124,6 +124,12 @@ }, { "path": "detect_secrets.filters.heuristic.is_templated_secret" + }, + { + "path": "detect_secrets.filters.regex.should_exclude_line", + "pattern": [ + "\\bgho_[a-z_]+\\b" + ] } ], "results": { @@ -145,22 +151,6 @@ "line_number": 112 } ], - "aave-core/chainlink-data-feeds/Move.toml": [ - { - "type": "Hex High Entropy String", - "filename": "aave-core/chainlink-data-feeds/Move.toml", - "hashed_secret": "624a21385c45fc68021078b856bdb7ef77f792e7", - "is_verified": false, - "line_number": 7 - }, - { - "type": "Hex High Entropy String", - "filename": "aave-core/chainlink-data-feeds/Move.toml", - "hashed_secret": "5836c8421a57080f8fed06b76343a66ba79eb007", - "is_verified": false, - "line_number": 9 - } - ], "aave-core/chainlink-platform/Move.toml": [ { "type": "Hex High Entropy String", @@ -11151,5 +11141,5 @@ } ] }, - "generated_at": "2025-09-02T11:33:59Z" + "generated_at": "2025-09-04T14:21:40Z" } From 87f856a0f6c74f425ae187c7d38b08ab9a1ff94c Mon Sep 17 00:00:00 2001 From: Harsh Date: Tue, 9 Sep 2025 10:44:36 +0100 Subject: [PATCH 9/9] feat: added getter for gho_reserve --- .../gho-direct-minter/gho_direct_minter.move | 15 +++++-- .../gho_direct_minter_tests.move | 41 ++++++++++--------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/aave-core/sources/gho-direct-minter/gho_direct_minter.move b/aave-core/sources/gho-direct-minter/gho_direct_minter.move index 295acc1..0431a3b 100644 --- a/aave-core/sources/gho-direct-minter/gho_direct_minter.move +++ b/aave-core/sources/gho-direct-minter/gho_direct_minter.move @@ -25,6 +25,9 @@ module aave_pool::gho_direct_minter { // Constants /// @notice Name for the GHO direct minter object const GHO_DIRECT_MINTER_NAME: vector = b"GHO_DIRECT_MINTER"; + /// @notice seed for deriving the GHO reserve address + const GHO_RESERVE_SEED: vector = b"GHO_RESERVE"; + // Structs #[resource_group_member(group = ObjectGroup)] @@ -81,8 +84,11 @@ module aave_pool::gho_direct_minter { // 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); + // get the reserve address + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); + // use GHO from the gho reserve - gho_reserve::use_gho(&minter_signer, amount); + gho_reserve::use_gho(&minter_signer, reserve_address, amount); // temporarily set the supply cap to 0 to disable it while supplying let old_supply_cap = @@ -133,9 +139,11 @@ module aave_pool::gho_direct_minter { amount, signer::address_of(&minter_signer) ); + // get the reserve address + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); // restore the gho amount back to the gho reserve (i.e. the burning) - gho_reserve::restore(&minter_signer, amount); + gho_reserve::restore(&minter_signer,reserve_address, amount); } /// @notice Transfers any excess GHO (over current usage) from the reserve entity to the treasury @@ -303,7 +311,8 @@ module aave_pool::gho_direct_minter { /// @param entity The reserve entity address /// @return (limit, used) usage tuple fun ensure_entity_gho_usage(entity: address): (u256, u256) { - let (limit, used) = gho_reserve::get_usage(entity); + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); + let (limit, used) = gho_reserve::get_usage(reserve_address,entity); assert!( limit > 0, error_config::get_ezero_entity_limit() diff --git a/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move b/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move index 8aad3ed..689034b 100644 --- a/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move +++ b/aave-core/tests/gho-direct-minter/gho_direct_minter_tests.move @@ -30,6 +30,7 @@ module aave_pool::gho_direct_minter_tests { const TEST_SUCCESS: u64 = 1; const TEST_FAILED: u64 = 2; + const GHO_RESERVE_SEED: vector = b"GHO_RESERVE"; fun create_test_gho_reserve(aave_pool: &signer): address { let test_symbol = b"TEST_GHO"; @@ -126,17 +127,17 @@ module aave_pool::gho_direct_minter_tests { pool_fee_manager::init_module_for_testing(aave_pool); // Simulate minting gho over the bridge to the gho reserve - let reserve_address = gho_reserve::get_reserve_address(); + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); let mint_amount = 10_000_000; // 10 gho tokens gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); // Add gho_reserve_entity as an entity with a limit let entity_limit = 8_000_000; // 8 gho tokens limit - gho_reserve::add_entity(gho_admin, gho_reserve_entity); - gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + gho_reserve::add_entity(gho_admin, reserve_address, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, reserve_address, gho_reserve_entity, entity_limit); // Check the entity limit - let limit = gho_reserve::get_limit(gho_reserve_entity); + let limit = gho_reserve::get_limit(reserve_address,gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); // Add gho as a reserve to the pool @@ -227,17 +228,17 @@ module aave_pool::gho_direct_minter_tests { pool_fee_manager::init_module_for_testing(aave_pool); // Simulate minting gho over the bridge to the gho reserve - let reserve_address = gho_reserve::get_reserve_address(); + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); let mint_amount = 10_000_000; // 10 gho tokens gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); // Add gho_reserve_entity as an entity with a limit let entity_limit = 8_000_000; // 8 gho tokens limit - gho_reserve::add_entity(gho_admin, gho_reserve_entity); - gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + gho_reserve::add_entity(gho_admin, reserve_address, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, reserve_address, gho_reserve_entity, entity_limit); // Check the entity level - let limit = gho_reserve::get_limit(gho_reserve_entity); + let limit = gho_reserve::get_limit(reserve_address, gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); // Add gho as a reserve to the pool @@ -311,17 +312,17 @@ module aave_pool::gho_direct_minter_tests { pool_fee_manager::init_module_for_testing(aave_pool); // Simulate minting gho over the bridge to the gho reserve - let reserve_address = gho_reserve::get_reserve_address(); + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); let mint_amount = 10_000_000; // 10 gho tokens gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); // Add gho_reserve_entity as an entity with a limit let entity_limit = 0; // zero limit - gho_reserve::add_entity(gho_admin, gho_reserve_entity); - gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + gho_reserve::add_entity(gho_admin, reserve_address, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, reserve_address, gho_reserve_entity, entity_limit); // Check the entity level - let limit = gho_reserve::get_limit(gho_reserve_entity); + let limit = gho_reserve::get_limit(reserve_address, gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); // Add gho as a reserve to the pool @@ -394,17 +395,17 @@ module aave_pool::gho_direct_minter_tests { pool_fee_manager::init_module_for_testing(aave_pool); // Simulate minting gho over the bridge to the gho reserve - let reserve_address = gho_reserve::get_reserve_address(); + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); let mint_amount = 10_000_000; // 10 gho tokens gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); // Add gho_reserve_entity as an entity with a limit let entity_limit = 8_000_000; // 8 gho tokens limit - gho_reserve::add_entity(gho_admin, gho_reserve_entity); - gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + gho_reserve::add_entity(gho_admin, reserve_address, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, reserve_address, gho_reserve_entity, entity_limit); // Check the entity level - let limit = gho_reserve::get_limit(gho_reserve_entity); + let limit = gho_reserve::get_limit(reserve_address, gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); // Add gho as a reserve to the pool @@ -531,7 +532,7 @@ module aave_pool::gho_direct_minter_tests { ); // Simulate minting gho over the bridge to the gho reserve - let reserve_address = gho_reserve::get_reserve_address(); + let reserve_address = gho_reserve::get_gho_reserve_address(GHO_RESERVE_SEED); let mint_amount = 10_000_000; // 10 gho tokens gho_token::test_mint_directly_to_user(gho_admin, reserve_address, mint_amount); @@ -542,11 +543,11 @@ module aave_pool::gho_direct_minter_tests { // Add gho_reserve_entity as an entity with a limit let entity_limit = 8_000_000; // 8 gho tokens limit - gho_reserve::add_entity(gho_admin, gho_reserve_entity); - gho_reserve::set_limit(gho_admin, gho_reserve_entity, entity_limit); + gho_reserve::add_entity(gho_admin, reserve_address, gho_reserve_entity); + gho_reserve::set_limit(gho_admin, reserve_address, gho_reserve_entity, entity_limit); // Check the entity level - let limit = gho_reserve::get_limit(gho_reserve_entity); + let limit = gho_reserve::get_limit(reserve_address, gho_reserve_entity); assert!(limit == entity_limit, TEST_SUCCESS); // Add gho as a reserve to the pool