Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Treasury Management - GHO Funding #323

Merged
merged 10 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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';
import {AaveV2Ethereum, AaveV2EthereumAssets} from 'aave-address-book/AaveV2Ethereum.sol';
import {AaveSwapper} from 'aave-helpers/swaps/AaveSwapper.sol';
import {AaveMisc} from 'aave-address-book/AaveMisc.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol';

interface aTokenV1 {
Copy link
Contributor

Choose a reason for hiding this comment

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

this interface is not used anywhere and could be removed

function redeem(uint256 _amount) external;
}

/**
* @title GHO Funding
* @author TokenLogic
* - Snapshot: TODO
sakulstra marked this conversation as resolved.
Show resolved Hide resolved
* - Discussion: https://governance.aave.com/t/arfc-treasury-management-gho-funding/14887
sakulstra marked this conversation as resolved.
Show resolved Hide resolved
*/
contract AaveV3_Ethereum_GHOFunding_20230926 is IProposalGenericExecutor {

struct Asset {
address underlying;
address aToken;
address oracle;
uint256 amount;
}

function execute() external {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this so unnecessarily imprecise?

The forum post & snapshot said 1.6M
the updated(after aip) post now says 1.611M
if it was executed now, it was already 1.613M

Wouldn't it be better to just be exact? as there's not really a good reason not to be - you already have all the amount and could just make e.g. DAI on v3 fill the 1.6M.

// DAI v2
Asset memory DAIv2 = Asset({
underlying: AaveV2EthereumAssets.DAI_UNDERLYING,
aToken: AaveV2EthereumAssets.DAI_A_TOKEN,
oracle: 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9,
Copy link
Contributor

@rustboyar rustboyar Oct 16, 2023

Choose a reason for hiding this comment

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

not important, but AaveV3EthereumAssets.DAI_ORACLE could be used

amount: IERC20(AaveV2EthereumAssets.DAI_A_TOKEN).balanceOf(address(AaveV3Ethereum.COLLECTOR))
});

// BUSD v2
Asset memory BUSDv2 = Asset({
underlying: AaveV2EthereumAssets.BUSD_UNDERLYING,
aToken: AaveV2EthereumAssets.BUSD_A_TOKEN,
oracle: 0x833D8Eb16D306ed1FbB5D7A2E019e106B960965A,
amount: IERC20(AaveV2EthereumAssets.BUSD_A_TOKEN).balanceOf(address(AaveV3Ethereum.COLLECTOR))
});

// DAI v3
Asset memory DAIv3 = Asset({
underlying: AaveV3EthereumAssets.DAI_UNDERLYING,
aToken: AaveV3EthereumAssets.DAI_A_TOKEN,
oracle: 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9,
amount: 400_000 * 1e18
});

// USDT v2
Asset memory USDTv2 = Asset({
underlying: AaveV2EthereumAssets.USDT_UNDERLYING,
aToken: AaveV2EthereumAssets.USDT_A_TOKEN,
oracle: 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D,
amount: 1_100_000 * 1e6
});

// USDT v3
Asset memory USDTv3 = Asset({
underlying: AaveV3EthereumAssets.USDT_UNDERLYING,
aToken: AaveV3EthereumAssets.USDT_A_TOKEN,
oracle: 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D,
amount: 500_000 * 1e6
});


////// DAI v2 & v3 swap //////
AaveV3Ethereum.COLLECTOR.transfer(DAIv2.aToken, address(this), DAIv2.amount);
AaveV3Ethereum.COLLECTOR.transfer(DAIv3.aToken, address(this), DAIv3.amount);
uint256 swapperBalance =
AaveV2Ethereum.POOL.withdraw(DAIv2.underlying, DAIv2.amount, AaveMisc.AAVE_SWAPPER_ETHEREUM);
Copy link
Collaborator

Choose a reason for hiding this comment

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

this might revert if transfer is 1 wei short

swapperBalance +=
AaveV3Ethereum.POOL.withdraw(DAIv3.underlying, DAIv3.amount, AaveMisc.AAVE_SWAPPER_ETHEREUM);
Copy link
Collaborator

Choose a reason for hiding this comment

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

as well

swapAsset(DAIv2, swapperBalance);

////// BUSD v2 & v1 swap //////
// transfer max busd to payload
swapperBalance = IERC20(BUSDv2.underlying).balanceOf(address(AaveV3Ethereum.COLLECTOR));
AaveV3Ethereum.COLLECTOR.transfer(BUSDv2.underlying, address (this), swapperBalance);
// transfer specific amount of busdv2 to payload
AaveV3Ethereum.COLLECTOR.transfer(BUSDv2.aToken, address(this), BUSDv2.amount);
swapperBalance += AaveV2Ethereum.POOL.withdraw(BUSDv2.underlying, BUSDv2.amount, address(this));

SafeERC20.safeTransfer(IERC20(BUSDv2.underlying), AaveMisc.AAVE_SWAPPER_ETHEREUM, swapperBalance);
swapAsset(BUSDv2, swapperBalance);

////// USDT v2 & v3 swap //////
AaveV3Ethereum.COLLECTOR.transfer(USDTv2.aToken, address(this), USDTv2.amount);
AaveV3Ethereum.COLLECTOR.transfer(USDTv3.aToken, address(this), USDTv3.amount);
swapperBalance =
sakulstra marked this conversation as resolved.
Show resolved Hide resolved
AaveV2Ethereum.POOL.withdraw(USDTv2.underlying, USDTv2.amount, AaveMisc.AAVE_SWAPPER_ETHEREUM);
Copy link
Collaborator

Choose a reason for hiding this comment

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

as well

swapperBalance +=
AaveV3Ethereum.POOL.withdraw(USDTv3.underlying, USDTv3.amount, AaveMisc.AAVE_SWAPPER_ETHEREUM);
swapAsset(USDTv2, swapperBalance);
}

function swapAsset(Asset memory asset, uint256 amount) internal {
address milkman = 0x11C76AD590ABDFFCD980afEC9ad951B160F02797;
address priceChecker = 0xe80a1C615F75AFF7Ed8F08c9F21f9d00982D666c;
address ghoOracle = 0x3f12643D3f6f874d39C2a4c9f2Cd6f2DbAC877FC;
AaveSwapper swapper = AaveSwapper(AaveMisc.AAVE_SWAPPER_ETHEREUM);
swapper.swap(
milkman,
priceChecker,
asset.underlying,
AaveV3EthereumAssets.GHO_UNDERLYING,
asset.oracle,
ghoOracle,
address(AaveV3Ethereum.COLLECTOR),
amount,
50
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import 'forge-std/Test.sol';
import {GovHelpers} from 'aave-helpers/GovHelpers.sol';
import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol';
import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/ProtocolV3TestBase.sol';
import {AaveV3_Ethereum_GHOFunding_20230926} from './AaveV3_Ethereum_GHOFunding_20230926.sol';
import {AaveV2Ethereum, AaveV2EthereumAssets} from 'aave-address-book/AaveV2Ethereum.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';

/**
* @dev Test for AaveV3_Ethereum_GHOFunding_20230926
* command: make test-contract filter=AaveV3_Ethereum_GHOFunding_20230926
*/
contract AaveV3_Ethereum_GHOFunding_20230926_Test is ProtocolV3TestBase {
AaveV3_Ethereum_GHOFunding_20230926 internal proposal;

struct Swap {
address proxy;
address underlying;
uint256 amount;
}

function setUp() public {
vm.createSelectFork(vm.rpcUrl('mainnet'), 18274396);
proposal = new AaveV3_Ethereum_GHOFunding_20230926();
}

function testProposalExecution() public {
uint256 daiv2CollectorBalance = IERC20(AaveV2EthereumAssets.DAI_A_TOKEN).balanceOf(address(AaveV2Ethereum.COLLECTOR));
uint256 busdv2CollectorBalance = IERC20(AaveV2EthereumAssets.BUSD_A_TOKEN).balanceOf(address(AaveV2Ethereum.COLLECTOR));
uint256 busdCollectorBalance = IERC20(AaveV2EthereumAssets.BUSD_UNDERLYING).balanceOf(address(AaveV2Ethereum.COLLECTOR));
// uint256 tusdv2CollectorBalance = 75_800 * 1e18;
Swap[3] memory swaps;
// milkman creates intermediary contract for each swap
// while swap is not executed the assets will be in these swap-specific proxy addresses instead of aaveSwapper
// proxy contracts addresses are deterministic, they could be derived via code.
// I simulated execution and copy pasted the address for simplicity
// see https://etherscan.io/address/0x11C76AD590ABDFFCD980afEC9ad951B160F02797#code#L878
swaps[0] = Swap({
proxy: 0x2414B7eDd549E62e8a5877b73D96C80bAbC30bca,
underlying: AaveV2EthereumAssets.DAI_UNDERLYING,
amount: (400_000 * 1e18) + daiv2CollectorBalance
});
swaps[1] = Swap({
proxy: 0x86487dad62c99A37d0052ed56BF1EafF2959294D,
underlying: AaveV2EthereumAssets.BUSD_UNDERLYING,
amount: busdv2CollectorBalance + busdCollectorBalance
});

swaps[2] = Swap({
proxy: 0x3Df592eae98c2b4f312ADE339C01BBE2C8444618,
underlying: AaveV2EthereumAssets.USDT_UNDERLYING,
amount: 1_600_000 * 1e6
});

GovHelpers.executePayload(vm, address(proposal), AaveGovernanceV2.SHORT_EXECUTOR);

for(uint i = 0; i < swaps.length; i++) {
uint256 proxyBalanceAfter = IERC20(swaps[i].underlying).balanceOf(swaps[i].proxy);
assertEq(proxyBalanceAfter, swaps[i].amount);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {GovHelpers} from 'aave-helpers/GovHelpers.sol';
import {EthereumScript} from 'aave-helpers/ScriptUtils.sol';
import {AaveV3_Ethereum_GHOFunding_20230926} from './AaveV3_Ethereum_GHOFunding_20230926.sol';

/**
* @dev Deploy AaveV3_Ethereum_GHOFunding_20230926
* command: make deploy-ledger contract=src/20230926_AaveV3_Eth_GHOFunding/AaveV3_GHOFunding_20230926.s.sol:DeployEthereum chain=mainnet
*/
contract DeployEthereum is EthereumScript {
function run() external broadcast {
new AaveV3_Ethereum_GHOFunding_20230926();
}
}

/**
* @dev Create Proposal
* command: make deploy-ledger contract=src/20230926_AaveV3_Eth_GHOFunding/AaveV3_GHOFunding_20230926.s.sol:CreateProposal chain=mainnet
*/
contract CreateProposal is EthereumScript {
function run() external broadcast {
GovHelpers.Payload[] memory payloads = new GovHelpers.Payload[](1);
payloads[0] = GovHelpers.buildMainnet(address(0)); // TODO
GovHelpers.createProposal(
payloads,
GovHelpers.ipfsHashFile(vm, 'src/20230926_AaveV3_Eth_GHOFunding/GHOFunding.md')
);
}
}
49 changes: 49 additions & 0 deletions src/20230926_AaveV3_Eth_GHOFunding/GHOFunding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: "GHO Funding"
author: "TokenLogic"
discussions: "https://governance.aave.com/t/arfc-treasury-management-gho-funding/14887"
sakulstra marked this conversation as resolved.
Show resolved Hide resolved
---

## Simple Summary

This publication aims to acquire GHO from secondary markets to support the Aave DAO's short-term funding requirements.

## Motivation

The primary objective of this publication is to shift Aave DAO's expenditure towards being nominated in GHO. The following outlines some potential use cases for GHO:

* 328,000 GHO allowance over 6 months [Aave Grants Continuation Proposal](https://governance.aave.com/t/temp-check-updated-aave-grants-continuation-proposal/14951)
* 550,000 GHO over 3 months[Aave Events & Sponsorship](https://governance.aave.com/t/temp-check-aave-events-sponsorship-budget/14953)
* 75,000 GHO over 3 months [Expansion of “Orbit”](https://governance.aave.com/t/arfc-expansion-of-orbit-a-dao-funded-delegate-platform-initiative/14785)
* 406,000 GHO over 3 months [GHO Liquidity Committee](https://governance.aave.com/t/temp-check-treasury-management-create-and-fund-gho-liquidity-committee/14800)
* TBA Future ACI Funding Request (Renewal mid-October)

Totaling 1,359,000 GHO plus future ACI budget.

This proposal is expected to acquire approximately $1.6M from secondary markets based on current holdings in the Ethereum Treasury.

## Specification
Using the Aave Swap Contract, convert the following asset holdings to GHO:

* [aDAI v2](https://etherscan.io/token/0x028171bca77440897b824ca71d1c56cac55b68a3?a=0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)
* [aBUSD v2](https://etherscan.io/token/0xa361718326c15715591c299427c62086f69923d9?a=0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)
* [BUSD](https://etherscan.io/token/0x4fabb145d64652a948d72533023f6e7a623c7c53?a=0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)
* [400,000 aEthDAI v3](https://etherscan.io/token/0x018008bfb33d285247a21d44e50697654f754e63?a=0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)
* [400,000 aUSDT v2](https://etherscan.io/token/0x3ed3b47dd13ec9a98b44e6204a523e766b225811?a=0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c)

The GHO will be transferred to the [Aave Ethereum Treasury](https://etherscan.io/address/0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c).

## References

- Implementation: [Ethereum](https://github.com/bgd-labs/aave-proposals/blob/main/src/20230926_AaveV3_Eth_GHOFunding/AaveV3_Ethereum_GHOFunding_20230926.sol)
- Tests: [Ethereum](https://github.com/bgd-labs/aave-proposals/blob/main/src/20230926_AaveV3_Eth_GHOFunding/AaveV3_Ethereum_GHOFunding_20230926.t.sol)
- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0xb094cdc806d407d0cf4ea00e595ae95b8c145f77b77cce165c463326cc757639)
- [Discussion](https://governance.aave.com/t/arfc-treasury-management-gho-funding/14887)
sakulstra marked this conversation as resolved.
Show resolved Hide resolved

# Disclosure

TokenLogic receives no payment from Aave DAO or any external source for the creation of this proposal. TokenLogic is a delegate within the Aave ecosystem.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).