BPool.sol

Created Diff never expires
136 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
740 lines
186 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
791 lines
// This program is free software: you can redistribute it and/or modify
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// (at your option) any later version.


// This program is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// GNU General Public License for more details.


// You should have received a copy of the GNU General Public License
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// along with this program. If not, see <http://www.gnu.org/licenses/>.


pragma solidity 0.5.12;
pragma solidity 0.5.12;


import "./BToken.sol";
import "./BToken.sol";
import "./BMath.sol";
import "./BMath.sol";


contract BPool is BBronze, BToken, BMath {
contract BPool is BBronze, BToken, BMath {


struct Record {
struct Record {
bool bound; // is token bound to pool
bool bound; // is token bound to pool
uint index; // private
uint index; // private
uint denorm; // denormalized weight
uint denorm; // denormalized weight
uint balance;
uint balance;
}
}


event LOG_SWAP(
event LOG_SWAP(
address indexed caller,
address indexed caller,
address indexed tokenIn,
address indexed tokenIn,
address indexed tokenOut,
address indexed tokenOut,
uint256 tokenAmountIn,
uint256 tokenAmountIn,
uint256 tokenAmountOut
uint256 tokenAmountOut,
uint256 reservesAmount
);
);


event LOG_JOIN(
event LOG_JOIN(
address indexed caller,
address indexed caller,
address indexed tokenIn,
address indexed tokenIn,
uint256 tokenAmountIn
uint256 tokenAmountIn,
uint256 reservesAmount
);
);


event LOG_EXIT(
event LOG_EXIT(
address indexed caller,
address indexed caller,
address indexed tokenOut,
address indexed tokenOut,
uint256 tokenAmountOut,
uint256 reservesAmount
);

event LOG_DRAIN_RESERVES(
address indexed caller,
address indexed tokenOut,
uint256 tokenAmountOut
uint256 tokenAmountOut
);
);


event LOG_ADD_RESERVES(
address indexed token,
uint256 reservesAmount
);

event LOG_CALL(
event LOG_CALL(
bytes4 indexed sig,
bytes4 indexed sig,
address indexed caller,
address indexed caller,
bytes data
bytes data
) anonymous;
) anonymous;


modifier _logs_() {
modifier _logs_() {
emit LOG_CALL(msg.sig, msg.sender, msg.data);
emit LOG_CALL(msg.sig, msg.sender, msg.data);
_;
_;
}
}


modifier _lock_() {
modifier _lock_() {
require(!_mutex, "ERR_REENTRY");
require(!_mutex);
_mutex = true;
_mutex = true;
_;
_;
_mutex = false;
_mutex = false;
}
}


modifier _viewlock_() {
modifier _viewlock_() {
require(!_mutex, "ERR_REENTRY");
require(!_mutex);
_;
_;
}
}


bool private _mutex;
bool private _mutex;


address private _factory; // BFactory address to push token exitFee to
address private _factory; // BFactory address to push token exitFee to
address private _controller; // has CONTROL role
address private _controller; // has CONTROL role
bool private _publicSwap; // true if PUBLIC can call SWAP functions
bool private _publicSwap; // true if PUBLIC can call SWAP functions


// `setSwapFee` and `finalize` require CONTROL
// `setSwapFee` and `finalize` require CONTROL
// `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`
// `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`
uint private _swapFee;
uint private _swapFee;
uint private _reservesRatio;
bool private _finalized;
bool private _finalized;


address[] private _tokens;
address[] private _tokens;
mapping(address=>Record) private _records;
mapping(address=>Record) private _records;
mapping(address=>uint) public totalReserves;

uint private _totalWeight;
uint private _totalWeight;


constructor() public {
constructor() public {
_controller = msg.sender;
_controller = msg.sender;
_factory = msg.sender;
_factory = msg.sender;
_swapFee = MIN_FEE;
_swapFee = MIN_FEE;
_reservesRatio = DEFAULT_RESERVES_RATIO;
_publicSwap = false;
_publicSwap = false;
_finalized = false;
_finalized = false;
}
}


function isPublicSwap()
function isPublicSwap()
external view
external view
returns (bool)
returns (bool)
{
{
return _publicSwap;
return _publicSwap;
}
}


function isFinalized()
function isFinalized()
external view
external view
returns (bool)
returns (bool)
{
{
return _finalized;
return _finalized;
}
}


function isBound(address t)
function isBound(address t)
external view
external view
returns (bool)
returns (bool)
{
{
return _records[t].bound;
return _records[t].bound;
}
}


function getNumTokens()
function getNumTokens()
external view
external view
returns (uint)
returns (uint)
{
{
return _tokens.length;
return _tokens.length;
}
}


function getCurrentTokens()
function getCurrentTokens()
external view _viewlock_
external view _viewlock_
returns (address[] memory tokens)
returns (address[] memory tokens)
{
{
return _tokens;
return _tokens;
}
}


function getFinalTokens()
function getFinalTokens()
external view
external view
_viewlock_
_viewlock_
returns (address[] memory tokens)
returns (address[] memory tokens)
{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized);
return _tokens;
return _tokens;
}
}


function getDenormalizedWeight(address token)
function getDenormalizedWeight(address token)
external view
external view
_viewlock_
_viewlock_
returns (uint)
returns (uint)
{
{


require(_records[token].bound, "ERR_NOT_BOUND");
require(_records[token].bound);
return _records[token].denorm;
return _records[token].denorm;
}
}


function getTotalDenormalizedWeight()
function getTotalDenormalizedWeight()
external view
external view
_viewlock_
_viewlock_
returns (uint)
returns (uint)
{
{
return _totalWeight;
return _totalWeight;
}
}


function getNormalizedWeight(address token)
function getNormalizedWeight(address token)
external view
external view
_viewlock_
_viewlock_
returns (uint)
returns (uint)
{
{


require(_records[token].bound, "ERR_NOT_BOUND");
require(_records[token].bound);
uint denorm = _records[token].denorm;
uint denorm = _records[token].denorm;
return bdiv(denorm, _totalWeight);
return bdiv(denorm, _totalWeight);
}
}


function getBalance(address token)
function getBalance(address token)
external view
external view
_viewlock_
_viewlock_
returns (uint)
returns (uint)
{
{


require(_records[token].bound, "ERR_NOT_BOUND");
require(_records[token].bound);
return _records[token].balance;
return _records[token].balance;
}
}


function getSwapFee()
function getSwapFee()
external view
external view
_viewlock_
_viewlock_
returns (uint)
returns (uint)
{
{
return _swapFee;
return _swapFee;
}
}


function getReservesRatio()
external view
_viewlock_
returns (uint)
{
return _reservesRatio;
}

function getController()
function getController()
external view
external view
_viewlock_
_viewlock_
returns (address)
returns (address)
{
{
return _controller;
return _controller;
}
}


function setSwapFee(uint swapFee)
function setSwapFee(uint swapFee)
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(!_finalized, "ERR_IS_FINALIZED");
require(!_finalized);
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
require(swapFee >= MIN_FEE, "ERR_MIN_FEE");
require(swapFee >= MIN_FEE);
require(swapFee <= MAX_FEE, "ERR_MAX_FEE");
require(swapFee <= MAX_FEE);
_swapFee = swapFee;
_swapFee = swapFee;
}
}



function setReservesRatio(uint reservesRatio)
external
_logs_
_lock_
{
require(!_finalized);
require(msg.sender == _controller);
require(reservesRatio <= BONE);
require(reservesRatio >= DEFAULT_RESERVES_RATIO);
_reservesRatio = reservesRatio;
}

function setController(address manager)
function setController(address manager)
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
_controller = manager;
_controller = manager;
}
}


function setPublicSwap(bool public_)
function setPublicSwap(bool public_)
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(!_finalized, "ERR_IS_FINALIZED");
require(!_finalized);
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
_publicSwap = public_;
_publicSwap = public_;
}
}


function finalize()
function finalize()
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
require(!_finalized, "ERR_IS_FINALIZED");
require(!_finalized);
require(_tokens.length >= MIN_BOUND_TOKENS, "ERR_MIN_TOKENS");
require(_tokens.length >= MIN_BOUND_TOKENS);


_finalized = true;
_finalized = true;
_publicSwap = true;
_publicSwap = true;


_mintPoolShare(INIT_POOL_SUPPLY);
_mintPoolShare(INIT_POOL_SUPPLY);
_pushPoolShare(msg.sender, INIT_POOL_SUPPLY);
_pushPoolShare(msg.sender, INIT_POOL_SUPPLY);
}
}




function bind(address token, uint balance, uint denorm)
function bind(address token, uint balance, uint denorm)
external
external
_logs_
_logs_
// _lock_ Bind does not lock because it jumps to `rebind`, which does
// _lock_ Bind does not lock because it jumps to `rebind`, which does
{
{
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
require(!_records[token].bound, "ERR_IS_BOUND");
require(!_records[token].bound);
require(!_finalized, "ERR_IS_FINALIZED");
require(!_finalized);


require(_tokens.length < MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");
require(_tokens.length < MAX_BOUND_TOKENS);


_records[token] = Record({
_records[token] = Record({
bound: true,
bound: true,
index: _tokens.length,
index: _tokens.length,
denorm: 0, // balance and denorm will be validated
denorm: 0, // balance and denorm will be validated
balance: 0 // and set by `rebind`
balance: 0 // and set by `rebind`
});
});
_tokens.push(token);
_tokens.push(token);
rebind(token, balance, denorm);
rebind(token, balance, denorm);
}
}


function rebind(address token, uint balance, uint denorm)
function rebind(address token, uint balance, uint denorm)
public
public
_logs_
_logs_
_lock_
_lock_
{
{


require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
require(_records[token].bound, "ERR_NOT_BOUND");
require(_records[token].bound);
require(!_finalized, "ERR_IS_FINALIZED");
require(!_finalized);


require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
require(denorm >= MIN_WEIGHT);
require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
require(denorm <= MAX_WEIGHT);
require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE");
require(balance >= MIN_BALANCE);


// Adjust the denorm and totalWeight
// Adjust the denorm and totalWeight
uint oldWeight = _records[token].denorm;
uint oldWeight = _records[token].denorm;
if (denorm > oldWeight) {
if (denorm > oldWeight) {
_totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));
_totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));
require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
require(_totalWeight <= MAX_TOTAL_WEIGHT);
} else if (denorm < oldWeight) {
} else if (denorm < oldWeight) {
_totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));
_totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));
}
}
_records[token].denorm = denorm;
_records[token].denorm = denorm;


// Adjust the balance record and actual token balance
// Adjust the balance record and actual token balance
uint oldBalance = _records[token].balance;
uint oldBalance = _records[token].balance;
_records[token].balance = balance;
_records[token].balance = balance;
if (balance > oldBalance) {
if (balance > oldBalance) {
_pullUnderlying(token, msg.sender, bsub(balance, oldBalance));
_pullUnderlying(token, msg.sender, bsub(balance, oldBalance));
} else if (balance < oldBalance) {
} else if (balance < oldBalance) {
// In this case liquidity is being withdrawn, so charge EXIT_FEE
// In this case liquidity is being withdrawn, so charge EXIT_FEE
uint tokenBalanceWithdrawn = bsub(oldBalance, balance);
uint tokenBalanceWithdrawn = bsub(oldBalance, balance);
uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE);
uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE);
_pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee));
_pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee));
_pushUnderlying(token, _factory, tokenExitFee);
_pushUnderlying(token, _factory, tokenExitFee);
}
}
}
}


function unbind(address token)
function unbind(address token)
external
external
_logs_
_logs_
_lock_
_lock_
{
{


require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(msg.sender == _controller);
require(_records[token].bound, "ERR_NOT_BOUND");
require(_records[token].bound);
require(!_finalized, "ERR_IS_FINALIZED");
require(!_finalized);


uint tokenBalance = _records[token].balance;
uint tokenBalance = _records[token].balance;
uint tokenExitFee = bmul(tokenBalance, EXIT_FEE);
uint tokenExitFee = bmul(tokenBalance, EXIT_FEE);


_totalWeight = bsub(_totalWeight, _records[token].denorm);
_totalWeight = bsub(_totalWeight, _records[token].denorm);


// Swap the token-to-unbind with the last token,
// Swap the token-to-unbind with the last token,
// then delete the last token
// then delete the last token
uint index = _records[token].index;
uint index = _records[token].index;
uint last = _tokens.length - 1;
uint last = _tokens.length - 1;
_tokens[index] = _tokens[last];
_tokens[index] = _tokens[last];
_records[_tokens[index]].index = index;
_records[_tokens[index]].index = index;
_tokens.pop();
_tokens.pop();
_records[token] = Record({
_records[token] = Record({
bound: false,
bound: false,
index: 0,
index: 0,
denorm: 0,
denorm: 0,
balance: 0
balance: 0
});
});


_pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee));
_pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee));
_pushUnderlying(token, _factory, tokenExitFee);
_pushUnderlying(token, _factory, tokenExitFee);
}
}


// Absorb any tokens that have been sent to this contract into the pool
// Absorb any tokens that have been sent to this contract into the pool
function gulp(address token)
function gulp(address token)
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(_records[token].bound, "ERR_NOT_BOUND");
require(_records[token].bound);
_records[token].balance = IERC20(token).balanceOf(address(this));
uint erc20Balance = IERC20(token).balanceOf(address(this));
uint reserves = totalReserves[token];
// `_records[token].balance` should be equaled to `bsub(erc20Balance, reserves)` unless there are extra
// tokens transferred to this pool without calling `joinxxx`.
require(_records[token].balance <= bsub(erc20Balance, reserves));
_records[token].balance = bsub(erc20Balance, reserves);
}

function seize(address token, uint amount)
external
_logs_
_lock_
{
require(msg.sender == _controller);
require(!_records[token].bound);

uint bal = IERC20(token).balanceOf(address(this));
require(amount <= bal);

_pushUnderlying(token, msg.sender, amount);
}
}


function getSpotPrice(address tokenIn, address tokenOut)
function getSpotPrice(address tokenIn, address tokenOut)
external view
external view
_viewlock_
_viewlock_
returns (uint spotPrice)
returns (uint spotPrice)
{
{
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
Record storage inRecord = _records[tokenIn];
Record storage inRecord = _records[tokenIn];
Record storage outRecord = _records[tokenOut];
Record storage outRecord = _records[tokenOut];
return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee);
return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee);
}
}


function getSpotPriceSansFee(address tokenIn, address tokenOut)
function getSpotPriceSansFee(address tokenIn, address tokenOut)
external view
external view
_viewlock_
_viewlock_
returns (uint spotPrice)
returns (uint spotPrice)
{
{
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
Record storage inRecord = _records[tokenIn];
Record storage inRecord = _records[tokenIn];
Record storage outRecord = _records[tokenOut];
Record storage outRecord = _records[tokenOut];
return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, 0);
return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, 0);
}
}


function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn)
function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn)
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized, "ERR_NOT_FINALIZED");


uint poolTotal = totalSupply();
uint poolTotal = totalSupply();
uint ratio = bdiv(poolAmountOut, poolTotal);
uint ratio = bdiv(poolAmountOut, poolTotal);
require(ratio != 0, "ERR_MATH_APPROX");
require(ratio != 0, "ERR_MATH_APPROX");


for (uint i = 0; i < _tokens.length; i++) {
for (uint i = 0; i < _tokens.length; i++) {
address t = _tokens[i];
address t = _tokens[i];
uint bal = _records[t].balance;
uint bal = _records[t].balance;
uint tokenAmountIn = bmul(ratio, bal);
uint tokenAmountIn = bmul(ratio, bal);
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
_records[t].balance = badd(_records[t].balance, tokenAmountIn);
_records[t].balance = badd(_records[t].balance, tokenAmountIn);
emit LOG_JOIN(msg.sender, t, tokenAmountIn);
emit LOG_JOIN(msg.sender, t, tokenAmountIn, 0);
_pullUnderlying(t, msg.sender, tokenAmountIn);
_pullUnderlying(t, msg.sender, tokenAmountIn);
}
}
_mintPoolShare(poolAmountOut);
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
}
}


function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut)
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut)
external
external
_logs_
_logs_
_lock_
_lock_
{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized, "ERR_NOT_FINALIZED");


uint poolTotal = totalSupply();
uint poolTotal = totalSupply();
uint exitFee = bmul(poolAmountIn, EXIT_FEE);
uint exitFee = bmul(poolAmountIn, EXIT_FEE);
uint pAiAfterExitFee = bsub(poolAmountIn, exitFee);
uint pAiAfterExitFee = bsub(poolAmountIn, exitFee);
uint ratio = bdiv(pAiAfterExitFee, poolTotal);
uint ratio = bdiv(pAiAfterExitFee, poolTotal);
require(ratio != 0, "ERR_MATH_APPROX");
require(ratio != 0, "ERR_MATH_APPROX");


_pullPoolShare(msg.sender, poolAmountIn);
_pullPoolShare(msg.sender, poolAmountIn);
_pushPoolShare(_factory, exitFee);
_pushPoolShare(_factory, exitFee);
_burnPoolShare(pAiAfterExitFee);
_burnPoolShare(pAiAfterExitFee);


for (uint i = 0; i < _tokens.length; i++) {
for (uint i = 0; i < _tokens.length; i++) {
address t = _tokens[i];
address t = _tokens[i];
uint bal = _records[t].balance;
uint bal = _records[t].balance;
uint tokenAmountOut = bmul(ratio, bal);
uint tokenAmountOut = bmul(ratio, bal);
require(tokenAmountOut != 0, "ERR_MATH_APPROX");
require(tokenAmountOut != 0, "ERR_MATH_APPROX");
require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");
require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");
_records[t].balance = bsub(_records[t].balance, tokenAmountOut);
_records[t].balance = bsub(_records[t].balance, tokenAmountOut);
emit LOG_EXIT(msg.sender, t, tokenAmountOut);
emit LOG_EXIT(msg.sender, t, tokenAmountOut, 0);
_pushUnderlying(t, msg.sender, tokenAmountOut);
_pushUnderlying(t, msg.sender, tokenAmountOut);
}
}


}
}




function swapExactAmountIn(
function swapExactAmountIn(
address tokenIn,
address tokenIn,
uint tokenAmountIn,
uint tokenAmountIn,
address tokenOut,
address tokenOut,
uint minAmountOut,
uint minAmountOut,
uint maxPrice
uint maxPrice
)
)
external
external
_logs_
_logs_
_lock_
_lock_
returns (uint tokenAmountOut, uint spotPriceAfter)
returns (uint tokenAmountOut, uint spotPriceAfter)
{
{


require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_publicSwap, "ERR_SWAP_NOT_PUBLIC");
require(_publicSwap, "ERR_SWAP_NOT_PUBLIC");


Record storage inRecord = _records[address(tokenIn)];
Record storage inRecord = _records[address(tokenIn)];
Record storage outRecord = _records[address(tokenOut)];
Record storage outRecord = _records[address(tokenOut)];


require(tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO");
require(tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO");


uint spotPriceBefore = calcSpotPrice(
uint spotPriceBefore = calcSpotPrice(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_swapFee
_swapFee
);
);
require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");
require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");


tokenAmountOut = calcOutGivenIn(
uint tokenInFee;
inRecord.balance,
(tokenAmountOut, tokenInFee) = calcOutGivenIn(
inRecord.denorm,
inRecord.balance,
outRecord.balance,
inRecord.denorm,
outRecord.denorm,
outRecord.balance,
tokenAmountIn,
outRecord.denorm,
_swapFee
tokenAmountIn,
);
_swapFee
);
require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");
require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");


inRecord.balance = badd(inRecord.balance, tokenAmountIn);
uint reserves = calcReservesFromFee(tokenInFee, _reservesRatio);

// Subtract `reserves`.
inRecord.balance = bsub(badd(inRecord.balance, tokenAmountIn), reserves);
outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
outRecord.balance = bsub(outRecord.balance, tokenAmountOut);


spotPriceAfter = calcSpotPrice(
spotPriceAfter = calcSpotPrice(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_swapFee
_swapFee
);
);
require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX");
require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX");


emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);
emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut, reserves);

totalReserves[address(tokenIn)] = badd(totalReserves[address(tokenIn)], reserves);
emit LOG_ADD_RESERVES(address(tokenIn), reserves);


_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);


return (tokenAmountOut, spotPriceAfter);
return (tokenAmountOut, spotPriceAfter);
}
}


function swapExactAmountOut(
function swapExactAmountOut(
address tokenIn,
address tokenIn,
uint maxAmountIn,
uint maxAmountIn,
address tokenOut,
address tokenOut,
uint tokenAmountOut,
uint tokenAmountOut,
uint maxPrice
uint maxPrice
)
)
external
external
_logs_
_logs_
_lock_
_lock_
returns (uint tokenAmountIn, uint spotPriceAfter)
returns (uint tokenAmountIn, uint spotPriceAfter)
{
{
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_publicSwap, "ERR_SWAP_NOT_PUBLIC");
require(_publicSwap, "ERR_SWAP_NOT_PUBLIC");


Record storage inRecord = _records[address(tokenIn)];
Record storage inRecord = _records[address(tokenIn)];
Record storage outRecord = _records[address(tokenOut)];
Record storage outRecord = _records[address(tokenOut)];


require(tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO");
require(tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO");


uint spotPriceBefore = calcSpotPrice(
uint spotPriceBefore = calcSpotPrice(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_swapFee
_swapFee
);
);
require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");
require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");


tokenAmountIn = calcInGivenOut(
uint tokenInFee;
inRecord.balance,
(tokenAmountIn, tokenInFee) = calcInGivenOut(
inRecord.denorm,
inRecord.balance,
outRecord.balance,
inRecord.denorm,
outRecord.denorm,
outRecord.balance,
tokenAmountOut,
outRecord.denorm,
_swapFee
tokenAmountOut,
);
_swapFee
);
require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");
require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");


inRecord.balance = badd(inRecord.balance, tokenAmountIn);
uint reserves = calcReservesFromFee(
tokenInFee,
_reservesRatio
);

// Subtract `reserves` which is reserved for admin.
inRecord.balance = bsub(badd(inRecord.balance, tokenAmountIn), reserves);
outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
outRecord.balance = bsub(outRecord.balance, tokenAmountOut);


spotPriceAfter = calcSpotPrice(
spotPriceAfter = calcSpotPrice(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_swapFee
_swapFee
);
);
require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX");
require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX");


emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);
emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut, reserves);

totalReserves[address(tokenIn)] = badd(totalReserves[address(tokenIn)], reserves);
emit LOG_ADD_RESERVES(address(tokenIn), reserves);


_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);


return (tokenAmountIn, spotPriceAfter);
return (tokenAmountIn, spotPriceAfter);
}
}




function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut)
function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut)
external
external
_logs_
_logs_
_lock_
_lock_
returns (uint poolAmountOut)
returns (uint poolAmountOut)


{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized, "ERR_NOT_FINALIZED");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO");
require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO");


Record storage inRecord = _records[tokenIn];
Record storage inRecord = _records[tokenIn];


poolAmountOut = calcPoolOutGivenSingleIn(
uint reserves;
(poolAmountOut, reserves) = calcPoolOutGivenSingleIn(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
_totalSupply,
_totalSupply,
_totalWeight,
_totalWeight,
tokenAmountIn,
tokenAmountIn,
_swapFee
_swapFee,
_reservesRatio
);
);


require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");
require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");


inRecord.balance = badd(inRecord.balance, tokenAmountIn);
inRecord.balance = bsub(badd(inRecord.balance, tokenAmountIn), reserves);


emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn, reserves);

totalReserves[address(tokenIn)] = badd(totalReserves[address(tokenIn)], reserves);
emit LOG_ADD_RESERVES(address(tokenIn), reserves);


_mintPoolShare(poolAmountOut);
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);


return poolAmountOut;
return poolAmountOut;
}
}


function joinswapPoolAmountOut(address tokenIn, uint poolAmountOut, uint maxAmountIn)
function joinswapPoolAmountOut(address tokenIn, uint poolAmountOut, uint maxAmountIn)
external
external
_logs_
_logs_
_lock_
_lock_
returns (uint tokenAmountIn)
returns (uint tokenAmountIn)
{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized, "ERR_NOT_FINALIZED");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");
require(_records[tokenIn].bound, "ERR_NOT_BOUND");


Record storage inRecord = _records[tokenIn];
Record storage inRecord = _records[tokenIn];


tokenAmountIn = calcSingleInGivenPoolOut(
tokenAmountIn = calcSingleInGivenPoolOut(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
_totalSupply,
_totalSupply,
_totalWeight,
_totalWeight,
poolAmountOut,
poolAmountOut,
_swapFee
_swapFee
);
);


require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");
require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");

require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO");
require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO");


inRecord.balance = badd(inRecord.balance, tokenAmountIn);
uint tokenAmountInZeroFee = calcSingleInGivenPoolOut(
inRecord.balance,
inRecord.denorm,
_totalSupply,
_totalWeight,
poolAmountOut,
0
);
uint reserves = calcReserves(
tokenAmountIn,
tokenAmountInZeroFee,
_reservesRatio
);


emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
inRecord.balance = bsub(badd(inRecord.balance, tokenAmountIn), reserves);

emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn, reserves);

totalReserves[address(tokenIn)] = badd(totalReserves[address(tokenIn)], reserves);
emit LOG_ADD_RESERVES(address(tokenIn), reserves);


_mintPoolShare(poolAmountOut);
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);


return tokenAmountIn;
return tokenAmountIn;
}
}


function exitswapPoolAmountIn(address tokenOut, uint poolAmountIn, uint minAmountOut)
function exitswapPoolAmountIn(address tokenOut, uint poolAmountIn, uint minAmountOut)
external
external
_logs_
_logs_
_lock_
_lock_
returns (uint tokenAmountOut)
returns (uint tokenAmountOut)
{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized, "ERR_NOT_FINALIZED");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");


Record storage outRecord = _records[tokenOut];
Record storage outRecord = _records[tokenOut];


tokenAmountOut = calcSingleOutGivenPoolIn(
tokenAmountOut = calcSingleOutGivenPoolIn(
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_totalSupply,
_totalSupply,
_totalWeight,
_totalWeight,
poolAmountIn,
poolAmountIn,
_swapFee
_swapFee
);
);


require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");
require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");

require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO");
require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO");


outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
uint tokenAmountOutZeroFee = calcSingleOutGivenPoolIn(
outRecord.balance,
outRecord.denorm,
_totalSupply,
_totalWeight,
poolAmountIn,
0
);
uint reserves = calcReserves(
tokenAmountOutZeroFee,
tokenAmountOut,
_reservesRatio
);

outRecord.balance = bsub(bsub(outRecord.balance, tokenAmountOut), reserves);


uint exitFee = bmul(poolAmountIn, EXIT_FEE);
uint exitFee = bmul(poolAmountIn, EXIT_FEE);


emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut, reserves);

totalReserves[address(tokenOut)] = badd(totalReserves[address(tokenOut)], reserves);
emit LOG_ADD_RESERVES(address(tokenOut), reserves);


_pullPoolShare(msg.sender, poolAmountIn);
_pullPoolShare(msg.sender, poolAmountIn);
_burnPoolShare(bsub(poolAmountIn, exitFee));
_burnPoolShare(bsub(poolAmountIn, exitFee));
_pushPoolShare(_factory, exitFee);
_pushPoolShare(_factory, exitFee);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);


return tokenAmountOut;
return tokenAmountOut;
}
}


function exitswapExternAmountOut(address tokenOut, uint tokenAmountOut, uint maxPoolAmountIn)
function exitswapExternAmountOut(address tokenOut, uint tokenAmountOut, uint maxPoolAmountIn)
external
external
_logs_
_logs_
_lock_
_lock_
returns (uint poolAmountIn)
returns (uint poolAmountIn)
{
{
require(_finalized, "ERR_NOT_FINALIZED");
require(_finalized, "ERR_NOT_FINALIZED");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(_records[tokenOut].bound, "ERR_NOT_BOUND");
require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO");
require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO");


Record storage outRecord = _records[tokenOut];
Record storage outRecord = _records[tokenOut];


poolAmountIn = calcPoolInGivenSingleOut(
uint reserves;
(poolAmountIn, reserves) = calcPoolInGivenSingleOut(
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_totalSupply,
_totalSupply,
_totalWeight,
_totalWeight,
tokenAmountOut,
tokenAmountOut,
_swapFee
);

require(poolAmountIn != 0, "ERR_MATH_APPROX");
require(poolAmountIn <= maxPoolAmountIn, "ERR_LIMIT_IN");

outRecord.balance = bsub(outRecord.balance, tokenAmountOut);

uint exitFee = bmul(poolAmountIn, EXIT_FEE);

emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);

_pullPoolShare(msg.sender, poolAmountIn);
_burnPoolShare(bsub(poolAmountIn, exitFee));
_pushPoolShare(_factory, exitFee);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);

return poolAmountIn;
}


// ==
// 'Underlying' token-manipulation functions make external calls but are NOT locked
// You must `_lock_` or otherwise ensure reentry-safety

function _pullUnderlying(address erc20, address from, uint amount)
internal
{
bool xfer = IERC20(erc20).transferFrom(from, address(this), amount);
require(xfer, "ERR_ERC20_FALSE");
}

function _pushUnderlying(address erc20, address to, uint amount)
internal
{
bool xfer = IERC20(erc20).transfer(to, amount);
require(xfer, "ERR_ERC20_FALSE");
}

function _pullPoolShare(address from, uint amount)
internal
{
_pull(from, amount);
}

function _pushPoolShare(address to, uint amount)
internal
{
_push(to, amount);
}

function _mintPoolShare(uint amount)
internal
{
_mint(amount);
}

function _burnPoolShare(uint amount)
internal
{
_burn(amount);
}

}