Skip to content

Commit

Permalink
Convex Changes (C4-11) (#930)
Browse files Browse the repository at this point in the history
Co-authored-by: Patrick McKelvy <pmckelvy1@gmail.com>
  • Loading branch information
akshatmittal and pmckelvy1 committed Sep 1, 2023
1 parent 165aeca commit 5e2e82f
Show file tree
Hide file tree
Showing 14 changed files with 95 additions and 17 deletions.
3 changes: 3 additions & 0 deletions contracts/plugins/assets/aave-v3/AaveV3FiatCollateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ contract AaveV3FiatCollateral is AppreciatingFiatCollateral {
using OracleLib for AggregatorV3Interface;
using FixLib for uint192;

// solhint-disable no-empty-blocks
/// @param config.chainlinkFeed Feed units: {UoA/ref}
/// @param revenueHiding {1} A value like 1e-6 that represents the maximum refPerTok to hide
constructor(CollateralConfig memory config, uint192 revenueHiding)
AppreciatingFiatCollateral(config, revenueHiding)
{}

// solhint-enable no-empty-blocks

/// @return {ref/tok} Actual quantity of whole reference units per whole collateral tokens
function _underlyingRefPerTok() internal view override returns (uint192) {
uint256 rate = StaticATokenV3LM(address(erc20)).rate(); // {ray ref/tok}
Expand Down
7 changes: 5 additions & 2 deletions contracts/plugins/assets/aave-v3/mock/MockStaticATokenV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ pragma solidity 0.8.19;
import { StaticATokenV3LM, IPool, IRewardsController } from "../vendor/StaticATokenV3LM.sol";

contract MockStaticATokenV3LM is StaticATokenV3LM {
uint256 customRate;
uint256 public customRate;

/* solhint-disable no-empty-blocks */
constructor(IPool pool, IRewardsController rewardsController)
StaticATokenV3LM(pool, rewardsController)
{}

/* solhint-enable no-empty-blocks */

function rate() public view override returns (uint256) {
if (customRate != 0) {
return customRate;
Expand All @@ -18,7 +21,7 @@ contract MockStaticATokenV3LM is StaticATokenV3LM {
return POOL.getReserveNormalizedIncome(_aTokenUnderlying);
}

function mock_setCustomRate(uint256 _customRate) external {
function mockSetCustomRate(uint256 _customRate) external {
customRate = _customRate;
}
}
4 changes: 4 additions & 0 deletions contracts/plugins/assets/aave-v3/vendor/ERC20.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/* solhint-disable */

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
Expand Down Expand Up @@ -231,3 +233,5 @@ abstract contract ERC20 {
uint256 amount
) internal virtual {}
}

/* solhint-enable */
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.10;

/* solhint-disable max-line-length */

enum Rounding {
UP,
DOWN
Expand Down Expand Up @@ -40,3 +42,5 @@ library RayMathExplicitRounding {
return a / WAD_RAY_RATIO;
}
}

/* solhint-enable max-line-length */
4 changes: 4 additions & 0 deletions contracts/plugins/assets/aave-v3/vendor/StaticATokenV3LM.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

/* solhint-disable */

import { IPool } from "@aave/core-v3/contracts/interfaces/IPool.sol";
import { DataTypes, ReserveConfiguration } from "@aave/core-v3/contracts/protocol/libraries/configuration/ReserveConfiguration.sol";
import { IScaledBalanceToken } from "@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol";
Expand Down Expand Up @@ -744,3 +746,5 @@ contract StaticATokenV3LM is
}
}
}

/* solhint-enable */
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.10;

/* solhint-disable */

import { IAaveIncentivesController } from "@aave/core-v3/contracts/interfaces/IAaveIncentivesController.sol";

interface IAToken {
Expand All @@ -16,3 +18,5 @@ interface IAToken {
*/
function scaledTotalSupply() external view returns (uint256);
}

/* solhint-enable */
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

pragma solidity ^0.8.10;

/* solhint-disable max-line-length */

/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
Expand Down Expand Up @@ -239,3 +241,5 @@ interface IERC4626 {
address owner
) external returns (uint256 assets);
}

/* solhint-enable max-line-length */
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.10;

/* solhint-disable max-line-length */

import { IPool } from "@aave/core-v3/contracts/interfaces/IPool.sol";
import { IAaveIncentivesController } from "@aave/core-v3/contracts/interfaces/IAaveIncentivesController.sol";

Expand Down Expand Up @@ -35,3 +37,5 @@ interface IInitializableStaticATokenLM {
string calldata staticATokenSymbol
) external;
}

/* solhint-enable max-line-length */
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.10;

/* solhint-disable max-line-length */

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IPool } from "@aave/core-v3/contracts/interfaces/IPool.sol";
import { IAaveIncentivesController } from "@aave/core-v3/contracts/interfaces/IAaveIncentivesController.sol";
Expand Down Expand Up @@ -214,3 +216,5 @@ interface IStaticATokenV3LM is IInitializableStaticATokenLM {
*/
function isRegisteredRewardToken(address reward) external view returns (bool);
}

/* solhint-enable max-line-length */
Original file line number Diff line number Diff line change
Expand Up @@ -289,15 +289,14 @@ contract ConvexStakingWrapper is ERC20, ReentrancyGuard {
}

function _checkpoint(address[2] memory _accounts) internal nonReentrant {
//if shutdown, no longer checkpoint in case there are problems
if (isShutdown()) return;

uint256 supply = _getTotalSupply();
uint256[2] memory depositedBalance;
depositedBalance[0] = _getDepositedBalance(_accounts[0]);
depositedBalance[1] = _getDepositedBalance(_accounts[1]);

IRewardStaking(convexPool).getReward(address(this), true);
if (!isShutdown()) {
IRewardStaking(convexPool).getReward(address(this), true);
}

_claimExtras();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ const modifyRefPerTok = async (ctx: AaveV3FiatCollateralFixtureContext, changeFa
const staticWrapper = ctx.staticWrapper
const currentRate = await staticWrapper.rate()

await staticWrapper.mock_setCustomRate(currentRate.mul(changeFactor).div(100))
await staticWrapper.mockSetCustomRate(currentRate.mul(changeFactor).div(100))
}

const reduceRefPerTok = async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ethers } from 'hardhat'
import { ContractFactory, BigNumberish } from 'ethers'
import {
ERC20Mock,
IERC20,
MockV3Aggregator,
MockV3Aggregator__factory,
TestICollateral,
Expand Down Expand Up @@ -42,6 +43,7 @@ import {
CRV,
THREE_POOL_HOLDER,
} from '../constants'
import { whileImpersonating } from '#/test/utils/impersonation'

type Fixture<T> = () => Promise<T>

Expand Down Expand Up @@ -130,7 +132,7 @@ export const deployMaxTokensCollateral = async (
const maxTokenCollOpts = {
...defaultCvxStableCollateralOpts,
...{
nTokens: bn('4'),
nTokens: 4,
erc20: fix.wrapper.address,
curvePool: fix.curvePool.address,
lpToken: SUSD_POOL_TOKEN,
Expand Down Expand Up @@ -244,7 +246,6 @@ const mintCollateralTo: MintCurveCollateralFunc<CurveCollateralFixtureContext> =
Define collateral-specific tests
*/

// eslint-disable-next-line @typescript-eslint/no-empty-function
const collateralSpecificConstructorTests = () => {
describe('Handles constructor with 4 tokens (max allowed) - sUSD', () => {
let collateral: TestICollateral
Expand Down Expand Up @@ -359,7 +360,6 @@ const collateralSpecificConstructorTests = () => {
})
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
const collateralSpecificStatusTests = () => {
it('handles properly multiple price feeds', async () => {
const MockV3AggregatorFactory = await ethers.getContractFactory('MockV3Aggregator')
Expand Down Expand Up @@ -413,6 +413,53 @@ const collateralSpecificStatusTests = () => {
const finalRefPerTok = await multiFeedCollateral.refPerTok()
expect(finalRefPerTok).to.equal(initialRefPerTok)
})

it('handles shutdown correctly', async () => {
const fix = await makeW3PoolStable()
const [, alice, bob] = await ethers.getSigners()
const amount = fp('100')
const rewardPerBlock = bn('83197823300')

const lpToken = <IERC20>(
await ethers.getContractAt(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20',
await fix.wrapper.curveToken()
)
)
const CRV = <IERC20>(
await ethers.getContractAt(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20',
'0xD533a949740bb3306d119CC777fa900bA034cd52'
)
)
await whileImpersonating(THREE_POOL_HOLDER, async (signer) => {
await lpToken.connect(signer).transfer(alice.address, amount.mul(2))
})

await lpToken.connect(alice).approve(fix.wrapper.address, ethers.constants.MaxUint256)
await fix.wrapper.connect(alice).deposit(amount, alice.address)

// let's shutdown!
await fix.wrapper.shutdown()

const prevBalance = await CRV.balanceOf(alice.address)
await fix.wrapper.connect(alice).claimRewards()
expect(await CRV.balanceOf(alice.address)).to.be.eq(prevBalance.add(rewardPerBlock))

const prevBalanceBob = await CRV.balanceOf(bob.address)

// transfer to bob
await fix.wrapper
.connect(alice)
.transfer(bob.address, await fix.wrapper.balanceOf(alice.address))

await fix.wrapper.connect(bob).claimRewards()
expect(await CRV.balanceOf(bob.address)).to.be.eq(prevBalanceBob.add(rewardPerBlock))

await expect(fix.wrapper.connect(alice).deposit(amount, alice.address)).to.be.reverted
await expect(fix.wrapper.connect(bob).withdraw(await fix.wrapper.balanceOf(bob.address))).to.not
.be.reverted
})
}

/*
Expand Down
9 changes: 4 additions & 5 deletions test/plugins/individual-collateral/curve/cvx/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
ERC20Mock,
ICurvePool,
MockV3Aggregator,
RewardableERC4626Vault,
} from '../../../../../typechain'
import { getResetFork } from '../../helpers'
import {
Expand Down Expand Up @@ -88,7 +87,7 @@ export const makeW3PoolStable = async (): Promise<Wrapped3PoolFixtureStable> =>
usdc,
usdt,
curvePool,
wrapper: wrapper as unknown as RewardableERC4626Vault,
wrapper: wrapper as unknown as ConvexStakingWrapper,
}
}

Expand Down Expand Up @@ -136,7 +135,7 @@ export const makeWSUSDPoolStable = async (): Promise<WrappedSUSDPoolFixtureStabl
const wrapper = await wrapperFactory.deploy()
await wrapper.initialize(SUSD_POOL_CVX_POOL_ID)

return { dai, usdc, usdt, susd, curvePool, wrapper: wrapper as unknown as RewardableERC4626Vault }
return { dai, usdc, usdt, susd, curvePool, wrapper }
}

export interface Wrapped3PoolFixtureVolatile extends CurveBase {
Expand Down Expand Up @@ -182,7 +181,7 @@ export const makeWTricryptoPoolVolatile = async (): Promise<Wrapped3PoolFixtureV
wbtc,
weth,
curvePool,
wrapper: wrapper as unknown as RewardableERC4626Vault,
wrapper: wrapper,
}
}

Expand All @@ -193,7 +192,7 @@ export const mintWPool = async (
recipient: string,
holder: string
) => {
const cvxWrapper = ctx.wrapper as ConvexStakingWrapper
const cvxWrapper = ctx.wrapper
const lpToken = await ethers.getContractAt(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20',
await cvxWrapper.curveToken()
Expand Down
3 changes: 1 addition & 2 deletions test/plugins/individual-collateral/curve/pluginTestTypes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { BigNumberish } from 'ethers'
import {
ConvexStakingWrapper,
CurveGaugeWrapper,
CurvePoolMock,
ERC20Mock,
MockV3Aggregator,
Expand All @@ -15,7 +14,7 @@ type Fixture<T> = () => Promise<T>

export interface CurveBase {
curvePool: CurvePoolMock
wrapper: CurveGaugeWrapper | ConvexStakingWrapper
wrapper: ConvexStakingWrapper
}

// The basic fixture context used in the Curve collateral plugin tests
Expand Down

0 comments on commit 5e2e82f

Please sign in to comment.