Skip to content

Commit eb0af96

Browse files
authored
feat: update demo proposal (#3)
1 parent 4b9e7a2 commit eb0af96

File tree

3 files changed

+118
-60
lines changed

3 files changed

+118
-60
lines changed

script/Deploy.s.sol

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import {EthereumScript} from "solidity-utils/contracts/utils/ScriptUtils.sol";
5+
import {IPoolAddressesProvider} from "aave-v3-origin/contracts/interfaces/IPoolAddressesProvider.sol";
6+
import {ICollector} from "aave-v3-origin/contracts/treasury/ICollector.sol";
7+
import {
8+
ITransparentProxyFactory,
9+
ProxyAdmin
10+
} from "solidity-utils/contracts/transparent-proxy/interfaces/ITransparentProxyFactory.sol";
11+
import {GhoDirectMinter} from "../src/GhoDirectMinter.sol";
12+
import {IGhoToken} from "../src/interfaces/IGhoToken.sol";
13+
14+
import {AaveV3EthereumAssets} from "aave-address-book/AaveV3Ethereum.sol";
15+
import {AaveV3EthereumLido} from "aave-address-book/AaveV3EthereumLido.sol";
16+
import {GovernanceV3Ethereum} from "aave-address-book/GovernanceV3Ethereum.sol";
17+
import {MiscEthereum} from "aave-address-book/MiscEthereum.sol";
18+
19+
library DeploymentLibrary {
20+
function _deployFacilitator(
21+
ITransparentProxyFactory proxyFactory,
22+
ProxyAdmin proxyAdmin,
23+
IPoolAddressesProvider poolAddressesprovider,
24+
address collector,
25+
IGhoToken gho,
26+
address council
27+
) internal returns (address) {
28+
address vaultImpl = address(new GhoDirectMinter(poolAddressesprovider, address(collector), address(gho)));
29+
return proxyFactory.create(
30+
vaultImpl,
31+
proxyAdmin,
32+
abi.encodeWithSelector(GhoDirectMinter.initialize.selector, address(GovernanceV3Ethereum.EXECUTOR_LVL_1), council)
33+
);
34+
}
35+
36+
function _deployLido() internal returns (address) {
37+
// its the council used on other GHO stewards
38+
// might make sense to have on address book
39+
address council = 0x8513e6F37dBc52De87b166980Fa3F50639694B60;
40+
return _deployFacilitator(
41+
ITransparentProxyFactory(MiscEthereum.TRANSPARENT_PROXY_FACTORY),
42+
ProxyAdmin(MiscEthereum.PROXY_ADMIN),
43+
AaveV3EthereumLido.POOL_ADDRESSES_PROVIDER,
44+
address(AaveV3EthereumLido.COLLECTOR),
45+
IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING),
46+
council
47+
);
48+
}
49+
}
50+
51+
contract DeployLido is EthereumScript {
52+
function run() external broadcast {
53+
DeploymentLibrary._deployLido();
54+
}
55+
}

src/proposals/LidoGHOListing.sol

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,64 +29,22 @@ contract LidoGHOListing is AaveV3PayloadEthereumLido {
2929
using SafeERC20 for IERC20;
3030

3131
uint128 public constant GHO_MINT_AMOUNT = 10_000_000e18;
32-
address public immutable COUNCIL;
32+
address public immutable FACILITATOR;
3333

34-
constructor(address council) {
35-
COUNCIL = council;
34+
constructor(address facilitator) {
35+
FACILITATOR = facilitator;
3636
}
3737

3838
function _postExecute() internal override {
39-
address vaultImpl = address(
40-
new GhoDirectMinter(
41-
AaveV3EthereumLido.POOL_ADDRESSES_PROVIDER,
42-
address(AaveV3EthereumLido.COLLECTOR),
43-
AaveV3EthereumAssets.GHO_UNDERLYING
44-
)
45-
);
46-
address vault = ITransparentProxyFactory(MiscEthereum.TRANSPARENT_PROXY_FACTORY).create(
47-
vaultImpl,
48-
ProxyAdmin(MiscEthereum.PROXY_ADMIN),
49-
abi.encodeWithSelector(GhoDirectMinter.initialize.selector, address(this), COUNCIL)
50-
);
5139
IAccessControl(address(AaveV3EthereumLido.ACL_MANAGER)).grantRole(
52-
AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(), address(vault)
40+
AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(), address(FACILITATOR)
5341
);
54-
IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).addFacilitator(vault, "LidoGhoDirectMinter", GHO_MINT_AMOUNT);
55-
GhoDirectMinter(vault).mintAndSupply(GHO_MINT_AMOUNT);
42+
IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).addFacilitator(FACILITATOR, "LidoGhoDirectMinter", GHO_MINT_AMOUNT);
43+
GhoDirectMinter(FACILITATOR).mintAndSupply(GHO_MINT_AMOUNT);
5644

5745
// allow risk council to control the bucket capacity
5846
address[] memory vaults = new address[](1);
59-
vaults[0] = vault;
47+
vaults[0] = FACILITATOR;
6048
IGhoBucketSteward(0x46Aa1063e5265b43663E81329333B47c517A5409).setControlledFacilitator(vaults, true);
6149
}
62-
63-
function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) {
64-
IAaveV3ConfigEngine.Listing[] memory listings = new IAaveV3ConfigEngine.Listing[](1);
65-
66-
listings[0] = IAaveV3ConfigEngine.Listing({
67-
asset: AaveV3EthereumAssets.GHO_UNDERLYING,
68-
assetSymbol: "GHO",
69-
priceFeed: AaveV3EthereumAssets.GHO_ORACLE,
70-
enabledToBorrow: EngineFlags.ENABLED,
71-
borrowableInIsolation: EngineFlags.DISABLED,
72-
withSiloedBorrowing: EngineFlags.DISABLED,
73-
flashloanable: EngineFlags.ENABLED,
74-
ltv: 0,
75-
liqThreshold: 0,
76-
liqBonus: 0,
77-
reserveFactor: 10_00,
78-
supplyCap: 20_000_000,
79-
borrowCap: 2_500_000,
80-
debtCeiling: 0,
81-
liqProtocolFee: 20_00,
82-
rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({
83-
optimalUsageRatio: 92_00,
84-
baseVariableBorrowRate: 4_50,
85-
variableRateSlope1: 3_00,
86-
variableRateSlope2: 50_00
87-
})
88-
});
89-
90-
return listings;
91-
}
9250
}

test/Lido_GhoDirectMinter.t.sol

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,44 @@ import {ReserveConfiguration} from "aave-v3-origin/contracts/protocol/libraries/
1919
import {GhoDirectMinter} from "../src/GhoDirectMinter.sol";
2020
import {LidoGHOListing} from "../src/proposals/LidoGHOListing.sol";
2121
import {IGhoToken} from "../src/interfaces/IGhoToken.sol";
22+
import {DeploymentLibrary} from "../script/Deploy.s.sol";
2223

2324
contract Lido_GHODirectMinter_Test is Test {
2425
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
2526

27+
// its the council used on other GHO stewards
28+
// might make sense to have on address book
29+
address council = 0x8513e6F37dBc52De87b166980Fa3F50639694B60;
30+
2631
GhoDirectMinter internal minter;
2732
IERC20 internal ghoAToken;
2833
LidoGHOListing internal proposal;
2934

30-
address council = makeAddr("council");
3135
address owner = GovernanceV3Ethereum.EXECUTOR_LVL_1;
3236

3337
function setUp() external {
34-
vm.createSelectFork(vm.rpcUrl("mainnet"), 21265036);
38+
vm.createSelectFork(vm.rpcUrl("mainnet"), 21378878);
39+
40+
// execute pending gho listing payload
41+
GovV3Helpers.executePayload(vm, 218);
3542

3643
// execute payload
37-
proposal = new LidoGHOListing(council);
44+
address facilitator = DeploymentLibrary._deployLido();
45+
proposal = new LidoGHOListing(facilitator);
3846
GovV3Helpers.executePayload(vm, address(proposal));
3947

4048
address[] memory facilitators = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorsList();
4149
minter = GhoDirectMinter(facilitators[facilitators.length - 1]);
50+
assertEq(address(minter), facilitator);
4251
ghoAToken = IERC20(minter.GHO_A_TOKEN());
4352

4453
// burn all supply to start with a clean state on the tests
54+
uint256 totalATokenSupply = ghoAToken.totalSupply();
4555
uint128 mintAmount = proposal.GHO_MINT_AMOUNT();
4656
vm.prank(owner);
4757
minter.withdrawAndBurn(mintAmount);
4858
assertEq(ghoAToken.balanceOf(address(minter)), 0);
49-
assertEq(ghoAToken.totalSupply(), 0);
59+
assertEq(ghoAToken.totalSupply(), totalATokenSupply - mintAmount);
5060
}
5161

5262
function test_mintAndSupply_owner(uint256 amount) public returns (uint256) {
@@ -90,33 +100,68 @@ contract Lido_GHODirectMinter_Test is Test {
90100
// generate some yield
91101
vm.warp(block.timestamp + 1000);
92102

103+
uint256 collectorBalanceBeforeTransfer = ghoAToken.balanceOf(address(minter.COLLECTOR()));
93104
uint256 balanceBeforeTransfer = ghoAToken.balanceOf(address(minter));
94105
assertGt(balanceBeforeTransfer, amount);
95106
minter.transferExcessToTreasury();
96107
assertApproxEqAbs(ghoAToken.balanceOf(address(minter)), amount, 1);
97-
assertApproxEqAbs(ghoAToken.balanceOf(address(minter.COLLECTOR())), balanceBeforeTransfer - amount, 1);
108+
assertApproxEqAbs(
109+
ghoAToken.balanceOf(address(minter.COLLECTOR())) - collectorBalanceBeforeTransfer,
110+
balanceBeforeTransfer - amount,
111+
1
112+
);
98113
}
99114

115+
/// @dev supplies a bounded value of [amount, 1, type(uint256).max] to the pool
100116
function _mintAndSupply(uint256 amount, address caller) internal returns (uint256) {
117+
// setup
101118
amount = bound(amount, 1, proposal.GHO_MINT_AMOUNT());
102119
DataTypes.ReserveConfigurationMap memory configurationBefore =
103120
AaveV3EthereumLido.POOL.getConfiguration(AaveV3EthereumAssets.GHO_UNDERLYING);
121+
uint256 totalATokenSupplyBefore = ghoAToken.totalSupply();
122+
uint256 minterATokenSupplyBefore = IERC20(ghoAToken).balanceOf(address(minter));
123+
(, uint256 levelBefore) =
124+
IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorBucket(proposal.FACILITATOR());
125+
126+
// mint
104127
vm.prank(caller);
105128
minter.mintAndSupply(amount);
129+
130+
// check
106131
DataTypes.ReserveConfigurationMap memory configurationAfter =
107132
AaveV3EthereumLido.POOL.getConfiguration(AaveV3EthereumAssets.GHO_UNDERLYING);
108-
assertEq(IERC20(ghoAToken).balanceOf(address(minter)), amount);
109-
assertEq(ghoAToken.totalSupply(), amount);
133+
(, uint256 levelAfter) = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorBucket(proposal.FACILITATOR());
134+
// after supplying the minters aToken balance should increase by the supplied amount
135+
assertEq(IERC20(ghoAToken).balanceOf(address(minter)), minterATokenSupplyBefore + amount);
136+
// the aToken total supply should be adjusted by the same amount
137+
assertEq(ghoAToken.totalSupply(), totalATokenSupplyBefore + amount);
138+
// the cap should not be touched
110139
assertEq(configurationBefore.getSupplyCap(), configurationAfter.getSupplyCap());
140+
// level should be increased by the minted amount
141+
assertEq(levelAfter, levelBefore + amount);
111142
return amount;
112143
}
113144

145+
// burns a bounded value of [withdrawAmount, 1, boundedSupplyAmount] from the pool
114146
function _withdrawAndBurn(uint256 supplyAmount, uint256 withdrawAmount, address caller) internal {
115-
uint256 amount = test_mintAndSupply_owner(supplyAmount);
147+
// setup
148+
uint256 amount = _mintAndSupply(supplyAmount, owner);
116149
withdrawAmount = bound(withdrawAmount, 1, amount);
150+
uint256 totalATokenSupplyBefore = ghoAToken.totalSupply();
151+
(, uint256 levelBefore) =
152+
IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorBucket(proposal.FACILITATOR());
153+
154+
// burn
117155
vm.prank(caller);
118-
minter.withdrawAndBurn(amount);
119-
assertEq(IERC20(ghoAToken).balanceOf(address(minter)), 0);
120-
assertEq(ghoAToken.totalSupply(), 0);
156+
minter.withdrawAndBurn(withdrawAmount);
157+
158+
// check
159+
(, uint256 levelAfter) = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorBucket(proposal.FACILITATOR());
160+
// aToken total supply should be decreased by the burned amount
161+
assertEq(ghoAToken.totalSupply(), totalATokenSupplyBefore - withdrawAmount);
162+
// the minter supply should shrink by the same amount
163+
assertEq(IERC20(ghoAToken).balanceOf(address(minter)), amount - withdrawAmount);
164+
// the minter level should shrink by the same amount
165+
assertEq(levelAfter, levelBefore - withdrawAmount);
121166
}
122167
}

0 commit comments

Comments
 (0)