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

cbBTC-WBTC vault add #12

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
25 changes: 25 additions & 0 deletions contracts/base/interface/curve/ICurveDeposit_NG.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.26;

interface ICurveDeposit_NG {
function get_virtual_price() external view returns (uint);
function add_liquidity(
uint256[] calldata amounts,
uint256 min_mint_amount
) external payable;
function remove_liquidity_imbalance(
uint256[] calldata amounts,
uint256 max_burn_amount
) external;
function remove_liquidity(
uint256 _amount,
uint256[] calldata amounts
) external;
function exchange(
int128 from, int128 to, uint256 _from_amount, uint256 _min_to_amount
) external payable;
function calc_token_amount(
uint256[] calldata amounts,
bool deposit
) external view returns(uint);
}
32 changes: 25 additions & 7 deletions contracts/strategies/convex/ConvexStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import "../../base/interface/curve/ICurveDeposit_3token.sol";
import "../../base/interface/curve/ICurveDeposit_3token_meta.sol";
import "../../base/interface/curve/ICurveDeposit_4token.sol";
import "../../base/interface/curve/ICurveDeposit_4token_meta.sol";
import "../../base/interface/curve/ICurveDeposit_NG.sol";
import "../../base/interface/weth/IWETH.sol";

contract ConvexStrategy is BaseUpgradeableStrategy {
Expand All @@ -32,6 +33,7 @@ contract ConvexStrategy is BaseUpgradeableStrategy {
bytes32 internal constant _CURVE_DEPOSIT_SLOT = 0xb306bb7adebd5a22f5e4cdf1efa00bc5f62d4f5554ef9d62c1b16327cd3ab5f9;
bytes32 internal constant _NTOKENS_SLOT = 0xbb60b35bae256d3c1378ff05e8d7bee588cd800739c720a107471dfa218f74c1;
bytes32 internal constant _METAPOOL_SLOT = 0x567ad8b67c826974a167f1a361acbef5639a3e7e02e99edbc648a84b0923d5b7;
bytes32 internal constant _NG_SLOT = 0xcd6a0b148d19dba2b2780e77ba620dd79534ef7aeb84c51a65b7d73c6a83da27;

address[] public rewardTokens;

Expand All @@ -42,6 +44,7 @@ contract ConvexStrategy is BaseUpgradeableStrategy {
assert(_CURVE_DEPOSIT_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.curveDeposit")) - 1));
assert(_NTOKENS_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.nTokens")) - 1));
assert(_METAPOOL_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.metaPool")) - 1));
assert(_NG_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.NG")) - 1));
}

function initializeBaseStrategy(
Expand All @@ -54,7 +57,8 @@ contract ConvexStrategy is BaseUpgradeableStrategy {
uint256 _depositArrayPosition,
address _curveDeposit,
uint256 _nTokens,
bool _metaPool
bool _metaPool,
bool _useNG
) public initializer {

BaseUpgradeableStrategy.initialize(
Expand All @@ -77,6 +81,7 @@ contract ConvexStrategy is BaseUpgradeableStrategy {
_setCurveDeposit(_curveDeposit);
_setNTokens(_nTokens);
_setMetaPool(_metaPool);
_setNG(_useNG);
}

function depositArbCheck() public pure returns(bool) {
Expand Down Expand Up @@ -197,13 +202,19 @@ contract ConvexStrategy is BaseUpgradeableStrategy {
// we can accept 0 as minimum, this will be called only by trusted roles
uint256 minimum = 0;
if (nTokens() == 2) {
uint256[2] memory depositArray;
depositArray[depositArrayPosition()] = tokenBalance;
if (_depositToken == weth){
IWETH(weth).withdraw(tokenBalance);
ICurveDeposit_2token(_curveDeposit).add_liquidity{value:tokenBalance}(depositArray, minimum);
if (NG()) {
uint256[] memory depositArray = new uint256[](2);
depositArray[depositArrayPosition()] = tokenBalance;
ICurveDeposit_NG(_curveDeposit).add_liquidity(depositArray, minimum);
} else {
ICurveDeposit_2token(_curveDeposit).add_liquidity(depositArray, minimum);
uint256[2] memory depositArray;
depositArray[depositArrayPosition()] = tokenBalance;
if (_depositToken == weth){
IWETH(weth).withdraw(tokenBalance);
ICurveDeposit_2token(_curveDeposit).add_liquidity{value:tokenBalance}(depositArray, minimum);
} else {
ICurveDeposit_2token(_curveDeposit).add_liquidity(depositArray, minimum);
}
}
} else if (nTokens() == 3) {
uint256[3] memory depositArray;
Expand Down Expand Up @@ -364,6 +375,13 @@ contract ConvexStrategy is BaseUpgradeableStrategy {
return getBoolean(_METAPOOL_SLOT);
}

function _setNG(bool _value) internal {
setBoolean(_NG_SLOT, _value);
}

function NG() public view returns (bool) {
return getBoolean(_NG_SLOT);
}

function finalizeUpgrade() external onlyGovernance {
_finalizeUpgrade();
Expand Down
3 changes: 2 additions & 1 deletion contracts/strategies/convex/ConvexStrategyMainnet_3CRV.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ contract ConvexStrategyMainnet_3CRV is ConvexStrategy {
1, //depositArrayPosition. Find deposit transaction -> input params
curveDeposit, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
3, //nTokens -> total number of deposit tokens
false //metaPool -> if LP token address == pool address (at curve)
false, //metaPool -> if LP token address == pool address (at curve)
false
);
rewardTokens = [crv, cvx];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ contract ConvexStrategyMainnet_CVX_ETH is ConvexStrategy {
1, //depositArrayPosition. Find deposit transaction -> input params
curveDeposit, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
2, //nTokens -> total number of deposit tokens
false //metaPool -> if LP token address == pool address (at curve)
false, //metaPool -> if LP token address == pool address (at curve)
false
);

_setRewardToken(cvx);
Expand Down
3 changes: 2 additions & 1 deletion contracts/strategies/convex/ConvexStrategyMainnet_OETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ contract ConvexStrategyMainnet_OETH is ConvexStrategy {
0, //depositArrayPosition. Find deposit transaction -> input params
underlying, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
2, //nTokens -> total number of deposit tokens
false //metaPool -> if LP token address == pool address (at curve)
false, //metaPool -> if LP token address == pool address (at curve)
false
);
rewardTokens = [crv, cvx];
}
Expand Down
34 changes: 34 additions & 0 deletions contracts/strategies/convex/ConvexStrategyMainnet_cbBTC_WBTC.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.26;

import "./ConvexStrategy.sol";

contract ConvexStrategyMainnet_cbBTC_WBTC is ConvexStrategy {

constructor() {}

function initializeStrategy(
address _storage,
address _vault
) public initializer {
address underlying = address(0x839d6bDeDFF886404A6d7a788ef241e4e28F4802); // Info -> LP Token address
address rewardPool = address(0xEd211Ec6F81f3516Ef6c5DFaC6CF09cD33A6Dff3); // Info -> Rewards contract address
address crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52);
address cvx = address(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B);
address cbbtc = address(0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf);
ConvexStrategy.initializeBaseStrategy(
_storage,
underlying,
_vault,
rewardPool, // rewardPool
392, // Pool id: Info -> Rewards contract address -> read -> pid
cbbtc, // depositToken
0, //depositArrayPosition. Find deposit transaction -> input params
underlying, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
2, //nTokens -> total number of deposit tokens
false, //metaPool -> if LP token address == pool address (at curve)
true
);
rewardTokens = [crv, cvx];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ contract ConvexStrategyMainnet_crvUSD_USDC is ConvexStrategy {
0, //depositArrayPosition. Find deposit transaction -> input params
underlying, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
2, //nTokens -> total number of deposit tokens
false //metaPool -> if LP token address == pool address (at curve)
false, //metaPool -> if LP token address == pool address (at curve)
false
);
rewardTokens = [crv, cvx];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ contract ConvexStrategyMainnet_stETH_ng is ConvexStrategy {
0, //depositArrayPosition. Find deposit transaction -> input params
underlying, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
2, //nTokens -> total number of deposit tokens
false //metaPool -> if LP token address == pool address (at curve)
false, //metaPool -> if LP token address == pool address (at curve)
false
);
rewardTokens = [crv, cvx];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ contract ConvexStrategyMainnet_thUSD_3CRV is ConvexStrategy {
2, //depositArrayPosition. Find deposit transaction -> input params
curveDeposit, // deposit contract: usually underlying. Find deposit transaction -> interacted contract
4, //nTokens -> total number of deposit tokens
true //metaPool -> if LP token address == pool address (at curve)
true, //metaPool -> if LP token address == pool address (at curve)
false
);
rewardTokens = [crv, cvx];
}
Expand Down
128 changes: 128 additions & 0 deletions test/convex/cbbtc-wbtc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Utilities
const Utils = require("../utilities/Utils.js");
const { impersonates, setupCoreProtocol, depositVault } = require("../utilities/hh-utils.js");
const addresses = require("../test-config.js");

const BigNumber = require("bignumber.js");
const IERC20 = artifacts.require("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20");

const Strategy = artifacts.require("ConvexStrategyMainnet_cbBTC_WBTC");

//This test was developed at blockNumber 21230520

// Vanilla Mocha test. Increased compatibility with tools that integrate Mocha.
describe("Mainnet Convex cbBTC-WBTC", function() {
let accounts;

// external contracts
let underlying;

// external setup
let underlyingWhale = "0xA13E4AD21dC3e879bFe1Af7B47f62A7cA6b192Bd";
let weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
let cbbtc = "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf";
// parties in the protocol
let governance;
let farmer1;

// numbers used in tests
let farmerBalance;

// Core protocol contracts
let controller;
let vault;
let strategy;

async function setupExternalContracts() {
underlying = await IERC20.at("0x839d6bDeDFF886404A6d7a788ef241e4e28F4802");
console.log("Fetching Underlying at: ", underlying.address);
}

async function setupBalance(){
let etherGiver = accounts[9];
// Give whale some ether to make sure the following actions are good
await web3.eth.sendTransaction({ from: etherGiver, to: underlyingWhale, value: 10e18});

farmerBalance = await underlying.balanceOf(underlyingWhale);
await underlying.transfer(farmer1, farmerBalance, { from: underlyingWhale });
}

before(async function() {
governance = addresses.Governance;
accounts = await web3.eth.getAccounts();

await web3.eth.sendTransaction({ from: accounts[8], to: governance, value: 10e18});
await web3.eth.sendTransaction({ from: accounts[8], to: addresses.ULOwner, value: 10e18});

farmer1 = accounts[1];

// impersonate accounts
await impersonates([governance, underlyingWhale, addresses.ULOwner]);

await setupExternalContracts();
[controller, vault, strategy] = await setupCoreProtocol({
"existingVaultAddress": null,
"strategyArtifact": Strategy,
"strategyArtifactIsUpgradable": true,
"underlying": underlying,
"governance": governance,
"liquidation": [
{"uniV3": [weth, cbbtc]},
],
"uniV3Fee": [
[weth, cbbtc, '3000'],
],
"ULOwner": addresses.ULOwner
});

// whale send underlying to farmers
await setupBalance();
});

describe("Happy path", function() {
it("Farmer should earn money", async function() {
let farmerOldBalance = new BigNumber(await underlying.balanceOf(farmer1));
await depositVault(farmer1, underlying, vault, farmerBalance);
let fTokenBalance = new BigNumber(await vault.balanceOf(farmer1));

// Using half days is to simulate how we doHardwork in the real world
let hours = 10;
let blocksPerHour = 2400;
let oldSharePrice;
let newSharePrice;
for (let i = 0; i < hours; i++) {
console.log("loop ", i);

oldSharePrice = new BigNumber(await vault.getPricePerFullShare());
await controller.doHardWork(vault.address, { from: governance });
newSharePrice = new BigNumber(await vault.getPricePerFullShare());

console.log("old shareprice: ", oldSharePrice.toFixed());
console.log("new shareprice: ", newSharePrice.toFixed());
console.log("growth: ", newSharePrice.toFixed() / oldSharePrice.toFixed());

apr = (newSharePrice.toFixed()/oldSharePrice.toFixed()-1)*(24/(blocksPerHour/300))*365;
apy = ((newSharePrice.toFixed()/oldSharePrice.toFixed()-1)*(24/(blocksPerHour/300))+1)**365;

console.log("instant APR:", apr*100, "%");
console.log("instant APY:", (apy-1)*100, "%");
await vault.withdraw(fTokenBalance.div(10), { from: farmer1 });
await depositVault(farmer1, underlying, vault, new BigNumber(await underlying.balanceOf(farmer1)))
await Utils.advanceNBlock(blocksPerHour);
}
fTokenBalance = new BigNumber(await vault.balanceOf(farmer1));
await vault.withdraw(fTokenBalance, { from: farmer1 });
let farmerNewBalance = new BigNumber(await underlying.balanceOf(farmer1));
Utils.assertBNGt(farmerNewBalance, farmerOldBalance);

apr = (farmerNewBalance.toFixed()/farmerOldBalance.toFixed()-1)*(24/(blocksPerHour*hours/300))*365;
apy = ((farmerNewBalance.toFixed()/farmerOldBalance.toFixed()-1)*(24/(blocksPerHour*hours/300))+1)**365;

console.log("earned!");
console.log("Overall APR:", apr*100, "%");
console.log("Overall APY:", (apy-1)*100, "%");

await strategy.withdrawAllToVault({ from: governance }); // making sure can withdraw all for a next switch
});
});
});