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

syncPricePerShare #20

Open
wants to merge 5 commits into
base: main
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
37 changes: 36 additions & 1 deletion contracts/Core.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ contract Core is GovernableProxy, ICore {

event PeakWhitelisted(address indexed peak);
event FeeCollected(uint amount);
event PricePerShareSynced(uint indexed pps);

/**
* @param _bBTC bBTC token address
Expand Down Expand Up @@ -89,7 +90,7 @@ contract Core is GovernableProxy, ICore {
* @notice Redeem bBTC
* @dev Only whitelisted peaks can call this function
* @param bBtc bBTC amount to redeem
* @return btc amount redeemed, scaled by 1e18
* @return btc amount redeemed, scaled by 1e36
*/
function redeem(uint bBtc, address account) override external returns (uint) {
require(bBtc > 0, "REDEEMING_0_bBTC");
Expand All @@ -116,6 +117,17 @@ contract Core is GovernableProxy, ICore {
return 1e18;
}

function syncPricePerShare() public {
(address fxRoot, address fxChildTunnel) = getFxContracts();
uint pps = pricePerShare();
IFxStateSender(fxRoot).sendMessageToChild(fxChildTunnel, abi.encode(pps));
emit PricePerShareSynced(pps);
}

function getFxContracts() public view returns (address fxRoot, address fxChildTunnel) {
return (address(__gap[0]), address(__gap[1]));
}

/**
* @notice Collect all the accumulated fee (denominated in bBTC)
*/
Expand Down Expand Up @@ -155,6 +167,13 @@ contract Core is GovernableProxy, ICore {
peaks[peak] == PeakState.Extinct,
"DUPLICATE_PEAK"
);

address[] memory _peakAddresses = peakAddresses;
uint numPeaks = _peakAddresses.length;
for (uint i = 0; i < numPeaks; i++) {
require(_peakAddresses[i] != peak, "USE_setPeakStatus");
}

IPeak(peak).portfolioValue(); // sanity check
peakAddresses.push(peak);
peaks[peak] = PeakState.Active;
Expand All @@ -172,6 +191,9 @@ contract Core is GovernableProxy, ICore {
peaks[peak] != PeakState.Extinct,
"Peak is extinct"
);
if (state == PeakState.Extinct) {
require(IPeak(peak).portfolioValue() <= 1e15, "NON_TRIVIAL_FUNDS_IN_PEAK");
}
peaks[peak] = state;
}

Expand Down Expand Up @@ -204,8 +226,21 @@ contract Core is GovernableProxy, ICore {
function setGuestList(address _guestList) external onlyGovernance {
guestList = BadgerGuestListAPI(_guestList);
}

function setFxContracts(address _fxRoot, address _fxChildTunnel) external onlyGovernance {
require(_fxRoot != address(0) && _fxChildTunnel != address(0), "NULL_ADDRESS");
__gap[0] = uint(_fxRoot);
__gap[1] = uint(_fxChildTunnel);
// sanity check
(address fxRoot, address fxChildTunnel) = getFxContracts();
require(fxRoot == _fxRoot && fxChildTunnel == _fxChildTunnel, "INSANE");
}
}

interface BadgerGuestListAPI {
function authorized(address guest, uint256 amount, bytes32[] calldata merkleProof) external view returns (bool);
}

interface IFxStateSender {
function sendMessageToChild(address _receiver, bytes calldata _data) external;
}
179 changes: 170 additions & 9 deletions contracts/Zap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ contract Zap is AccessControlDefended {

IBadgerSettPeak public constant settPeak = IBadgerSettPeak(0x41671BA1abcbA387b9b2B752c205e22e916BE6e3);
IByvWbtcPeak public constant byvWbtcPeak = IByvWbtcPeak(0x825218beD8BE0B30be39475755AceE0250C50627);
IbBTC public constant ibbtc = IbBTC(0xc4E15973E6fF2A35cC804c2CF9D2a1b817a8b40F);

IERC20 public constant ibbtc = IERC20(0xc4E15973E6fF2A35cC804c2CF9D2a1b817a8b40F);
IERC20 public constant ren = IERC20(0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D);
IERC20 public constant wbtc = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
IController public constant controller = IController(0x63cF44B2548e4493Fd099222A1eC79F3344D9682);

struct Pool {
IERC20 lpToken;
Expand Down Expand Up @@ -64,6 +64,7 @@ contract Zap is AccessControlDefended {
IERC20(address(pool.sett)).safeApprove(address(byvWbtcPeak), uint(-1));
}
}
pools[2].lpToken.safeApprove(address(pools[2].deposit), uint(-1));
}

/**
Expand All @@ -81,10 +82,9 @@ contract Zap is AccessControlDefended {
blockLocked
returns(uint _ibbtc)
{
Pool memory pool = pools[poolId];

token.safeTransferFrom(msg.sender, address(this), amount);

Pool memory pool = pools[poolId];
if (poolId < 3) { // setts
_addLiquidity(pool.deposit, amount, poolId + 2, idx); // pools are such that the #tokens they support is +2 from their poolId.
pool.sett.deposit(pool.lpToken.balanceOf(address(this)));
Expand All @@ -97,7 +97,7 @@ contract Zap is AccessControlDefended {
}

require(_ibbtc >= minOut, "INSUFFICIENT_IBBTC"); // used for capping slippage in curve pools
IERC20(address(ibbtc)).safeTransfer(msg.sender, _ibbtc);
ibbtc.safeTransfer(msg.sender, _ibbtc);
}

/**
Expand Down Expand Up @@ -130,8 +130,8 @@ contract Zap is AccessControlDefended {
/**
* @notice Calculate the most optimal route and expected ibbtc amount when minting with wBTC / 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.
The last param `minOut` in mint(...) should be a bit less than the returned bBTC value.
For instance 0.2% - 1% lesser depending on slippage tolerange.
* @param amount renBTC amount
* @return poolId 0=crvRenWBTC, 1=crvRenWSBTC, 2=tbtc-sbtcCrv, 3=byvwbtc
* @return idx Index of the supported token in the curve pool (poolId). Should be ignored for poolId=3
Expand Down Expand Up @@ -236,6 +236,160 @@ contract Zap is AccessControlDefended {
uint _sett = _lp.mul(1e18).div(pool.sett.getPricePerFullShare());
return settPeak.calcMint(poolId, _sett);
}

// Redeem Methods

function redeem(IERC20 token, uint amount, uint poolId, int128 idx, uint minOut)
external
defend
blockLocked
returns(uint out)
{
ibbtc.safeTransferFrom(msg.sender, address(this), amount);

Pool memory pool = pools[poolId];
if (poolId < 3) { // setts
settPeak.redeem(poolId, amount);
pool.sett.withdrawAll();
pool.deposit.remove_liquidity_one_coin(pool.lpToken.balanceOf(address(this)), idx, minOut);
} else if (poolId == 3) { // byvwbtc
byvWbtcPeak.redeem(amount);
IbyvWbtc(address(pool.sett)).withdraw(); // withdraws all available
} else {
revert("INVALID_POOL_ID");
}
out = token.balanceOf(address(this));
token.safeTransfer(msg.sender, out);
}

/**
* @notice Calculate the most optimal 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.
* @param amount ibbtc amount
* @return poolId 0=crvRenWBTC, 1=crvRenWSBTC, 2=tbtc-sbtcCrv, 3=byvwbtc
* @return idx Index of the supported token in the curve pool (poolId). Should be ignored for poolId=3
* @return out Expected amount for token. Not for precise calculations. Doesn't factor in (deposit) fee charged by the curve pool / byvwbtc.
* @return fee Fee being charged by ibbtc + setts. Denominated in corresponding sett token
*/
function calcRedeem(address token, uint amount) external view returns(uint poolId, uint idx, uint out, uint fee) {
if (token == address(ren)) {
return calcRedeemInRen(amount);
}
if (token == address(wbtc)) {
return calcRedeemInWbtc(amount);
}
revert("INVALID_TOKEN");
}

/**
* @notice Calculate the most optimal 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.
* @param amount ibbtc amount
* @return poolId 0=crvRenWBTC, 1=crvRenWSBTC, 2=tbtc-sbtcCrv
* @return idx Index of the supported token in the curve pool (poolId)
* @return renAmount Expected renBtc. Not for precise calculations. Doesn't factor in fee charged by the curve pool
* @return fee Fee being charged by ibbtc system. Denominated in corresponding sett token
*/
function calcRedeemInRen(uint amount) public view returns(uint poolId, uint idx, uint renAmount, uint fee) {
uint _lp;
uint _fee;
uint _ren;

// 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;
}
}

/**
* @notice Calculate the most optimal route and expected wbtc amount when redeeming ibbtc.
* @dev Use returned params poolId, idx and wbtc in the call to redeem(...)
The last param `minOut` in redeem(...) should be a bit less than the returned wbtc value.
For instance 0.2% - 1% lesser depending on slippage tolerange.
* @param amount ibbtc amount
* @return poolId 0=crvRenWBTC, 1=crvRenWSBTC, 2=tbtc-sbtcCrv, 3=byvwbtc
* @return idx Index of the supported token in the curve pool (poolId)
* @return wBTCAmount Expected wbtc. Not for precise calculations. Doesn't factor in fee charged by the curve pool
* @return fee Fee being charged by ibbtc system. Denominated in corresponding sett token
*/
function calcRedeemInWbtc(uint amount) public view returns(uint poolId, uint idx, uint wBTCAmount, uint fee) {
uint _lp;
uint _fee;
uint _wbtc;

// poolId=0, idx=0
(_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) {
uint sett;
uint max;
(sett,fee,max) = settPeak.calcRedeem(poolId, bBtc);
Pool memory pool = pools[poolId];
if (bBtc > max) {
return (0,fee);
} 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);
lp = sett.sub(strategyFee).mul(pool.sett.getPricePerFullShare()).div(1e18);
fee = fee.add(strategyFee);
}
}
}

interface ICurveFi {
Expand All @@ -247,8 +401,15 @@ interface ICurveFi {

function add_liquidity(uint[4] calldata amounts, uint min_mint_amount) external;
function calc_token_amount(uint[4] calldata amounts, bool isDeposit) external view returns(uint);

function remove_liquidity_one_coin(uint _token_amount, int128 i, uint min_amount) external;
function calc_withdraw_one_coin(uint _token_amount, int128 i) external view returns(uint);
}

interface IStrategy {
function withdrawalFee() external view returns(uint);
}

interface IyvWbtc {
function deposit(uint) external;
interface IController {
function strategies(IERC20 token) external view returns(IStrategy);
}
18 changes: 18 additions & 0 deletions contracts/interfaces/IPeak.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ interface IBadgerSettPeak is IPeak {
external
view
returns(uint bBTC, uint fee);

function redeem(uint poolId, uint inAmount)
external
returns (uint outAmount);

function calcRedeem(uint poolId, uint bBtc)
external
view
returns(uint sett, uint fee, uint max);
}

interface IByvWbtcPeak is IPeak {
Expand All @@ -26,4 +35,13 @@ interface IByvWbtcPeak is IPeak {
external
view
returns(uint bBTC, uint fee);

function redeem(uint inAmount)
external
returns (uint outAmount);

function calcRedeem(uint bBtc)
external
view
returns(uint sett, uint fee, uint max);
}
4 changes: 4 additions & 0 deletions contracts/interfaces/ISett.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ interface ISett is IERC20 {

function getPricePerFullShare() external view returns (uint256);
function balance() external view returns (uint256);

// byvwbtc
function pricePerShare() external view returns (uint256);
function withdrawalFee() external view returns (uint256);
}
1 change: 1 addition & 0 deletions contracts/interfaces/IbyvWbtc.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IbyvWbtc is IERC20 {
function pricePerShare() external view returns (uint);
function deposit(bytes32[] calldata merkleProof) external;
function withdraw() external returns (uint);
}
3 changes: 3 additions & 0 deletions contracts/mocks/Sett.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ contract Sett is ERC20, ISett {
function balance() override external view returns (uint256) {
return token.balanceOf(address(this));
}

function pricePerShare() override external view returns (uint256) {}
function withdrawalFee() override external view returns (uint256) {}
}
2 changes: 2 additions & 0 deletions contracts/mocks/byvWbtc.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ contract byvWbtc is ERC20, IbyvWbtc {
}

function deposit(bytes32[] calldata merkleProof) override external {}

function withdraw() override external returns (uint) {}
}
6 changes: 4 additions & 2 deletions contracts/peaks/BadgerSettPeak.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ contract BadgerSettPeak is AccessControlDefended, IBadgerSettPeak {
* @return outAmount Amount of Sett LP token
*/
function redeem(uint poolId, uint inAmount)
override
external
defend
blockLocked
Expand Down Expand Up @@ -103,6 +104,7 @@ contract BadgerSettPeak is AccessControlDefended, IBadgerSettPeak {
* @return max Max amount of bBTC redeemable for chosen sett
*/
function calcRedeem(uint poolId, uint bBtc)
override
external
view
returns(uint sett, uint fee, uint max)
Expand Down Expand Up @@ -151,8 +153,8 @@ contract BadgerSettPeak is AccessControlDefended, IBadgerSettPeak {
{
return btc // is already scaled by 1e36
.mul(1e18)
.div(pool.sett.getPricePerFullShare())
.div(pool.swap.get_virtual_price());
.div(pool.swap.get_virtual_price())
.div(pool.sett.getPricePerFullShare());
}

/**
Expand Down
Loading