Skip to content

feat: v4 direct minter#3

Open
DhairyaSethi wants to merge 3 commits intomainfrom
feat/v4-minter
Open

feat: v4 direct minter#3
DhairyaSethi wants to merge 3 commits intomainfrom
feat/v4-minter

Conversation

@DhairyaSethi
Copy link
Member

@DhairyaSethi DhairyaSethi commented Feb 27, 2026

Add GhoDirectMinterV4, a GHO facilitator that injects (mints) and removes (burns) GHO from an Aave v4 Hub. The minter is expected to be registered as a spoke on the Hub with infinite addCap. mintAndSupply mints GHO and adds it as liquidity to the Hub, withdrawAndBurn removes GHO liquidity and burns it, and transferExcessToTreasury transfers excess interest (added shares above the facilitator bucket level) to the Hub's fee receiver.

@github-actions
Copy link

github-actions bot commented Feb 27, 2026

Forge Build Sizes

Contract Runtime Size (B) Initcode Size (B) Runtime Margin (B) Initcode Margin (B)
GhoDirectMinterV4 2,994 3,596 21,582 45,556
GhoEthereum 44 94 24,532 49,058
🔕 Unchanged
Contract Runtime Size (B) Initcode Size (B) Runtime Margin (B) Initcode Margin (B)
AaveV3Ethereum 44 94 24,532 49,058
AaveV3EthereumAssets 44 94 24,532 49,058
AaveV3EthereumEModes 44 94 24,532 49,058
AaveV3EthereumExternalLibraries 44 94 24,532 49,058
AaveV3EthereumLido 44 94 24,532 49,058
AaveV3EthereumLidoAssets 44 94 24,532 49,058
AaveV3EthereumLidoEModes 44 94 24,532 49,058
AaveV3EthereumLidoExternalLibraries 44 94 24,532 49,058
AaveV3InkWhitelabel 44 94 24,532 49,058
AaveV3InkWhitelabelAssets 44 94 24,532 49,058
AaveV3InkWhitelabelEModes 44 94 24,532 49,058
AaveV3InkWhitelabelExternalLibraries 44 94 24,532 49,058
Address 44 94 24,532 49,058
ChainHelpers 44 94 24,532 49,058
ChainIds 44 94 24,532 49,058
ConfiguratorInputTypes 44 94 24,532 49,058
Create2Utils 121 171 24,455 48,981
Create2UtilsZkSync 104 154 24,472 48,998
DataTypes 44 94 24,532 49,058
DeploymentLibrary 44 94 24,532 49,058
Errors (lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/Errors.sol) 44 94 24,532 49,058
Errors (lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/contracts/protocol/libraries/helpers/Errors.sol) 44 94 24,532 49,058
Errors (lib/aave-helpers/lib/aave-address-book/src/governance-v3/Errors.sol) 4,652 4,704 19,924 44,448
GhoDirectMinter 3,926 5,397 20,650 43,755
GovV3Helpers 2,469 2,521 22,107 46,631
GovV3StorageHelpers 44 94 24,532 49,058
GovernanceV3Arbitrum 44 94 24,532 49,058
GovernanceV3Avalanche 44 94 24,532 49,058
GovernanceV3BNB 44 94 24,532 49,058
GovernanceV3Base 44 94 24,532 49,058
GovernanceV3Bob 44 94 24,532 49,058
GovernanceV3Celo 44 94 24,532 49,058
GovernanceV3Ethereum 44 94 24,532 49,058
GovernanceV3Gnosis 44 94 24,532 49,058
GovernanceV3Ink 44 94 24,532 49,058
GovernanceV3InkWhitelabel 44 94 24,532 49,058
GovernanceV3Linea 44 94 24,532 49,058
GovernanceV3Mantle 44 94 24,532 49,058
GovernanceV3MegaEth 44 94 24,532 49,058
GovernanceV3Metis 44 94 24,532 49,058
GovernanceV3Optimism 44 94 24,532 49,058
GovernanceV3Plasma 44 94 24,532 49,058
GovernanceV3Polygon 44 94 24,532 49,058
GovernanceV3PolygonZkEvm 44 94 24,532 49,058
GovernanceV3Scroll 44 94 24,532 49,058
GovernanceV3Soneium 44 94 24,532 49,058
GovernanceV3Sonic 44 94 24,532 49,058
GovernanceV3ZkSync 44 94 24,532 49,058
IpfsUtils 44 94 24,532 49,058
MiscEthereum 44 94 24,532 49,058
PayloadsControllerUtils 44 94 24,532 49,058
ProxyHelpers 44 94 24,532 49,058
ReserveConfiguration 128 178 24,448 48,974
SafeERC20 44 94 24,532 49,058
StorageHelpers 44 94 24,532 49,058
TestNetChainIds 44 94 24,532 49,058

@github-actions
Copy link

github-actions bot commented Feb 27, 2026

♻️ Forge Gas Snapshots

Seems like you are not measuring gas of any operations yet. 🤔
Consider adding some snapshot tests to measure regressions & improvements.

@github-actions
Copy link

github-actions bot commented Feb 27, 2026

🌈 Test Results
No files changed, compilation skipped

Ran 7 tests for test/GhoDirectMinter.t.sol:GHODirectMinter_Test
[PASS] test_mintAndSupply_council(uint256) (runs: 256, μ: 245222, ~: 244950)
[PASS] test_mintAndSupply_owner(uint256) (runs: 256, μ: 242921, ~: 242649)
[PASS] test_mintAndSupply_rando() (gas: 18366)
[PASS] test_transferExcessToTreasury() (gas: 919147)
[PASS] test_withdrawAndBurn_council(uint256,uint256) (runs: 256, μ: 309765, ~: 311012)
[PASS] test_withdrawAndBurn_owner(uint256,uint256) (runs: 256, μ: 305510, ~: 306757)
[PASS] test_withdrawAndBurn_rando() (gas: 18266)
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 1.08s (1.95s CPU time)

Ran 13 tests for test/GhoDirectMinterV4.t.sol:GHODirectMinterV4_Test
[PASS] test_mintAndSupply_council(uint256) (runs: 256, μ: 124534, ~: 124248)
[PASS] test_mintAndSupply_exceedsBucketCapacity() (gas: 98089)
[PASS] test_mintAndSupply_owner(uint256) (runs: 256, μ: 122326, ~: 122040)
[PASS] test_mintAndSupply_revertsWith_InvalidCaller() (gas: 18255)
[PASS] test_mintAndSupply_zeroAmount() (gas: 21755)
[PASS] test_setup() (gas: 80064)
[PASS] test_transferExcessToTreasury() (gas: 355290)
[PASS] test_transferExcessToTreasury_noExcess(uint256) (runs: 256, μ: 119511, ~: 119225)
[PASS] test_withdrawAndBurn_council(uint256,uint256) (runs: 256, μ: 166955, ~: 167387)
[PASS] test_withdrawAndBurn_exceedsSpokeBalance() (gas: 110547)
[PASS] test_withdrawAndBurn_owner(uint256,uint256) (runs: 256, μ: 162748, ~: 163180)
[PASS] test_withdrawAndBurn_rando() (gas: 18322)
[PASS] test_withdrawAndBurn_zeroBalance() (gas: 52408)
Suite result: ok. 13 passed; 0 failed; 0 skipped; finished in 1.08s (1.13s CPU time)

Ran 2 test suites in 1.08s (2.16s CPU time): 20 tests passed, 0 failed, 0 skipped (20 total tests)

@github-actions
Copy link

github-actions bot commented Feb 27, 2026

🔮 Coverage report
File Line Coverage Function Coverage Branch Coverage
src/GhoDirectMinter.sol ${\color{red}0\%}$
$0 / 26$
37, 38, 39, 40, 41 and 21 more
${\color{red}0\%}$
$0 / 5$
GhoDirectMinter.constructor, GhoDirectMinter.initialize, GhoDirectMinter.mintAndSupply, GhoDirectMinter.withdrawAndBurn, GhoDirectMinter.transferExcessToTreasury
${\color{red}0\%}$
$0 / 2$
src/GhoDirectMinterV4.sol ${\color{green}100\%}$
$26 / 26$
${\color{green}100\%}$
$8 / 8$
${\color{green}100\%}$
$1 / 1$

/// @notice Transfers excess GHO interest (added shares above facilitator bucket level) to the fee receiver.
/// @dev Callable by anyone.
/// @dev Due to rounding in the share conversion, the amount transferred may be slightly less than the true excess.
/// This is neglectable and should not have any impact on the system.

Choose a reason for hiding this comment

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

Suggested change
/// This is neglectable and should not have any impact on the system.
/// @dev This is negligible and should not have any impact on the system.

Dev tag and word change, but perhaps fine as is

Comment on lines +4 to +9
import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {
UpgradeableOwnableWithGuardian
} from "solidity-utils/contracts/access-control/UpgradeableOwnableWithGuardian.sol";

Choose a reason for hiding this comment

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

Suggested change
import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {
UpgradeableOwnableWithGuardian
} from "solidity-utils/contracts/access-control/UpgradeableOwnableWithGuardian.sol";
import {
UpgradeableOwnableWithGuardian
} from "solidity-utils/contracts/access-control/UpgradeableOwnableWithGuardian.sol";
import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

HUB = IHub(hub_);
ASSET_ID = HUB.getAssetId(gho_); // reverts on invalid `underlying`
GHO = IGhoToken(gho_);
_disableInitializers();
Copy link

@CheyenneAtapour CheyenneAtapour Mar 3, 2026

Choose a reason for hiding this comment

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

@miguelmtzinf had wanted this as first line of constructor on another PR of mine. I'm personally fine like this, but just checking if that's the convention we want

/// @dev Constructor.
/// @param hub_ The address of the Aave v4 Hub.
/// @param gho_ The address of the GHO token.
constructor(address hub_, address gho_) {

Choose a reason for hiding this comment

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

What do we think about having this more flexible so it can mint GHO to multiple hubs if needed?

Is that unnecessary?

_withdrawAndBurn(supplyAmount, withdrawAmount, council);
}

function test_withdrawAndBurn_rando() external {

Choose a reason for hiding this comment

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

Suggested change
function test_withdrawAndBurn_rando() external {
function test_withdrawAndBurn_revertsWith_InvalidCaller() external {

uint256 excess = spokeAddedAssets - level;
uint256 expectedShares = hub.previewAddByAssets(ghoAssetId, excess);

minter.transferExcessToTreasury();
Copy link

@CheyenneAtapour CheyenneAtapour Mar 3, 2026

Choose a reason for hiding this comment

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

Do you want to ensure we are actually transferring at least 1 share here? This works even if it's a nop (ofc as you know since you have a test showcasing)

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.

2 participants