BPool.sol

Created Diff never expires
// 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);
}

}