forked from bgd-labs/aave-proposals-v3
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add AIP for V4 AL Service Provider Proposal (bgd-labs#357)
- Loading branch information
1 parent
ee5f182
commit e8167f7
Showing
6 changed files
with
543 additions
and
0 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
...al_20240614_before_AaveV3Ethereum_V4ALServiceProviderProposal_20240614_after.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
## Raw diff | ||
|
||
```json | ||
{} | ||
``` |
41 changes: 41 additions & 0 deletions
41
...ereum_V4ALServiceProviderProposal/AaveV3Ethereum_V4ALServiceProviderProposal_20240614.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {IProposalGenericExecutor} from 'aave-helpers/interfaces/IProposalGenericExecutor.sol'; | ||
import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; | ||
|
||
/** | ||
* @title V4 AL Service Provider Proposal | ||
* @author Aave Labs | ||
* - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x70dfd865b78c4c391e2b0729b907d152e6e8a0da683416d617d8f84782036349 | ||
* - Discussion: https://governance.aave.com/t/arfc-al-service-provider-proposal/17974 | ||
*/ | ||
contract AaveV3Ethereum_V4ALServiceProviderProposal_20240614 is IProposalGenericExecutor { | ||
address public constant AAVE_LABS = 0x1c037b3C22240048807cC9d7111be5d455F640bd; | ||
|
||
// 3 million GHO upfront | ||
uint256 public constant GHO_UPFRONT_AMOUNT = 3_000_000 ether; | ||
|
||
// 9 million GHO streamed over the year | ||
uint256 public constant GHO_STREAM_AMOUNT = 9_000_000 ether; | ||
uint256 public constant GHO_STREAM_DURATION = 365 days; | ||
|
||
uint256 public constant ACTUAL_STREAM = | ||
(GHO_STREAM_AMOUNT / GHO_STREAM_DURATION) * GHO_STREAM_DURATION; | ||
|
||
function execute() external { | ||
AaveV3Ethereum.COLLECTOR.createStream( | ||
AAVE_LABS, | ||
ACTUAL_STREAM, | ||
AaveV3EthereumAssets.GHO_UNDERLYING, | ||
block.timestamp, | ||
block.timestamp + GHO_STREAM_DURATION | ||
); | ||
|
||
AaveV3Ethereum.COLLECTOR.transfer( | ||
AaveV3EthereumAssets.GHO_UNDERLYING, | ||
AAVE_LABS, | ||
GHO_UPFRONT_AMOUNT | ||
); | ||
} | ||
} |
311 changes: 311 additions & 0 deletions
311
...eum_V4ALServiceProviderProposal/AaveV3Ethereum_V4ALServiceProviderProposal_20240614.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,311 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; | ||
|
||
import 'forge-std/Test.sol'; | ||
import 'forge-std/StdUtils.sol'; | ||
import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/ProtocolV3TestBase.sol'; | ||
import {AaveV3Ethereum_V4ALServiceProviderProposal_20240614} from './AaveV3Ethereum_V4ALServiceProviderProposal_20240614.sol'; | ||
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; | ||
|
||
/** | ||
* @dev Test for AaveV3Ethereum_V4ALServiceProviderProposal_20240614 | ||
* command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20240614_AaveV3Ethereum_V4ALServiceProviderProposal/AaveV3Ethereum_V4ALServiceProviderProposal_20240614.t.sol -vv | ||
*/ | ||
contract AaveV3Ethereum_V4ALServiceProviderProposal_20240614_Test is ProtocolV3TestBase { | ||
AaveV3Ethereum_V4ALServiceProviderProposal_20240614 internal proposal; | ||
|
||
address public constant AAVE_LABS = 0x1c037b3C22240048807cC9d7111be5d455F640bd; | ||
uint256 public constant GHO_UPFRONT_AMOUNT = 3_000_000 ether; | ||
uint256 public constant GHO_STREAM_AMOUNT = 9_000_000 ether; | ||
uint256 public constant GHO_STREAM_DURATION = 365 days; | ||
uint256 public constant ACTUAL_STREAM_AMOUNT_GHO = | ||
(GHO_STREAM_AMOUNT / GHO_STREAM_DURATION) * GHO_STREAM_DURATION; | ||
|
||
function setUp() public { | ||
vm.createSelectFork(vm.rpcUrl('mainnet'), 20092863); | ||
proposal = new AaveV3Ethereum_V4ALServiceProviderProposal_20240614(); | ||
} | ||
|
||
/** | ||
* @dev executes the generic test suite including e2e and config snapshots | ||
*/ | ||
function test_defaultProposalExecution() public { | ||
defaultTest( | ||
'AaveV3Ethereum_V4ALServiceProviderProposal_20240614', | ||
AaveV3Ethereum.POOL, | ||
address(proposal) | ||
); | ||
} | ||
|
||
function testProposalExecution() public { | ||
uint256 nextCollectorStreamID = AaveV3Ethereum.COLLECTOR.getNextStreamId(); | ||
uint256 ALGHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
uint256 CollectorV3GHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
executePayload(vm, address(proposal)); | ||
|
||
// Check balances directly after proposal execution (upfront payment distributed to Aave Labs) | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS), | ||
ALGHOBalanceBefore + GHO_UPFRONT_AMOUNT | ||
); | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
CollectorV3GHOBalanceBefore - GHO_UPFRONT_AMOUNT | ||
); | ||
|
||
// Checking if the streams have been created properly | ||
// Scoping to avoid "stack too deep" error | ||
{ | ||
( | ||
address senderGHO, | ||
address recipientGHO, | ||
uint256 depositGHO, | ||
address tokenAddressGHO, | ||
uint256 startTimeGHO, | ||
uint256 stopTimeGHO, | ||
uint256 remainingBalanceGHO, | ||
|
||
) = AaveV3Ethereum.COLLECTOR.getStream(nextCollectorStreamID); | ||
|
||
assertEq(senderGHO, address(AaveV3Ethereum.COLLECTOR)); | ||
assertEq(recipientGHO, AAVE_LABS); | ||
assertEq(depositGHO, ACTUAL_STREAM_AMOUNT_GHO); | ||
assertEq(tokenAddressGHO, AaveV3EthereumAssets.GHO_UNDERLYING); | ||
assertEq(stopTimeGHO - startTimeGHO, GHO_STREAM_DURATION); | ||
assertEq(remainingBalanceGHO, ACTUAL_STREAM_AMOUNT_GHO); | ||
} | ||
|
||
// Checking if Aave Labs can withdraw from the stream | ||
vm.startPrank(AAVE_LABS); | ||
vm.warp(block.timestamp + GHO_STREAM_DURATION); | ||
|
||
// Currently Collector has less funds than stream amount | ||
assertLe( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
ACTUAL_STREAM_AMOUNT_GHO | ||
); | ||
|
||
// Partial withdrawal of Collector's remaining balance | ||
AaveV3Ethereum.COLLECTOR.withdrawFromStream( | ||
nextCollectorStreamID, | ||
CollectorV3GHOBalanceBefore - GHO_UPFRONT_AMOUNT | ||
); | ||
uint256 nextALGHOBalance = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
|
||
// Aave Labs received the entirety of Collector's balance | ||
assertEq(ALGHOBalanceBefore, nextALGHOBalance - CollectorV3GHOBalanceBefore); | ||
|
||
// Check Collector balance after stream withdrawal | ||
uint256 CollectorV3GHOBalanceAfter = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
assertEq(CollectorV3GHOBalanceAfter, 0); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
// Test giving V2 Collector more funds and full withdrawing | ||
function testProposalExecutionPrankFunds() public { | ||
uint256 nextCollectorStreamID = AaveV3Ethereum.COLLECTOR.getNextStreamId(); | ||
uint256 ALGHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
|
||
// Giving the Collector enough funds to cover the stream | ||
deal( | ||
AaveV3EthereumAssets.GHO_UNDERLYING, | ||
address(AaveV3Ethereum.COLLECTOR), | ||
GHO_UPFRONT_AMOUNT + GHO_STREAM_AMOUNT | ||
); | ||
|
||
uint256 CollectorV3GHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
executePayload(vm, address(proposal)); | ||
|
||
// Check balances directly after proposal execution (upfront payment distributed to Aave Labs) | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS), | ||
ALGHOBalanceBefore + GHO_UPFRONT_AMOUNT | ||
); | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
CollectorV3GHOBalanceBefore - GHO_UPFRONT_AMOUNT | ||
); | ||
|
||
// Checking if the streams have been created properly | ||
// Scoping to avoid "stack too deep" error | ||
{ | ||
( | ||
address senderGHO, | ||
address recipientGHO, | ||
uint256 depositGHO, | ||
address tokenAddressGHO, | ||
uint256 startTimeGHO, | ||
uint256 stopTimeGHO, | ||
uint256 remainingBalanceGHO, | ||
|
||
) = AaveV3Ethereum.COLLECTOR.getStream(nextCollectorStreamID); | ||
|
||
assertEq(senderGHO, address(AaveV3Ethereum.COLLECTOR)); | ||
assertEq(recipientGHO, AAVE_LABS); | ||
assertEq(depositGHO, ACTUAL_STREAM_AMOUNT_GHO); | ||
assertEq(tokenAddressGHO, AaveV3EthereumAssets.GHO_UNDERLYING); | ||
assertEq(stopTimeGHO - startTimeGHO, GHO_STREAM_DURATION); | ||
assertEq(remainingBalanceGHO, ACTUAL_STREAM_AMOUNT_GHO); | ||
} | ||
|
||
// Checking if Aave Labs can withdraw from the stream | ||
vm.startPrank(AAVE_LABS); | ||
vm.warp(block.timestamp + GHO_STREAM_DURATION); | ||
|
||
assertGe( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
ACTUAL_STREAM_AMOUNT_GHO | ||
); | ||
|
||
AaveV3Ethereum.COLLECTOR.withdrawFromStream(nextCollectorStreamID, ACTUAL_STREAM_AMOUNT_GHO); | ||
uint256 nextALGHOBalance = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
|
||
assertEq( | ||
ALGHOBalanceBefore, | ||
nextALGHOBalance - (ACTUAL_STREAM_AMOUNT_GHO + GHO_UPFRONT_AMOUNT) | ||
); | ||
|
||
// Check Collector balance after stream withdrawal | ||
uint256 CollectorV3GHOBalanceAfter = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
assertEq( | ||
CollectorV3GHOBalanceAfter, | ||
CollectorV3GHOBalanceBefore - (ACTUAL_STREAM_AMOUNT_GHO + GHO_UPFRONT_AMOUNT) | ||
); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
// Test showing V2 Collector currently doesn't have enough funds | ||
function testProposalExecutionFailureFunding() public { | ||
uint256 nextCollectorStreamID = AaveV3Ethereum.COLLECTOR.getNextStreamId(); | ||
uint256 ALGHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
|
||
uint256 CollectorV3GHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
executePayload(vm, address(proposal)); | ||
|
||
// Check balances directly after proposal execution (upfront payment distributed to Aave Labs) | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS), | ||
ALGHOBalanceBefore + GHO_UPFRONT_AMOUNT | ||
); | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
CollectorV3GHOBalanceBefore - GHO_UPFRONT_AMOUNT | ||
); | ||
|
||
// Checking if the streams have been created properly | ||
// Scoping to avoid "stack too deep" error | ||
{ | ||
( | ||
address senderGHO, | ||
address recipientGHO, | ||
uint256 depositGHO, | ||
address tokenAddressGHO, | ||
uint256 startTimeGHO, | ||
uint256 stopTimeGHO, | ||
uint256 remainingBalanceGHO, | ||
|
||
) = AaveV3Ethereum.COLLECTOR.getStream(nextCollectorStreamID); | ||
|
||
assertEq(senderGHO, address(AaveV3Ethereum.COLLECTOR)); | ||
assertEq(recipientGHO, AAVE_LABS); | ||
assertEq(depositGHO, ACTUAL_STREAM_AMOUNT_GHO); | ||
assertEq(tokenAddressGHO, AaveV3EthereumAssets.GHO_UNDERLYING); | ||
assertEq(stopTimeGHO - startTimeGHO, GHO_STREAM_DURATION); | ||
assertEq(remainingBalanceGHO, ACTUAL_STREAM_AMOUNT_GHO); | ||
} | ||
|
||
// Checking if Aave Labs can withdraw from the stream | ||
vm.startPrank(AAVE_LABS); | ||
vm.warp(block.timestamp + GHO_STREAM_DURATION); | ||
|
||
assertLe( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
ACTUAL_STREAM_AMOUNT_GHO | ||
); | ||
|
||
vm.expectRevert(stdError.arithmeticError); | ||
AaveV3Ethereum.COLLECTOR.withdrawFromStream(nextCollectorStreamID, ACTUAL_STREAM_AMOUNT_GHO); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function testProposalExecutionFuzzWithdrawalAmounts( | ||
uint256 withdrawalAmount, | ||
uint256 waitPeriod | ||
) public { | ||
waitPeriod = bound(waitPeriod, 1, GHO_STREAM_DURATION); | ||
withdrawalAmount = bound( | ||
withdrawalAmount, | ||
1, | ||
waitPeriod * (ACTUAL_STREAM_AMOUNT_GHO / GHO_STREAM_DURATION) | ||
); | ||
|
||
// Giving the Collector enough funds to cover the stream | ||
deal( | ||
AaveV3EthereumAssets.GHO_UNDERLYING, | ||
address(AaveV3Ethereum.COLLECTOR), | ||
GHO_UPFRONT_AMOUNT + GHO_STREAM_AMOUNT | ||
); | ||
|
||
assertGe( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
ACTUAL_STREAM_AMOUNT_GHO | ||
); | ||
|
||
uint256 ALGHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
uint256 CollectorV3GHOBalanceBefore = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
uint256 nextCollectorStreamID = AaveV3Ethereum.COLLECTOR.getNextStreamId(); | ||
executePayload(vm, address(proposal)); | ||
|
||
// Check balances directly after proposal execution (upfront payment distributed to Aave Labs) | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS), | ||
ALGHOBalanceBefore + GHO_UPFRONT_AMOUNT | ||
); | ||
assertEq( | ||
IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(address(AaveV3Ethereum.COLLECTOR)), | ||
CollectorV3GHOBalanceBefore - GHO_UPFRONT_AMOUNT | ||
); | ||
|
||
vm.startPrank(AAVE_LABS); | ||
vm.warp(block.timestamp + waitPeriod); | ||
|
||
// Withdraw fuzzed amount from stream | ||
AaveV3Ethereum.COLLECTOR.withdrawFromStream(nextCollectorStreamID, withdrawalAmount); | ||
|
||
uint256 ALGHOBalanceAfter = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf(AAVE_LABS); | ||
uint256 CollectorV3GHOBalanceAfter = IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( | ||
address(AaveV3Ethereum.COLLECTOR) | ||
); | ||
|
||
assertEq(ALGHOBalanceAfter - ALGHOBalanceBefore, withdrawalAmount + GHO_UPFRONT_AMOUNT); | ||
assertEq( | ||
CollectorV3GHOBalanceAfter, | ||
CollectorV3GHOBalanceBefore - (withdrawalAmount + GHO_UPFRONT_AMOUNT) | ||
); | ||
|
||
vm.stopPrank(); | ||
} | ||
} |
Oops, something went wrong.