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

Prepare LBP withdraw #821

Merged
merged 20 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e04cf3f
feat: add withdraw lbp liquidity withdraw script
thomas-waite May 18, 2022
5d09213
feat: add TC proposal script
thomas-waite May 18, 2022
3518a49
refactor: validate safe addresses set
thomas-waite May 18, 2022
ecdd690
Merge branch 'consolidate-to-one-vote' into prep-lbp-withdraw
thomas-waite May 18, 2022
437362e
Merge branch 'develop' into prep-lbp-withdraw
thomas-waite May 23, 2022
8be4899
test: remove need to drop liquidity
thomas-waite May 23, 2022
b93fd66
refactor: remove executed proposal, update block num
thomas-waite May 23, 2022
48ad2a3
refactor: use exitPool in withdraw proposal
thomas-waite May 23, 2022
43ab496
docs: update proposal name and copy
thomas-waite May 23, 2022
64aa64f
Merge branch 'develop' into prep-lbp-withdraw
thomas-waite May 23, 2022
434f695
refactor: make use of ratio PCV controller
thomas-waite May 23, 2022
2a8dbef
docs: update proposal copy
thomas-waite May 23, 2022
19f131c
refactor: remove TC multisig from being safe address
thomas-waite May 24, 2022
8830555
docs: update proposal copy
thomas-waite May 24, 2022
5fc72b1
Merge branch 'develop' into prep-lbp-withdraw
thomas-waite Jun 6, 2022
8d66561
refactor: remove unneeded safe addresses, cleanup
thomas-waite Jun 6, 2022
44065d8
refactor: update block num, add proposal back in
thomas-waite Jun 6, 2022
1ab8c54
test: unpause RAI psm for e2e
thomas-waite Jun 6, 2022
1f77029
test: setup test fixture for incentives
thomas-waite Jun 6, 2022
a818cbe
test: relax sanity check on DAI amount
thomas-waite Jun 7, 2022
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
2 changes: 1 addition & 1 deletion block.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14885000
14915266
20 changes: 16 additions & 4 deletions proposals/dao/end_tribe_incentives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import {
TeardownUpgradeFunc,
ValidateUpgradeFunc
} from '@custom-types/types';
import { getImpersonatedSigner } from '@test/helpers';
import { getImpersonatedSigner, time } from '@test/helpers';
import { forceEth } from '@test/integration/setup/utils';

const toBN = ethers.BigNumber.from;

/*
TIP-109: Discontinue Tribe Incentives
Expand Down Expand Up @@ -42,7 +40,21 @@ const deploy: DeployUpgradeFunc = async (deployAddress: string, addresses: Named
// This could include setting up Hardhat to impersonate accounts,
// ensuring contracts have a specific state, etc.
const setup: SetupUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
console.log(`No actions to complete in setup for fip${fipNumber}`);
const stakeAmount = ethers.constants.WeiPerEther.mul(40_000);
const curve3Metapool = '0x06cb22615BA53E60D67Bf6C341a0fD5E718E1655';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a fixture that sets up a staker to have some TRIBE rewards to claim.

It is later relied on in an e2e test to ensure that the ending of incentives does not stop people who were staking LP tokens from claiming rewards. It will be removed along with the test once the proposal executes.

const curvePoolId = 1;
const curve3LPWhale = '0xdc69d4cb5b86388fff0b51885677e258883534ae';
const curveLPStaker = await getImpersonatedSigner(curve3LPWhale);
const curveLPToken = await ethers.getContractAt('ERC20', curve3Metapool);

await forceEth(curve3LPWhale);
await curveLPToken.connect(curveLPStaker).approve(contracts.tribalChief.address, stakeAmount);
await contracts.tribalChief.connect(curveLPStaker).deposit(curvePoolId, stakeAmount, 0);

// Set the pool to have a non-zero AP, so can test can claim rewards
const daoSigner = await getImpersonatedSigner(addresses.feiDAOTimelock);
await contracts.tribalChief.connect(daoSigner).set(curvePoolId, 500, ethers.constants.AddressZero, false);
await time.increase(86400 * 7);
};

// Tears down any changes made in setup() that need to be
Expand Down
67 changes: 67 additions & 0 deletions proposals/dao/fip_104b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import {
DeployUpgradeFunc,
NamedAddresses,
SetupUpgradeFunc,
TeardownUpgradeFunc,
ValidateUpgradeFunc
} from '@custom-types/types';
import { time } from '@test/helpers';
import { BigNumber } from 'ethers';

/*
Withdraw liquidity from the LBP once it has finished
*/

const fipNumber = 'fip_104b';

let initialDaiPCVBalance: BigNumber;
let initialTCDpiBalance: BigNumber;

// Do any deployments
// This should exclusively include new contract deployments
const deploy: DeployUpgradeFunc = async (deployAddress: string, addresses: NamedAddresses, logging: boolean) => {
console.log(`No deploy actions for fip${fipNumber}`);
return {
// put returned contract objects here
};
};

// Do any setup necessary for running the test.
// This could include setting up Hardhat to impersonate accounts,
// ensuring contracts have a specific state, etc.
const setup: SetupUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
const dpi = contracts.dpi;
initialTCDpiBalance = await dpi.balanceOf(addresses.tribalCouncilSafe);
initialDaiPCVBalance = await contracts.compoundDaiPCVDeposit.balance();
logging && console.log('Initial dai balance: ', initialDaiPCVBalance.toString());

// Fast forward to end of LPB
const timeRemaining = await contracts.dpiToDaiLBPSwapper.remainingTime();
await time.increase(timeRemaining);
};

// Tears down any changes made in setup() that need to be
// cleaned up before doing any validation checks.
const teardown: TeardownUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
console.log(`No actions to complete in teardown for fip${fipNumber}`);
};

// Run any validations required on the fip using mocha or console logging
// IE check balances, check state of contracts, etc.
const validate: ValidateUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
// 1. Validate withdrawn liquidity destinations
const sanityCheckDAIAmount = ethers.constants.WeiPerEther.mul(3_200_000);
const finalDAIDepositBalance = await contracts.compoundDaiPCVDeposit.balance();
const daiGained = finalDAIDepositBalance.sub(initialDaiPCVBalance);
expect(daiGained).to.be.bignumber.at.least(sanityCheckDAIAmount);

const dpi = contracts.dpi;
const sanityCheckDPIAmount = ethers.constants.WeiPerEther.mul(1500);
const finalTCDpiBalance = await dpi.balanceOf(addresses.tribalCouncilSafe);
const dpiGained = finalTCDpiBalance.sub(initialTCDpiBalance);
expect(dpiGained).to.be.bignumber.at.least(sanityCheckDPIAmount);
};

export { deploy, setup, teardown, validate };
48 changes: 48 additions & 0 deletions proposals/description/fip_104b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ProposalDescription } from '@custom-types/types';

const fip_104b: ProposalDescription = {
title: 'FIP_104b: Withdraw LBP liquidity',
commands: [
{
target: 'dpiToDaiLBPSwapper',
values: '0',
method: 'exitPool(address)',
arguments: ['{compoundDaiPCVDeposit}'],
description: 'Withdraw all DAI and DPI from LBP pool to the compoundDAIPCVDeposit (~$3.8m)'
},
{
target: 'ratioPCVControllerV2',
values: '0',
method: 'withdrawRatioERC20(address,address,address,uint256)',
arguments: [
'{compoundDaiPCVDeposit}', // pcvDeposit
'{dpi}', // token
'{tribalCouncilSafe}', // to
thomas-waite marked this conversation as resolved.
Show resolved Hide resolved
'10000' // basisPoints, 100%
],
description:
'Withdraw all DPI from the Compound DAI PCV deposit (~$200k) to the TribalCouncil multisig, where it will be liquidated'
},
{
target: 'compoundDaiPCVDeposit',
values: '0',
method: 'deposit()',
arguments: [],
description: 'Deposit DAI on compoundDAIPCVdeposit into Compound'
}
],
description: `
FIP_104b: Withdraw all liquidity from the DPI LBP.

This FIP cleans up and finishes elements of the PCV reinforcement process snapshotted here:
https://snapshot.fei.money/#/proposal/0x2fd5bdda0067098f6c0520fe309dfe90ca403758f0ce98c1854a00bf38999674
and discussed here: https://tribe.fei.money/t/fip-104-fei-pcv-reinforcement-proposal/4162 .

Specifically it:
- Exits the LBP pool and withdraws all liquidity to the compound DAI deposit
- Withdraws the DPI from the deposit to the TribalCouncil multisig, using the ratioPCVController
- Deposits the DAI on the deposit into Compound
`
};

export default fip_104b;
25 changes: 15 additions & 10 deletions test/integration/proposals_config.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import { ProposalCategory, ProposalsConfigMap } from '@custom-types/types';

import repay_fuse_bad_debt from '@proposals/description/repay_fuse_bad_debt';
import register_proposal from '@proposals/description/register_proposal';
import end_tribe_incentives from '@proposals/description/end_tribe_incentives';
import fip_104b from '@proposals/description/fip_104b';

const proposals: ProposalsConfigMap = {
register_proposal: {
deploy: false, // deploy flag for whether to run deploy action during e2e tests or use mainnet state
totalValue: 0, // amount of ETH to send to DAO execution
proposal: register_proposal, // full proposal file, imported from '@proposals/description/fip_xx.ts'
proposalId: '',
affectedContractSignoff: ['core'],
deprecatedContractSignoff: [],
category: ProposalCategory.TC
},
repay_fuse_bad_debt: {
deploy: false, // deploy flag for whether to run deploy action during e2e tests or use mainnet state
totalValue: 0, // amount of ETH to send to DAO execution
Expand Down Expand Up @@ -64,6 +55,20 @@ const proposals: ProposalsConfigMap = {
'votiumBriberD3pool'
],
category: ProposalCategory.TC
},
fip_104b: {
deploy: false, // deploy flag for whether to run deploy action during e2e tests or use mainnet state
totalValue: 0, // amount of ETH to send to DAO execution
proposal: fip_104b, // full proposal file, imported from '@proposals/description/fip_xx.ts'
proposalId: '',
affectedContractSignoff: [
'dpiToDaiLBPSwapper',
'compoundDaiPCVDeposit',
'tribalCouncilSafe',
'ratioPCVControllerV2'
],
deprecatedContractSignoff: [],
category: ProposalCategory.DAO
}
};

Expand Down
6 changes: 6 additions & 0 deletions test/integration/tests/psm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ describe('e2e-peg-stability-module', function () {

// Set floor to something sufficiently low for tests to pass - RAI price on-chain fluctuates
await raiPriceBoundPSM.connect(impersonatedSigners[userAddress]).setOracleFloorBasisPoints(25000);

// Ensure RAI PSM is not paused
const isPaused = await raiPriceBoundPSM.paused();
if (isPaused) {
await raiPriceBoundPSM.connect(impersonatedSigners[userAddress]).unpause();
}
});

it('exchanges 1000 FEI for rai', async () => {
Expand Down
19 changes: 10 additions & 9 deletions test/integration/tests/tribeIncentivesEnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import proposals from '@test/integration/proposals_config';
import { TestEndtoEndCoordinator } from '../setup';
import { forceEth } from '@test/integration/setup/utils';
import { TribalChief, Tribe } from '@custom-types/contracts';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';

const toBN = ethers.BigNumber.from;
chai.use(CBN(ethers.BigNumber));
Expand All @@ -21,11 +22,12 @@ describe('e2e-end-tribe-incentives', function () {
let doLogging: boolean;
let tribe: Tribe;
let tribalChief: TribalChief;
let curveLPStaker: SignerWithAddress;

const receiver = '0xbEA4B2357e8ec53AF60BbcA4bc570332a7C7E232';
const curvePoolId = 1;
const curve3Metapool = '0x06cb22615BA53E60D67Bf6C341a0fD5E718E1655';
const curvePoolStaker = '0x019EdcB493Bd91e2b25b70f26D5d9041Fd7EF946';
const curve3LPWhale = '0xdc69d4cb5b86388fff0b51885677e258883534ae';
const pool8User = '0x9544A83A8cB74062c836AA11565d4BB4A54fe40D';

beforeEach(async function () {
Expand All @@ -52,24 +54,24 @@ describe('e2e-end-tribe-incentives', function () {

tribe = contracts.tribe as Tribe;
tribalChief = contracts.tribalChief as TribalChief;

curveLPStaker = await getImpersonatedSigner(curve3LPWhale);
await forceEth(curve3LPWhale);
});

it('should be able to harvest existing TRIBE rewards and withdraw principle from an LP pool', async () => {
const initialBalance = await tribe.balanceOf(receiver);
const stakerInPoolSigner = await getImpersonatedSigner(curvePoolStaker);
await forceEth(curvePoolStaker);

const curveLPToken = await ethers.getContractAt('ERC20', curve3Metapool);

// Harvest already earnt TRIBE rewards
await tribalChief.connect(stakerInPoolSigner).harvest(curvePoolId, receiver);
await tribalChief.connect(curveLPStaker).harvest(curvePoolId, receiver);
const finalBalance = await tribe.balanceOf(receiver);
const harvestedTribe = finalBalance.sub(initialBalance);
expect(harvestedTribe).to.be.bignumber.at.least(toBN(1));

// Withdraw principle from staked pool
const receiverBalanceBefore = await curveLPToken.balanceOf(receiver);
await tribalChief.connect(stakerInPoolSigner).withdrawAllAndHarvest(curvePoolId, receiver);
await tribalChief.connect(curveLPStaker).withdrawAllAndHarvest(curvePoolId, receiver);
const receiverBalanceAfter = await curveLPToken.balanceOf(receiver);
const withdrawnPrinciple = receiverBalanceAfter.sub(receiverBalanceBefore);
expect(withdrawnPrinciple).to.be.bignumber.at.least(toBN(1));
Expand All @@ -78,8 +80,7 @@ describe('e2e-end-tribe-incentives', function () {
it('should NOT be able to harvest future TRIBE rewards from an LP pool', async () => {
// Harvest, to zero out already earned rewards
const firstHarvestInitial = await tribe.balanceOf(receiver);
const stakerInPoolSigner = await getImpersonatedSigner(curvePoolStaker);
await tribalChief.connect(stakerInPoolSigner).harvest(curvePoolId, receiver);
await tribalChief.connect(curveLPStaker).harvest(curvePoolId, receiver);
const firstHarvestFinal = await tribe.balanceOf(receiver);
expect(firstHarvestFinal.sub(firstHarvestInitial)).to.be.bignumber.greaterThan(toBN(1));

Expand All @@ -88,7 +89,7 @@ describe('e2e-end-tribe-incentives', function () {

// Attempt to harvest again. As rewards have been stopped, it should not harvest any Tribe
const balanceBeforeHarvest = await tribe.balanceOf(receiver);
await tribalChief.connect(stakerInPoolSigner).harvest(curvePoolId, receiver);
await tribalChief.connect(curveLPStaker).harvest(curvePoolId, receiver);
const balanceAfterHarvest = await tribe.balanceOf(receiver);

const harvestedTribe = balanceAfterHarvest.sub(balanceBeforeHarvest);
Expand Down