From 9295bcc02192f2ebd6bf034e7ec220cf3a20c82a Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 17 May 2021 18:45:27 -0400 Subject: [PATCH] More accurate method of calculating 3pool balances (#592) --- .../contracts/mocks/curve/MockCurvePool.sol | 4 + contracts/contracts/strategies/ICurvePool.sol | 2 + .../strategies/ThreePoolStrategy.sol | 3 +- contracts/deploy/017_3pool_strategy_update.js | 81 +++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 contracts/deploy/017_3pool_strategy_update.js diff --git a/contracts/contracts/mocks/curve/MockCurvePool.sol b/contracts/contracts/mocks/curve/MockCurvePool.sol index cff2210789..2331b08ff4 100644 --- a/contracts/contracts/mocks/curve/MockCurvePool.sol +++ b/contracts/contracts/mocks/curve/MockCurvePool.sol @@ -12,6 +12,7 @@ contract MockCurvePool is ERC20 { using StableMath for uint256; address[] public coins; + uint256[3] public balances; address lpToken; constructor(address[3] memory _coins, address _lpToken) public { @@ -34,6 +35,7 @@ contract MockCurvePool is ERC20 { uint256 assetDecimals = Helpers.getDecimals(coins[i]); // Convert to 1e18 and add to sum sum += _amounts[i].scaleBy(int8(18 - assetDecimals)); + balances[uint256(i)] = balances[i].add(uint256(_amounts[i])); } } // Hacky way of simulating slippage to check _minAmount @@ -64,6 +66,7 @@ contract MockCurvePool is ERC20 { amounts[uint256(_index)] = _amount; uint256 amount = calc_withdraw_one_coin(_amount, _index); IERC20(coins[uint256(_index)]).transfer(msg.sender, amount); + balances[uint256(_index)] = balances[uint256(_index)].sub(amount); } function get_virtual_price() external view returns (uint256) { @@ -80,6 +83,7 @@ contract MockCurvePool is ERC20 { IERC20(coins[i]).balanceOf(address(this)) ); IERC20(coins[i]).transfer(msg.sender, amount); + balances[uint256(i)] = balances[uint256(i)].sub(amount); } } } diff --git a/contracts/contracts/strategies/ICurvePool.sol b/contracts/contracts/strategies/ICurvePool.sol index 2f31dd839d..533f87d665 100644 --- a/contracts/contracts/strategies/ICurvePool.sol +++ b/contracts/contracts/strategies/ICurvePool.sol @@ -5,6 +5,8 @@ interface ICurvePool { function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external; + function balances(uint256) external view returns (uint256); + function calc_token_amount(uint256[3] calldata _amounts, bool _deposit) external returns (uint256); diff --git a/contracts/contracts/strategies/ThreePoolStrategy.sol b/contracts/contracts/strategies/ThreePoolStrategy.sol index 6b1517dd4b..5fa785722d 100644 --- a/contracts/contracts/strategies/ThreePoolStrategy.sol +++ b/contracts/contracts/strategies/ThreePoolStrategy.sol @@ -264,7 +264,8 @@ contract ThreePoolStrategy is InitializableAbstractStrategy { uint256 pTokenTotalSupply = IERC20(assetToPToken[_asset]).totalSupply(); if (pTokenTotalSupply > 0) { - uint256 curveBalance = IERC20(_asset).balanceOf(address(curvePool)); + uint256 poolCoinIndex = _getPoolCoinIndex(_asset); + uint256 curveBalance = curvePool.balances(poolCoinIndex); if (curveBalance > 0) { balance = totalPTokens.mul(curveBalance).div(pTokenTotalSupply); } diff --git a/contracts/deploy/017_3pool_strategy_update.js b/contracts/deploy/017_3pool_strategy_update.js new file mode 100644 index 0000000000..d53e925445 --- /dev/null +++ b/contracts/deploy/017_3pool_strategy_update.js @@ -0,0 +1,81 @@ +const { + isMainnet, + isFork, + isRinkeby, + isSmokeTest, +} = require("../test/helpers.js"); +const { + log, + deployWithConfirmation, + withConfirmation, + executeProposal, + sendProposal, +} = require("../utils/deploy"); +const { proposeArgs } = require("../utils/governor"); +const { getTxOpts } = require("../utils/tx"); + +const deployName = "017_3pool_strategy_update"; + +const runDeployment = async (hre) => { + console.log(`Running ${deployName} deployment...`); + const { governorAddr } = await hre.getNamedAccounts(); + + // Signers + const sGovernor = await ethers.provider.getSigner(governorAddr); + + const cThreePoolStrategyProxy = await ethers.getContract( + "ThreePoolStrategyProxy" + ); + + // Deploy Updated 3Pool strategy. + const dThreePoolStrategy = await deployWithConfirmation("ThreePoolStrategy"); + + // Proposal for the governor update the contract + const propDescription = "Update 3pool implementation"; + const propArgs = await proposeArgs([ + { + contract: cThreePoolStrategyProxy, + signature: "upgradeTo(address)", + args: [dThreePoolStrategy.address], + }, + ]); + + if (isMainnet) { + // On Mainnet, only propose. The enqueue and execution are handled manually via multi-sig. + log("Sending proposal to governor..."); + await sendProposal(propArgs, propDescription); + log("Proposal sent."); + } else if (isFork) { + // On Fork we can send the proposal then impersonate the guardian to execute it. + log("Sending and executing proposal..."); + await executeProposal(propArgs, propDescription); + log("Proposal executed."); + } else { + // Hardcoding gas estimate on Rinkeby since it fails for an undetermined reason... + const gasLimit = isRinkeby ? 1000000 : null; + await withConfirmation( + cThreePoolStrategyProxy + .connect(sGovernor) + .upgradeTo(dThreePoolStrategy.address, await getTxOpts(gasLimit)) + ); + log("Switched implementation of ThreePoolStrategy"); + } + + return true; +}; + +const main = async (hre) => { + console.log(`Running ${deployName} deployment...`); + if (!hre) { + hre = require("hardhat"); + } + await runDeployment(hre); + console.log(`${deployName} deploy done.`); + return true; +}; + +main.id = deployName; +main.dependencies = ["014_3pool_strategy"]; +main.skip = () => !(isMainnet || isRinkeby || isFork) || isSmokeTest; + +module.exports = main;