Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ampl distribution #290

Merged
merged 12 commits into from
Apr 11, 2024
1 change: 1 addition & 0 deletions .assets/010633e355dbf154c1151f809618bd82bf6c78f6.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .assets/223a1aac9347ec68266ca4077ea5d03b73ca9e05.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .assets/4048416bedf0808a47706e49a9f98ee64701623a.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .assets/81174f6db1d6055f4be1846c1155b509e25ef500.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .assets/ab240e54d4468f7b1cd32614f764cbef5a270f32.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .assets/e8e7b9196f167723da0b9cc203b01762e5360791.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Raw diff

```json
{}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Raw diff

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

import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import {AaveV2Ethereum, AaveV2EthereumAssets} from 'aave-address-book/AaveV2Ethereum.sol';
import {IProposalGenericExecutor} from 'aave-helpers/interfaces/IProposalGenericExecutor.sol';

interface IDistributionCreator {
struct CampaignParameters {
// POPULATED ONCE CREATED

// ID of the campaign. This can be left as a null bytes32 when creating campaigns
// on Merkl.
bytes32 campaignId;
// CHOSEN BY CAMPAIGN CREATOR

// Address of the campaign creator, if marked as address(0), it will be overriden with the
// address of the `msg.sender` creating the campaign
address creator;
// Address of the token used as a reward
address rewardToken;
// Amount of `rewardToken` to distribute across all the epochs
// Amount distributed per epoch is `amount/numEpoch`
uint256 amount;
// Type of campaign
uint32 campaignType;
// Timestamp at which the campaign should start
uint32 startTimestamp;
// Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600
uint32 duration;
// Extra data to pass to specify the campaign
bytes campaignData;
}

function campaign(bytes32 id) external view returns (CampaignParameters memory);

function acceptConditions() external;

function createCampaign(CampaignParameters memory newCampaign) external returns (bytes32);
}

/**
* @title Interim aAMPL distribution
* @author BGD Labs
* - Snapshot: https://snapshot.org/#/aave.eth/proposal/0xb7226dd6441b67225924082215f7a512bfd98252897ee43a879084e07ab53607
* - Discussion: https://governance.aave.com/t/arfc-aampl-interim-distribution/17184
*/
contract AaveV2Ethereum_InterimAAMPLDistribution_20240409 is IProposalGenericExecutor {
IDistributionCreator public constant DISTRIBUTION_CREATOR =
IDistributionCreator(0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd);
address public constant REWARD_TOKEN = AaveV2EthereumAssets.USDC_UNDERLYING;
uint256 public constant REWARD_AMOUNT = 300_000e6;
string public constant FILE_URL = 'TODO';

function execute() external {
// 1. send funds to executor
// 0.5% fee for angle merkle distributor
uint256 amountPlusFee = REWARD_AMOUNT + (REWARD_AMOUNT * 5) / 1000;
AaveV2Ethereum.COLLECTOR.transfer(
AaveV2EthereumAssets.USDC_A_TOKEN,
address(this),
amountPlusFee + 1 // account for imprecision on transfer
);
AaveV2Ethereum.POOL.withdraw(REWARD_TOKEN, amountPlusFee, address(this));
// 2. approve to merkl
IERC20(REWARD_TOKEN).approve(0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd, amountPlusFee);
// 3. accept tos
DISTRIBUTION_CREATOR.acceptConditions();
// 4.create campaign
IDistributionCreator.CampaignParameters memory newCampaign = IDistributionCreator
.CampaignParameters({
campaignId: '',
creator: address(0),
sendra marked this conversation as resolved.
Show resolved Hide resolved
rewardToken: REWARD_TOKEN,
amount: amountPlusFee,
campaignType: 4,
startTimestamp: uint32(block.timestamp + 2 hours),
duration: 1 hours,
sendra marked this conversation as resolved.
Show resolved Hide resolved
campaignData: abi.encode(FILE_URL, string(''), bytes('0x'))
});
DISTRIBUTION_CREATOR.createCampaign(newCampaign);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AaveV2Ethereum, AaveV2EthereumAssets} from 'aave-address-book/AaveV2Ethereum.sol';
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';

import 'forge-std/Test.sol';
import {ProtocolV2TestBase, ReserveConfig} from 'aave-helpers/ProtocolV2TestBase.sol';
import {AaveV2Ethereum_InterimAAMPLDistribution_20240409, IDistributionCreator} from './AaveV2Ethereum_InterimAAMPLDistribution_20240409.sol';

/**
* @dev Test for AaveV2Ethereum_InterimAAMPLDistribution_20240409
* command: make test-contract filter=AaveV2Ethereum_InterimAAMPLDistribution_20240409
*/
contract AaveV2Ethereum_InterimAAMPLDistribution_20240409_Test is ProtocolV2TestBase {
AaveV2Ethereum_InterimAAMPLDistribution_20240409 internal proposal;

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

/**
* @dev executes the generic test suite including e2e and config snapshots
*/
function test_defaultProposalExecution() public {
defaultTest(
'AaveV2Ethereum_InterimAAMPLDistribution_20240409',
AaveV2Ethereum.POOL,
address(proposal)
);
}

function test_dataCorrectness() public {
executePayload(vm, address(proposal));

IDistributionCreator.CampaignParameters memory campaign = proposal
.DISTRIBUTION_CREATOR()
.campaign(bytes32(0xf9c2eb37d44c3495bb42c51d2483f332ace5f1a0e02d828a3bc6f32e5d02ab30));

assertEq(campaign.creator, GovernanceV3Ethereum.EXECUTOR_LVL_1);
assertEq(campaign.rewardToken, AaveV2EthereumAssets.USDC_UNDERLYING);
// assertEq(campaign.amount, 300_000e6);
assertEq(campaign.campaignType, 4);
assertGt(campaign.startTimestamp, block.timestamp);
assertEq(campaign.duration, 3600);
(string memory url, , ) = abi.decode(campaign.campaignData, (string, string, bytes));
assertEq(url, proposal.FILE_URL());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: "Interim aAMPL distribution"
author: "BGD Labs"
discussions: "https://governance.aave.com/t/arfc-aampl-interim-distribution/17184"
snapshot: "https://snapshot.org/#/aave.eth/proposal/0xb7226dd6441b67225924082215f7a512bfd98252897ee43a879084e07ab53607"
---

## Simple Summary

Distribute 300.000 USDC to users affected by aAMPL problem.

## Motivation

On December 2023, a problem was detected on the AMPL custom reserve on Aave v2 Ethereum, causing an unexpected inflation of AMPL-related balances and supply, not following the intended design by the Ampleforth team.

While further analysis is performed for the most reasonable strategy on giving withdrawal liquidity for aAMPL supplies, an interim distribution of 300’000 USD value is proposed as lower threshold, to allow aAMPL suppliers to proceed partially with their withdrawals.

With [aip 72](https://vote.onaave.com/proposal/?proposalId=72&ipfsHash=0xaa46d2cf629d68cc84bcc83156b2fd8e54819c5e848c229c7e62d1f6212886cc) having passed the governance process, aAMPL transfers are no longer permitted, which allows to snapshot the current aAMPL balances to perform a fair distribution between affected users.

## Specification

The proposal will perform the following steps upon execution:

- withdraw 301.5k USDC from the collector (300k for the distribution and 1.5k as fee for angle labs)
- approve the full amount to [0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd](https://etherscan.io/address/0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd) which is the distribution creator by angle labs
sakulstra marked this conversation as resolved.
Show resolved Hide resolved
- sign the tos of https://app.merkl.xyz/
sakulstra marked this conversation as resolved.
Show resolved Hide resolved
- create a campaign to distribute funds to the affected users

2 hours after proposal execution, users will be able to claim the USDC on https://app.merkl.xyz/

sendra marked this conversation as resolved.
Show resolved Hide resolved
## References

- Implementation: [AaveV2Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/AaveV2Ethereum_InterimAAMPLDistribution_20240409.sol)
- Tests: [AaveV2Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/AaveV2Ethereum_InterimAAMPLDistribution_20240409.t.sol)
- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0xb7226dd6441b67225924082215f7a512bfd98252897ee43a879084e07ab53607)
- [Discussion](https://governance.aave.com/t/arfc-aampl-interim-distribution/17184)
- [Distribution](todo - link to the ipfs used by merkl)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils} from 'aave-helpers/GovV3Helpers.sol';
import {EthereumScript} from 'aave-helpers/ScriptUtils.sol';
import {AaveV2Ethereum_InterimAAMPLDistribution_20240409} from './AaveV2Ethereum_InterimAAMPLDistribution_20240409.sol';

/**
* @dev Deploy Ethereum
* deploy-command: make deploy-ledger contract=src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/InterimAAMPLDistribution_20240409.s.sol:DeployEthereum chain=mainnet
* verify-command: npx catapulta-verify -b broadcast/InterimAAMPLDistribution_20240409.s.sol/1/run-latest.json
*/
contract DeployEthereum is EthereumScript {
function run() external broadcast {
// deploy payloads
address payload0 = GovV3Helpers.deployDeterministic(
type(AaveV2Ethereum_InterimAAMPLDistribution_20240409).creationCode
);

// compose action
IPayloadsControllerCore.ExecutionAction[]
memory actions = new IPayloadsControllerCore.ExecutionAction[](1);
actions[0] = GovV3Helpers.buildAction(payload0);

// register action at payloadsController
GovV3Helpers.createPayload(actions);
}
}

/**
* @dev Create Proposal
* command: make deploy-ledger contract=src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/InterimAAMPLDistribution_20240409.s.sol:CreateProposal chain=mainnet
*/
contract CreateProposal is EthereumScript {
function run() external {
// create payloads
PayloadsControllerUtils.Payload[] memory payloads = new PayloadsControllerUtils.Payload[](1);

// compose actions for validation
IPayloadsControllerCore.ExecutionAction[]
memory actionsEthereum = new IPayloadsControllerCore.ExecutionAction[](1);
actionsEthereum[0] = GovV3Helpers.buildAction(
type(AaveV2Ethereum_InterimAAMPLDistribution_20240409).creationCode
);
payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum);

// create proposal
vm.startBroadcast();
GovV3Helpers.createProposal(
vm,
payloads,
GovV3Helpers.ipfsHashFile(
vm,
'src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/InterimAAMPLDistribution.md'
)
);
}
}
15 changes: 15 additions & 0 deletions src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {ConfigFile} from '../../generator/types';
export const config: ConfigFile = {
rootOptions: {
configFile: 'src/20240409_AaveV2Ethereum_MerklTest/config.ts',
pools: ['AaveV2Ethereum'],
title: 'Interim aAMPL distribution',
shortName: 'InterimAAMPLDistribution',
date: '20240409',
author: 'BGD Labs',
discussion: 'https://governance.aave.com/t/arfc-aampl-interim-distribution/17184',
snapshot:
'https://snapshot.org/#/aave.eth/proposal/0xb7226dd6441b67225924082215f7a512bfd98252897ee43a879084e07ab53607',
},
poolOptions: {AaveV2Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 19627195}}},
};
21 changes: 21 additions & 0 deletions src/20240409_AaveV2Ethereum_InterimAAMPLDistribution/process.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { AaveV2Ethereum } from "@bgd-labs/aave-address-book";
import chaos from "./chaos.json" assert { type: "json" };

const processedUsers = chaos
.filter((f) => Math.floor(f.USDC_Per_User) !== 0)
.reduce((acc, f) => {
acc[f.Wallet] = { aAmplCompensation: Math.floor(f.USDC_Per_User) };
return acc;
}, {});

const totalAmount = chaos.reduce((acc, i) => {
return acc + Math.floor(i.USDC_Per_User);
}, 0);

const angleJSON = {
rewardToken: AaveV2Ethereum.ASSETS.USDC.UNDERLYING,
rewards: processedUsers,
};

// console.log(totalAmount);
console.log(JSON.stringify(angleJSON, null, 2));
Loading