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/feature flags #22

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Changes -
tl'dr - The current RenBTC/wBTC Zap contract (`contracts/Zap.sol`) for ibBTC estimates the best mint route from all the available curve pools and byWBTC. Since we want Zaps to always go through the RenWBTC Curve Pool the Zap contract needs to be adjusted to remove the other options

- `calcMintWithRen`, `calcMintWithWbtc` functions in `Zap.sol` changed to calculate mint amount only from pool[0] ie. renWBTC Curve pool

# Interest-bearing Badger BTC (ibBTC)

- [bBTC.sol](./contracts/bBTC.sol) is the interest-bearing bitcoin ERC20 token.
Expand Down
49 changes: 37 additions & 12 deletions contracts/Core.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import {IPeak} from "./interfaces/IPeak.sol";
import {IbBTC} from "./interfaces/IbBTC.sol";
import {ICore} from "./interfaces/ICore.sol";
import {GovernableProxy} from "./common/proxy/GovernableProxy.sol";
import {PausableSlot} from "./common/PausableSlot.sol";

contract Core is GovernableProxy, ICore {
contract Core is GovernableProxy, PausableSlot, ICore {
using SafeERC20 for IERC20;
using SafeMath for uint;
using Math for uint;
Expand All @@ -22,7 +23,7 @@ contract Core is GovernableProxy, ICore {

BadgerGuestListAPI public guestList;

enum PeakState { Extinct, Active, Dormant }
enum PeakState { Extinct, Active, RedeemOnly, MintOnly }
mapping(address => PeakState) public peaks;

address[] public peakAddresses;
Expand All @@ -31,7 +32,10 @@ contract Core is GovernableProxy, ICore {
uint public redeemFee;
uint public accumulatedFee;

uint256[50] private __gap;
address public guardian;
address constant public badgerGovernance = 0xB65cef03b9B89f99517643226d76e286ee999e77;

uint256[49] private __gap;

// END OF STORAGE VARIABLES

Expand All @@ -46,6 +50,16 @@ contract Core is GovernableProxy, ICore {
bBTC = IbBTC(_bBTC);
}

modifier onlyGuardianOrGovernance() {
require(msg.sender == guardian || msg.sender == owner(), "onlyGuardianOrGovernance");
_;
}

modifier onlyGovernanceOrBadgerGovernance() {
require(msg.sender == badgerGovernance || msg.sender == owner(), "onlyGovernanceOrBadgerGovernance");
_;
}

/**
* @notice Mint bBTC
* @dev Only whitelisted peaks can call this function
Expand All @@ -55,9 +69,10 @@ contract Core is GovernableProxy, ICore {
function mint(uint btc, address account, bytes32[] calldata merkleProof)
override
external
whenNotPaused
returns(uint)
{
require(peaks[msg.sender] == PeakState.Active, "PEAK_INACTIVE");
require(peaks[msg.sender] == PeakState.Active || peaks[msg.sender] == PeakState.MintOnly, "PEAK_INACTIVE_OR_MINTING_DISABLED");
if (address(guestList) != address(0)) {
require(
guestList.authorized(account, btc, merkleProof),
Expand Down Expand Up @@ -91,9 +106,9 @@ contract Core is GovernableProxy, ICore {
* @param bBtc bBTC amount to redeem
* @return btc amount redeemed, scaled by 1e36
*/
function redeem(uint bBtc, address account) override external returns (uint) {
function redeem(uint bBtc, address account) override external whenNotPaused returns (uint) {
require(bBtc > 0, "REDEEMING_0_bBTC");
require(peaks[msg.sender] != PeakState.Extinct, "PEAK_EXTINCT");
require(peaks[msg.sender] == PeakState.Active || peaks[msg.sender] == PeakState.RedeemOnly, "PEAK_INACTIVE_OR_REDEMPTION_DISABLED");
(uint btc, uint fee) = bBtcToBtc(bBtc);
accumulatedFee = accumulatedFee.add(fee);
bBTC.burn(account, bBtc);
Expand All @@ -119,7 +134,7 @@ contract Core is GovernableProxy, ICore {
/**
* @notice Collect all the accumulated fee (denominated in bBTC)
*/
function collectFee() external {
function collectFee() external whenNotPaused {
require(feeSink != address(0), "NULL_ADDRESS");
uint _fee = accumulatedFee;
require(_fee > 0, "NO_FEE");
Expand All @@ -141,15 +156,13 @@ contract Core is GovernableProxy, ICore {
}
}

/* ##### Governance ##### */

/**
* @notice Whitelist a new peak
* @param peak Address of the contract that interfaces with the 3rd-party protocol
*/
function whitelistPeak(address peak)
external
onlyGovernance
onlyGovernanceOrBadgerGovernance
{
require(
peaks[peak] == PeakState.Extinct,
Expand All @@ -173,7 +186,7 @@ contract Core is GovernableProxy, ICore {
*/
function setPeakStatus(address peak, PeakState state)
external
onlyGovernance
onlyGovernanceOrBadgerGovernance
{
require(
peaks[peak] != PeakState.Extinct,
Expand Down Expand Up @@ -211,9 +224,21 @@ contract Core is GovernableProxy, ICore {
feeSink = _feeSink;
}

function setGuestList(address _guestList) external onlyGovernance {
function setGuestList(address _guestList) external onlyGovernanceOrBadgerGovernance {
guestList = BadgerGuestListAPI(_guestList);
}

function setGuardian(address _guardian) external onlyGovernanceOrBadgerGovernance {
guardian = _guardian;
}

function pause() external onlyGuardianOrGovernance {
_pause();
}

function unpause() external onlyGovernanceOrBadgerGovernance {
_unpause();
}
}

interface BadgerGuestListAPI {
Expand Down
100 changes: 5 additions & 95 deletions contracts/Zap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
}

/**
* @notice Calculate the most optimal route and expected ibbtc amount when minting with renBTC.
* @notice Calculate mint through renWBTC pool route and expected ibbtc amount when minting with renBTC.
* @dev Use returned params poolId, idx and bBTC in the call to mint(...)
The last param `minOut` in mint(...) should be a bit more than the returned bBTC value.
For instance 0.2% - 1% higher depending on slippage tolerange.
Expand All @@ -180,26 +180,10 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {

// poolId=0, idx=0
(bBTC, fee) = curveLPToIbbtc(0, pools[0].deposit.calc_token_amount([amount,0], true));

(_ibbtc, _fee) = curveLPToIbbtc(1, pools[1].deposit.calc_token_amount([amount,0,0], true));
if (_ibbtc > bBTC) {
bBTC = _ibbtc;
fee = _fee;
poolId = 1;
// idx=0
}

(_ibbtc, _fee) = curveLPToIbbtc(2, pools[2].deposit.calc_token_amount([0,amount,0,0], true));
if (_ibbtc > bBTC) {
bBTC = _ibbtc;
fee = _fee;
poolId = 2;
idx = 1;
}
}

/**
* @notice Calculate the most optimal route and expected ibbtc amount when minting with wBTC.
* @notice Calculate mint through renWBTC pool route and expected ibbtc amount when minting with wBTC.
* @dev Use returned params poolId, idx and bBTC in the call to mint(...)
The last param `minOut` in mint(...) should be a bit more than the returned bBTC value.
For instance 0.2% - 1% higher depending on slippage tolerange.
Expand All @@ -216,31 +200,6 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
// poolId=0
(bBTC, fee) = curveLPToIbbtc(0, pools[0].deposit.calc_token_amount([0,amount], true));
idx = 1;

(_ibbtc, _fee) = curveLPToIbbtc(1, pools[1].deposit.calc_token_amount([0,amount,0], true));
if (_ibbtc > bBTC) {
bBTC = _ibbtc;
fee = _fee;
poolId = 1;
// idx=1
}

(_ibbtc, _fee) = curveLPToIbbtc(2, pools[2].deposit.calc_token_amount([0,0,amount,0], true));
if (_ibbtc > bBTC) {
bBTC = _ibbtc;
fee = _fee;
poolId = 2;
idx = 2;
}

// for byvwbtc, sett.pricePerShare returns a wbtc value, as opposed to lpToken amount in setts
(_ibbtc, _fee) = byvWbtcPeak.calcMint(amount.mul(1e8).div(IbyvWbtc(address(pools[3].sett)).pricePerShare()));
if (_ibbtc > bBTC) {
bBTC = _ibbtc;
fee = _fee;
poolId = 3;
// idx value will be ignored anyway
}
}

/**
Expand Down Expand Up @@ -279,7 +238,7 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
}

/**
* @notice Calculate the most optimal route and expected token amount when redeeming ibbtc.
* @notice Calculate redeem through renWBTC pool route and expected token amount when redeeming ibbtc.
* @dev Use returned params poolId, idx and out in the call to redeem(...)
The last param `redeem` in mint(...) should be a bit less than the returned `out` value.
For instance 0.2% - 1% lesser depending on slippage tolerange.
Expand All @@ -300,7 +259,7 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
}

/**
* @notice Calculate the most optimal route and expected renbtc amount when redeeming ibbtc.
* @notice Calculate redeem through renWBTC pool route and expected renbtc amount when redeeming ibbtc.
* @dev Use returned params poolId, idx and renAmount in the call to redeem(...)
The last param `minOut` in redeem(...) should be a bit less than the returned renAmount value.
For instance 0.2% - 1% lesser depending on slippage tolerange.
Expand All @@ -318,24 +277,6 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
// poolId=0, idx=0
(_lp, fee) = ibbtcToCurveLP(0, amount);
renAmount = pools[0].deposit.calc_withdraw_one_coin(_lp, 0);

(_lp, _fee) = ibbtcToCurveLP(1, amount);
_ren = pools[1].deposit.calc_withdraw_one_coin(_lp, 0);
if (_ren > renAmount) {
renAmount = _ren;
fee = _fee;
poolId = 1;
// idx=0
}

(_lp, _fee) = ibbtcToCurveLP(2, amount);
_ren = pools[2].deposit.calc_withdraw_one_coin(_lp, 1);
if (_ren > renAmount) {
renAmount = _ren;
fee = _fee;
poolId = 2;
idx = 1;
}
}

/**
Expand All @@ -358,37 +299,6 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
(_lp, fee) = ibbtcToCurveLP(0, amount);
wBTCAmount = pools[0].deposit.calc_withdraw_one_coin(_lp, 1);
idx = 1;

(_lp, _fee) = ibbtcToCurveLP(1, amount);
_wbtc = pools[1].deposit.calc_withdraw_one_coin(_lp, 1);
if (_wbtc > wBTCAmount) {
wBTCAmount = _wbtc;
fee = _fee;
poolId = 1;
// idx=1
}

(_lp, _fee) = ibbtcToCurveLP(2, amount);
_wbtc = pools[2].deposit.calc_withdraw_one_coin(_lp, 2);
if (_wbtc > wBTCAmount) {
wBTCAmount = _wbtc;
fee = _fee;
poolId = 2;
idx = 2;
}

uint _byvWbtc;
uint _max;
(_byvWbtc,_fee,_max) = byvWbtcPeak.calcRedeem(amount);
if (amount <= _max) {
uint strategyFee = _byvWbtc.mul(pools[3].sett.withdrawalFee()).div(10000);
_wbtc = _byvWbtc.sub(strategyFee).mul(pools[3].sett.pricePerShare()).div(1e8);
if (_wbtc > wBTCAmount) {
wBTCAmount = _wbtc;
fee = _fee.add(strategyFee);
poolId = 3;
}
}
}

function ibbtcToCurveLP(uint poolId, uint bBtc) public view returns(uint lp, uint fee) {
Expand All @@ -401,7 +311,7 @@ contract Zap is Initializable, Pausable, AccessControlDefendedBase {
} else {
// pesimistically charge 0.5% on the withdrawal.
// Actual fee might be lesser if the vault keeps keeps a buffer
uint strategyFee = sett.mul(controller.strategies(pool.lpToken).withdrawalFee()).div(1000);
uint strategyFee = sett.mul(controller.strategies(pool.lpToken).withdrawalFee()).div(10000);
lp = sett.sub(strategyFee).mul(pool.sett.getPricePerFullShare()).div(1e18);
fee = fee.add(strategyFee);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/common/AccessControlDefended.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract AccessControlDefendedBase {
}

contract AccessControlDefended is GovernableProxy, AccessControlDefendedBase {
uint256[50] private __gap;
uint256[48] private __gap;
Copy link
Member

Choose a reason for hiding this comment

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

Why reduce 2 slots?


function approveContractAccess(address account) external onlyGovernance {
_approveContractAccess(account);
Expand Down
82 changes: 82 additions & 0 deletions contracts/common/PausableSlot.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.6.11;

contract PausableSlot {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);

/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);

bytes32 constant PAUSED_SLOT = keccak256("_paused");

/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view returns (bool _paused) {
bytes32 position = PAUSED_SLOT;
assembly {
_paused := sload(position)
}
}

function _setPaused(bool _paused) internal {
bytes32 position = PAUSED_SLOT;
assembly {
sstore(position, _paused)
}
}

/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}

/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}

/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_setPaused(true);
emit Paused(msg.sender);
}

/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_setPaused(false);
emit Unpaused(msg.sender);
}
}
Loading