Skip to content

Commit

Permalink
feat: add test
Browse files Browse the repository at this point in the history
  • Loading branch information
sakulstra committed Nov 26, 2024
1 parent 8c02f68 commit f786a7b
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ contract AaveV3EthereumLido_GHOListingOnLidoPool_20241119 is AaveV3PayloadEthere
using SafeERC20 for IERC20;

// could be significantly more
uint128 public constant GHO_MINT_AMOUNT = 10_000_000e18;
uint128 public constant GHO_MINT_AMOUNT = 100_000_000e18;
uint256 public constant GHO_BORROW_CAP = 10_000_000e18;
address public immutable VAULT;

constructor(address vault) {
Expand Down Expand Up @@ -54,9 +55,9 @@ contract AaveV3EthereumLido_GHOListingOnLidoPool_20241119 is AaveV3PayloadEthere
liqThreshold: 0,
liqBonus: 0,
// TODO: consult risk teams
reserveFactor: 100_00,
supplyCap: 0,
borrowCap: 10_000_000,
reserveFactor: 20_00,
supplyCap: GHO_MINT_AMOUNT / 1e18,
borrowCap: GHO_BORROW_CAP / 1e18,
debtCeiling: 0,
liqProtocolFee: 20_00,
rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ contract GHODirectMinter is Initializable, OwnableUpgradeable {
IPool public immutable POOL;
address public immutable COLLECTOR;
IGhoToken public immutable GHO;
address public immutable GHO_A_TOKEN;

struct GHODirectMinterStorage {
uint256 amountMinted;
}

// keccak256(abi.encode(uint256(keccak256("aave.storage.GHODirectMinterStorageLocation")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant GHODirectMinterStorageLocation =
0x6bf88133411bc3f7568ae2901946a9d52d421a01a138a2f0b6a52a1b7c899d00; // TODO: update this

function _getGHODirectMinterStorage() private pure returns (GHODirectMinterStorage storage $) {
assembly {
$.slot := GHODirectMinterStorageLocation
}
}

constructor(IPool pool, address collector, address gho) {
POOL = pool;
COLLECTOR = collector;
GHO = IGhoToken(gho);
DataTypes.ReserveDataLegacy memory reserveData = POOL.getReserveData(address(GHO));
GHO_A_TOKEN = reserveData.aTokenAddress;
_disableInitializers();
}

Expand All @@ -39,6 +50,8 @@ contract GHODirectMinter is Initializable, OwnableUpgradeable {
* @param amount Amount of GHO to mint and supply to the pool
*/
function mintAndSupply(uint256 amount) external onlyOwner {
_getGHODirectMinterStorage().amountMinted += amount;

GHO.mint(address(this), amount);
IERC20(address(GHO)).forceApprove(address(POOL), amount);
POOL.supply(address(GHO), amount, address(this), 0);
Expand All @@ -49,7 +62,9 @@ contract GHODirectMinter is Initializable, OwnableUpgradeable {
* @param amount Amount of GHO to withdraw and burn from the pool
*/
function withdrawAndBurn(uint256 amount) external onlyOwner {
// violating CEI because there might be rounding on the withdrawal, but we want exact accounting on amountMinted
uint256 amountWithdrawn = POOL.withdraw(address(GHO), amount, address(this));
_getGHODirectMinterStorage().amountMinted -= amount;

GHO.burn(amountWithdrawn);
}
Expand All @@ -58,8 +73,9 @@ contract GHODirectMinter is Initializable, OwnableUpgradeable {
* @dev Transfers excess GHO to the treasury
*/
function transferExcessToTreasury() external {
(uint256 capacity, ) = GHO.getFacilitatorBucket(address(this));
uint256 balanceIncrease = IERC20(GHO_A_TOKEN).balanceOf(address(this)) - capacity;
IERC20(GHO_A_TOKEN).transfer(address(COLLECTOR), balanceIncrease);
DataTypes.ReserveDataLegacy memory reserveData = POOL.getReserveData(address(GHO));
uint256 balanceIncrease = IERC20(reserveData.aTokenAddress).balanceOf(address(this)) -
_getGHODirectMinterStorage().amountMinted;
IERC20(reserveData.aTokenAddress).transfer(address(COLLECTOR), balanceIncrease);
}
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,100 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import 'forge-std/Test.sol';
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {AaveV3EthereumLido} from 'aave-address-book/AaveV3EthereumLido.sol';
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import {IGhoToken} from '../interfaces/IGhoToken.sol';
import {ITransparentProxyFactory, ProxyAdmin} from 'solidity-utils/contracts/transparent-proxy/interfaces/ITransparentProxyFactory.sol';

import 'forge-std/Test.sol';
import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol';
import {IPool, DataTypes} from 'aave-v3-origin/contracts/interfaces/IPool.sol';
import {GHODirectMinter} from './GHODirectMinter.sol';
import {AaveV3EthereumLido_GHOListingOnLidoPool_20241119} from './AaveV3EthereumLido_GHOListingOnLidoPool_20241119.sol';
import {GHODirectMinterDeploymentLib} from './GHOListingOnLidoPool_20241119.s.sol';
import {IGhoToken} from '../interfaces/IGhoToken.sol';

contract GHODirectMinter_Test is Test {
GHODirectMinter internal minter;
uint128 public constant GHO_MINT_AMOUNT = 10_000_000e18;
IERC20 internal ghoAToken;
AaveV3EthereumLido_GHOListingOnLidoPool_20241119 internal proposal;

function setUp() external {
vm.createSelectFork(vm.rpcUrl('mainnet'), 21265036);

GHODirectMinter minterImpl = new GHODirectMinter(
AaveV3EthereumLido.POOL,
address(AaveV3EthereumLido.COLLECTOR),
AaveV3EthereumAssets.GHO_UNDERLYING
);
// execute payload
minter = GHODirectMinter(GHODirectMinterDeploymentLib._deploy());
proposal = new AaveV3EthereumLido_GHOListingOnLidoPool_20241119(address(minter));
GovV3Helpers.executePayload(vm, address(proposal));

minter = GHODirectMinter(
ITransparentProxyFactory(MiscEthereum.TRANSPARENT_PROXY_FACTORY).create(
address(minterImpl),
ProxyAdmin(MiscEthereum.PROXY_ADMIN),
abi.encodeWithSelector(
GHODirectMinter.initialize.selector,
GovernanceV3Ethereum.EXECUTOR_LVL_1
)
)
DataTypes.ReserveDataLegacy memory reserveData = AaveV3EthereumLido.POOL.getReserveData(
address(AaveV3EthereumAssets.GHO_UNDERLYING)
);
ghoAToken = IERC20(reserveData.aTokenAddress);

uint128 mintAmount = proposal.GHO_MINT_AMOUNT();
vm.prank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
minter.withdrawAndBurn(mintAmount);
assertEq(ghoAToken.balanceOf(address(minter)), 0);
assertEq(ghoAToken.totalSupply(), 0);
}

vm.startPrank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).addFacilitator(
address(minter),
'LidoGHODirectMinter',
GHO_MINT_AMOUNT
function test_storageLocation() external pure {
assertEq(
keccak256(abi.encode(uint256(keccak256('aave.storage.GHODirectMinterStorageLocation')) - 1)) &
~bytes32(uint256(0xff)),
0x6bf88133411bc3f7568ae2901946a9d52d421a01a138a2f0b6a52a1b7c899d00
);
vm.stopPrank();
}

function test_mintAndSupply() external {}
function test_mintAndSupply(uint256 amount) public returns (uint256) {
amount = bound(amount, 1, proposal.GHO_MINT_AMOUNT());
vm.prank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
minter.mintAndSupply(amount);
assertEq(IERC20(ghoAToken).balanceOf(address(minter)), amount);
assertEq(ghoAToken.totalSupply(), amount);
return amount;
}

function test_withdrawAndBurn(uint256 supplyAmount, uint256 withdrawAmount) external {
uint256 amount = test_mintAndSupply(supplyAmount);
withdrawAmount = bound(withdrawAmount, 1, amount);
vm.prank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
minter.withdrawAndBurn(amount);
assertEq(IERC20(ghoAToken).balanceOf(address(minter)), 0);
assertEq(ghoAToken.totalSupply(), 0);
}

function test_transferExcessToTreasury() external {
uint256 amount = test_mintAndSupply(1000 ether);
// supply sth and borrow gho
deal(AaveV3EthereumAssets.wstETH_UNDERLYING, address(this), 1_000 ether);
IERC20(AaveV3EthereumAssets.wstETH_UNDERLYING).approve(
address(AaveV3EthereumLido.POOL),
1_000 ether
);
AaveV3EthereumLido.POOL.deposit(
AaveV3EthereumAssets.wstETH_UNDERLYING,
1_000 ether,
address(this),
0
);
AaveV3EthereumLido.POOL.borrow(
AaveV3EthereumAssets.GHO_UNDERLYING,
amount,
2,
0,
address(this)
);

// generate some yield
vm.warp(block.timestamp + 1000);

uint256 balanceBeforeTransfer = ghoAToken.balanceOf(address(minter));
assertGt(balanceBeforeTransfer, amount);
minter.transferExcessToTreasury();
assertEq(ghoAToken.balanceOf(address(minter)), amount);
assertEq(ghoAToken.balanceOf(address(minter.COLLECTOR())), balanceBeforeTransfer - amount);
}
}

0 comments on commit f786a7b

Please sign in to comment.