Diff
checker
Text
Text
Images
Documents
Excel
Folders
Legal
Enterprise
Desktop
Pricing
Sign in
Download Diffchecker Desktop
Compare text
Find the difference between two text files
Tools
History
Real-time editor
Hide unchanged lines
Disable line wrap
Layout
Split
Unified
Diff precision
Smart
Word
Char
Syntax highlighting
Choose syntax
Ignore
Transform text
Go to first change
Edit input
Diffchecker Desktop
The most secure way to run Diffchecker. Get the Diffchecker Desktop app: your diffs never leave your computer!
Get Desktop
BPool.sol
Created
6 years ago
Diff never expires
Clear
Export
Share
Explain
111 removals
Lines
Total
Removed
Characters
Total
Removed
To continue using this feature, upgrade to
Diff
checker
Pro
View Pricing
740 lines
Copy
167 additions
Lines
Total
Added
Characters
Total
Added
To continue using this feature, upgrade to
Diff
checker
Pro
View Pricing
791 lines
Copy
// 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,
Copy
Copied
Copy
Copied
uint256 tokenAmountOut
uint256 tokenAmountOut
,
uint256 reservesAmount
);
);
event LOG_JOIN(
event LOG_JOIN(
address indexed caller,
address indexed caller,
address indexed tokenIn,
address indexed tokenIn,
Copy
Copied
Copy
Copied
uint256 tokenAmountIn
uint256 tokenAmountIn
,
uint256 reservesAmount
);
);
event LOG_EXIT(
event LOG_EXIT(
address indexed caller,
address indexed caller,
address indexed tokenOut,
address indexed tokenOut,
Copy
Copied
Copy
Copied
uint256 tokenAmountOut,
uint256 reservesAmount
);
event LOG_DRAIN_RESERVES(
address indexed caller,
address indexed tokenOut,
uint256 tokenAmountOut
uint256 tokenAmountOut
);
);
Copy
Copied
Copy
Copied
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_() {
Copy
Copied
Copy
Copied
require(!_mutex
, "ERR_REENTRY"
);
require(!_mutex
);
_mutex = true;
_mutex = true;
_;
_;
_mutex = false;
_mutex = false;
}
}
modifier _viewlock_() {
modifier _viewlock_() {
Copy
Copied
Copy
Copied
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;
Copy
Copied
Copy
Copied
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;
Copy
Copied
Copy
Copied
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;
Copy
Copied
Copy
Copied
_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
Copy
Copied
Copy
Copied
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)
{
{
Copy
Copied
Copy
Copied
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)
{
{
Copy
Copied
Copy
Copied
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)
{
{
Copy
Copied
Copy
Copied
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)
{
{
Copy
Copied
Copy
Copied
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;
}
}
Copy
Copied
Copy
Copied
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_
Copy
Copied
Copy
Copied
{
{
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;
}
}
Copy
Copied
Copy
Copied
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_
{
{
Copy
Copied
Copy
Copied
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_
{
{
Copy
Copied
Copy
Copied
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_
{
{
Copy
Copied
Copy
Copied
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
{
{
Copy
Copied
Copy
Copied
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
);
Copy
Copied
Copy
Copied
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_
{
{
Copy
Copied
Copy
Copied
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
);
Copy
Copied
Copy
Copied
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));
Copy
Copied
Copy
Copied
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));
Copy
Copied
Copy
Copied
}
}
_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_
{
{
Copy
Copied
Copy
Copied
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_
{
{
Copy
Copied
Copy
Copied
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);
Copy
Copied
Copy
Copied
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);
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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
);
);
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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_
Copy
Copied
Copy
Copied
_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");
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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)
Copy
Copied
Copy
Copied
{
{
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];
Copy
Copied
Copy
Copied
poolAmountOut
= calcPoolOutGivenSingleIn(
uint reserves;
(
poolAmountOut
, reserves)
= calcPoolOutGivenSingleIn(
inRecord.balance,
inRecord.balance,
inRecord.denorm,
inRecord.denorm,
_totalSupply,
_totalSupply,
_totalWeight,
_totalWeight,
tokenAmountIn,
tokenAmountIn,
Copy
Copied
Copy
Copied
_swapFee
_swapFee
,
_reservesRatio
);
);
require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");
require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");
Copy
Copied
Copy
Copied
inRecord.balance =
badd(inRecord.balance, tokenAmountIn)
;
inRecord.balance =
bsub(
badd(inRecord.balance, tokenAmountIn)
, reserves)
;
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
inRecord.balance
= badd(
inRecord.
balance, tokenAmountIn);
uint tokenAmountInZeroFee = calcSingleInGivenPoolOut(
inRecord.balance
,
inRecord.
denorm,
_totalSupply,
_totalWeight,
poolAmountOut,
0
);
uint reserves = calcReserves(
tokenAmountIn,
tokenAmountInZeroFee,
_reservesRatio
);
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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");
Copy
Copied
Copy
Copied
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);
Copy
Copied
Copy
Copied
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];
Copy
Copied
Copy
Copied
poolAmountIn
= calcPoolInGivenSingleOut(
uint reserves;
(
poolAmountIn
, reserves)
= calcPoolInGivenSingleOut(
outRecord.balance,
outRecord.balance,
outRecord.denorm,
outRecord.denorm,
_totalSupply,
_totalSupply,
_totalWeight,
_totalWeight,
tokenAmountOut,
tokenAmountOut,
Copy
Copied
Copy
Copied
_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);
}
}
Saved diffs
Original text
Open file
// 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 // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity 0.5.12; import "./BToken.sol"; import "./BMath.sol"; contract BPool is BBronze, BToken, BMath { struct Record { bool bound; // is token bound to pool uint index; // private uint denorm; // denormalized weight uint balance; } event LOG_SWAP( address indexed caller, address indexed tokenIn, address indexed tokenOut, uint256 tokenAmountIn, uint256 tokenAmountOut ); event LOG_JOIN( address indexed caller, address indexed tokenIn, uint256 tokenAmountIn ); event LOG_EXIT( address indexed caller, address indexed tokenOut, uint256 tokenAmountOut ); event LOG_CALL( bytes4 indexed sig, address indexed caller, bytes data ) anonymous; modifier _logs_() { emit LOG_CALL(msg.sig, msg.sender, msg.data); _; } modifier _lock_() { require(!_mutex, "ERR_REENTRY"); _mutex = true; _; _mutex = false; } modifier _viewlock_() { require(!_mutex, "ERR_REENTRY"); _; } bool private _mutex; address private _factory; // BFactory address to push token exitFee to address private _controller; // has CONTROL role bool private _publicSwap; // true if PUBLIC can call SWAP functions // `setSwapFee` and `finalize` require CONTROL // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN` uint private _swapFee; bool private _finalized; address[] private _tokens; mapping(address=>Record) private _records; uint private _totalWeight; constructor() public { _controller = msg.sender; _factory = msg.sender; _swapFee = MIN_FEE; _publicSwap = false; _finalized = false; } function isPublicSwap() external view returns (bool) { return _publicSwap; } function isFinalized() external view returns (bool) { return _finalized; } function isBound(address t) external view returns (bool) { return _records[t].bound; } function getNumTokens() external view returns (uint) { return _tokens.length; } function getCurrentTokens() external view _viewlock_ returns (address[] memory tokens) { return _tokens; } function getFinalTokens() external view _viewlock_ returns (address[] memory tokens) { require(_finalized, "ERR_NOT_FINALIZED"); return _tokens; } function getDenormalizedWeight(address token) external view _viewlock_ returns (uint) { require(_records[token].bound, "ERR_NOT_BOUND"); return _records[token].denorm; } function getTotalDenormalizedWeight() external view _viewlock_ returns (uint) { return _totalWeight; } function getNormalizedWeight(address token) external view _viewlock_ returns (uint) { require(_records[token].bound, "ERR_NOT_BOUND"); uint denorm = _records[token].denorm; return bdiv(denorm, _totalWeight); } function getBalance(address token) external view _viewlock_ returns (uint) { require(_records[token].bound, "ERR_NOT_BOUND"); return _records[token].balance; } function getSwapFee() external view _viewlock_ returns (uint) { return _swapFee; } function getController() external view _viewlock_ returns (address) { return _controller; } function setSwapFee(uint swapFee) external _logs_ _lock_ { require(!_finalized, "ERR_IS_FINALIZED"); require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); require(swapFee >= MIN_FEE, "ERR_MIN_FEE"); require(swapFee <= MAX_FEE, "ERR_MAX_FEE"); _swapFee = swapFee; } function setController(address manager) external _logs_ _lock_ { require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); _controller = manager; } function setPublicSwap(bool public_) external _logs_ _lock_ { require(!_finalized, "ERR_IS_FINALIZED"); require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); _publicSwap = public_; } function finalize() external _logs_ _lock_ { require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); require(!_finalized, "ERR_IS_FINALIZED"); require(_tokens.length >= MIN_BOUND_TOKENS, "ERR_MIN_TOKENS"); _finalized = true; _publicSwap = true; _mintPoolShare(INIT_POOL_SUPPLY); _pushPoolShare(msg.sender, INIT_POOL_SUPPLY); } function bind(address token, uint balance, uint denorm) external _logs_ // _lock_ Bind does not lock because it jumps to `rebind`, which does { require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); require(!_records[token].bound, "ERR_IS_BOUND"); require(!_finalized, "ERR_IS_FINALIZED"); require(_tokens.length < MAX_BOUND_TOKENS, "ERR_MAX_TOKENS"); _records[token] = Record({ bound: true, index: _tokens.length, denorm: 0, // balance and denorm will be validated balance: 0 // and set by `rebind` }); _tokens.push(token); rebind(token, balance, denorm); } function rebind(address token, uint balance, uint denorm) public _logs_ _lock_ { require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); require(_records[token].bound, "ERR_NOT_BOUND"); require(!_finalized, "ERR_IS_FINALIZED"); require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT"); require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT"); require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE"); // Adjust the denorm and totalWeight uint oldWeight = _records[token].denorm; if (denorm > oldWeight) { _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight)); require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT"); } else if (denorm < oldWeight) { _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm)); } _records[token].denorm = denorm; // Adjust the balance record and actual token balance uint oldBalance = _records[token].balance; _records[token].balance = balance; if (balance > oldBalance) { _pullUnderlying(token, msg.sender, bsub(balance, oldBalance)); } else if (balance < oldBalance) { // In this case liquidity is being withdrawn, so charge EXIT_FEE uint tokenBalanceWithdrawn = bsub(oldBalance, balance); uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE); _pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee)); _pushUnderlying(token, _factory, tokenExitFee); } } function unbind(address token) external _logs_ _lock_ { require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); require(_records[token].bound, "ERR_NOT_BOUND"); require(!_finalized, "ERR_IS_FINALIZED"); uint tokenBalance = _records[token].balance; uint tokenExitFee = bmul(tokenBalance, EXIT_FEE); _totalWeight = bsub(_totalWeight, _records[token].denorm); // Swap the token-to-unbind with the last token, // then delete the last token uint index = _records[token].index; uint last = _tokens.length - 1; _tokens[index] = _tokens[last]; _records[_tokens[index]].index = index; _tokens.pop(); _records[token] = Record({ bound: false, index: 0, denorm: 0, balance: 0 }); _pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee)); _pushUnderlying(token, _factory, tokenExitFee); } // Absorb any tokens that have been sent to this contract into the pool function gulp(address token) external _logs_ _lock_ { require(_records[token].bound, "ERR_NOT_BOUND"); _records[token].balance = IERC20(token).balanceOf(address(this)); } function getSpotPrice(address tokenIn, address tokenOut) external view _viewlock_ returns (uint spotPrice) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); Record storage inRecord = _records[tokenIn]; Record storage outRecord = _records[tokenOut]; return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee); } function getSpotPriceSansFee(address tokenIn, address tokenOut) external view _viewlock_ returns (uint spotPrice) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); Record storage inRecord = _records[tokenIn]; Record storage outRecord = _records[tokenOut]; return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, 0); } function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external _logs_ _lock_ { require(_finalized, "ERR_NOT_FINALIZED"); uint poolTotal = totalSupply(); uint ratio = bdiv(poolAmountOut, poolTotal); require(ratio != 0, "ERR_MATH_APPROX"); for (uint i = 0; i < _tokens.length; i++) { address t = _tokens[i]; uint bal = _records[t].balance; uint tokenAmountIn = bmul(ratio, bal); require(tokenAmountIn != 0, "ERR_MATH_APPROX"); require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN"); _records[t].balance = badd(_records[t].balance, tokenAmountIn); emit LOG_JOIN(msg.sender, t, tokenAmountIn); _pullUnderlying(t, msg.sender, tokenAmountIn); } _mintPoolShare(poolAmountOut); _pushPoolShare(msg.sender, poolAmountOut); } function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external _logs_ _lock_ { require(_finalized, "ERR_NOT_FINALIZED"); uint poolTotal = totalSupply(); uint exitFee = bmul(poolAmountIn, EXIT_FEE); uint pAiAfterExitFee = bsub(poolAmountIn, exitFee); uint ratio = bdiv(pAiAfterExitFee, poolTotal); require(ratio != 0, "ERR_MATH_APPROX"); _pullPoolShare(msg.sender, poolAmountIn); _pushPoolShare(_factory, exitFee); _burnPoolShare(pAiAfterExitFee); for (uint i = 0; i < _tokens.length; i++) { address t = _tokens[i]; uint bal = _records[t].balance; uint tokenAmountOut = bmul(ratio, bal); require(tokenAmountOut != 0, "ERR_MATH_APPROX"); require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT"); _records[t].balance = bsub(_records[t].balance, tokenAmountOut); emit LOG_EXIT(msg.sender, t, tokenAmountOut); _pushUnderlying(t, msg.sender, tokenAmountOut); } } function swapExactAmountIn( address tokenIn, uint tokenAmountIn, address tokenOut, uint minAmountOut, uint maxPrice ) external _logs_ _lock_ returns (uint tokenAmountOut, uint spotPriceAfter) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); Record storage inRecord = _records[address(tokenIn)]; Record storage outRecord = _records[address(tokenOut)]; require(tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); uint spotPriceBefore = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE"); tokenAmountOut = calcOutGivenIn( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, tokenAmountIn, _swapFee ); require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT"); inRecord.balance = badd(inRecord.balance, tokenAmountIn); outRecord.balance = bsub(outRecord.balance, tokenAmountOut); spotPriceAfter = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX"); require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE"); require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX"); emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut); _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); return (tokenAmountOut, spotPriceAfter); } function swapExactAmountOut( address tokenIn, uint maxAmountIn, address tokenOut, uint tokenAmountOut, uint maxPrice ) external _logs_ _lock_ returns (uint tokenAmountIn, uint spotPriceAfter) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); Record storage inRecord = _records[address(tokenIn)]; Record storage outRecord = _records[address(tokenOut)]; require(tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); uint spotPriceBefore = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE"); tokenAmountIn = calcInGivenOut( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, tokenAmountOut, _swapFee ); require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN"); inRecord.balance = badd(inRecord.balance, tokenAmountIn); outRecord.balance = bsub(outRecord.balance, tokenAmountOut); spotPriceAfter = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX"); require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE"); require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX"); emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut); _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); return (tokenAmountIn, spotPriceAfter); } function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) external _logs_ _lock_ returns (uint poolAmountOut) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); Record storage inRecord = _records[tokenIn]; poolAmountOut = calcPoolOutGivenSingleIn( inRecord.balance, inRecord.denorm, _totalSupply, _totalWeight, tokenAmountIn, _swapFee ); require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT"); inRecord.balance = badd(inRecord.balance, tokenAmountIn); emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn); _mintPoolShare(poolAmountOut); _pushPoolShare(msg.sender, poolAmountOut); _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); return poolAmountOut; } function joinswapPoolAmountOut(address tokenIn, uint poolAmountOut, uint maxAmountIn) external _logs_ _lock_ returns (uint tokenAmountIn) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenIn].bound, "ERR_NOT_BOUND"); Record storage inRecord = _records[tokenIn]; tokenAmountIn = calcSingleInGivenPoolOut( inRecord.balance, inRecord.denorm, _totalSupply, _totalWeight, poolAmountOut, _swapFee ); require(tokenAmountIn != 0, "ERR_MATH_APPROX"); require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN"); require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); inRecord.balance = badd(inRecord.balance, tokenAmountIn); emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn); _mintPoolShare(poolAmountOut); _pushPoolShare(msg.sender, poolAmountOut); _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); return tokenAmountIn; } function exitswapPoolAmountIn(address tokenOut, uint poolAmountIn, uint minAmountOut) external _logs_ _lock_ returns (uint tokenAmountOut) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); Record storage outRecord = _records[tokenOut]; tokenAmountOut = calcSingleOutGivenPoolIn( outRecord.balance, outRecord.denorm, _totalSupply, _totalWeight, poolAmountIn, _swapFee ); require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT"); require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); 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 tokenAmountOut; } function exitswapExternAmountOut(address tokenOut, uint tokenAmountOut, uint maxPoolAmountIn) external _logs_ _lock_ returns (uint poolAmountIn) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); Record storage outRecord = _records[tokenOut]; poolAmountIn = calcPoolInGivenSingleOut( outRecord.balance, outRecord.denorm, _totalSupply, _totalWeight, 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); } }
Changed text
Open file
// 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 // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity 0.5.12; import "./BToken.sol"; import "./BMath.sol"; contract BPool is BBronze, BToken, BMath { struct Record { bool bound; // is token bound to pool uint index; // private uint denorm; // denormalized weight uint balance; } event LOG_SWAP( address indexed caller, address indexed tokenIn, address indexed tokenOut, uint256 tokenAmountIn, uint256 tokenAmountOut, uint256 reservesAmount ); event LOG_JOIN( address indexed caller, address indexed tokenIn, uint256 tokenAmountIn, uint256 reservesAmount ); event LOG_EXIT( address indexed caller, address indexed tokenOut, uint256 tokenAmountOut, uint256 reservesAmount ); event LOG_DRAIN_RESERVES( address indexed caller, address indexed tokenOut, uint256 tokenAmountOut ); event LOG_ADD_RESERVES( address indexed token, uint256 reservesAmount ); event LOG_CALL( bytes4 indexed sig, address indexed caller, bytes data ) anonymous; modifier _logs_() { emit LOG_CALL(msg.sig, msg.sender, msg.data); _; } modifier _lock_() { require(!_mutex); _mutex = true; _; _mutex = false; } modifier _viewlock_() { require(!_mutex); _; } bool private _mutex; address private _factory; // BFactory address to push token exitFee to address private _controller; // has CONTROL role bool private _publicSwap; // true if PUBLIC can call SWAP functions // `setSwapFee` and `finalize` require CONTROL // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN` uint private _swapFee; uint private _reservesRatio; bool private _finalized; address[] private _tokens; mapping(address=>Record) private _records; mapping(address=>uint) public totalReserves; uint private _totalWeight; constructor() public { _controller = msg.sender; _factory = msg.sender; _swapFee = MIN_FEE; _reservesRatio = DEFAULT_RESERVES_RATIO; _publicSwap = false; _finalized = false; } function isPublicSwap() external view returns (bool) { return _publicSwap; } function isFinalized() external view returns (bool) { return _finalized; } function isBound(address t) external view returns (bool) { return _records[t].bound; } function getNumTokens() external view returns (uint) { return _tokens.length; } function getCurrentTokens() external view _viewlock_ returns (address[] memory tokens) { return _tokens; } function getFinalTokens() external view _viewlock_ returns (address[] memory tokens) { require(_finalized); return _tokens; } function getDenormalizedWeight(address token) external view _viewlock_ returns (uint) { require(_records[token].bound); return _records[token].denorm; } function getTotalDenormalizedWeight() external view _viewlock_ returns (uint) { return _totalWeight; } function getNormalizedWeight(address token) external view _viewlock_ returns (uint) { require(_records[token].bound); uint denorm = _records[token].denorm; return bdiv(denorm, _totalWeight); } function getBalance(address token) external view _viewlock_ returns (uint) { require(_records[token].bound); return _records[token].balance; } function getSwapFee() external view _viewlock_ returns (uint) { return _swapFee; } function getReservesRatio() external view _viewlock_ returns (uint) { return _reservesRatio; } function getController() external view _viewlock_ returns (address) { return _controller; } function setSwapFee(uint swapFee) external _logs_ _lock_ { require(!_finalized); require(msg.sender == _controller); require(swapFee >= MIN_FEE); require(swapFee <= MAX_FEE); _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) external _logs_ _lock_ { require(msg.sender == _controller); _controller = manager; } function setPublicSwap(bool public_) external _logs_ _lock_ { require(!_finalized); require(msg.sender == _controller); _publicSwap = public_; } function finalize() external _logs_ _lock_ { require(msg.sender == _controller); require(!_finalized); require(_tokens.length >= MIN_BOUND_TOKENS); _finalized = true; _publicSwap = true; _mintPoolShare(INIT_POOL_SUPPLY); _pushPoolShare(msg.sender, INIT_POOL_SUPPLY); } function bind(address token, uint balance, uint denorm) external _logs_ // _lock_ Bind does not lock because it jumps to `rebind`, which does { require(msg.sender == _controller); require(!_records[token].bound); require(!_finalized); require(_tokens.length < MAX_BOUND_TOKENS); _records[token] = Record({ bound: true, index: _tokens.length, denorm: 0, // balance and denorm will be validated balance: 0 // and set by `rebind` }); _tokens.push(token); rebind(token, balance, denorm); } function rebind(address token, uint balance, uint denorm) public _logs_ _lock_ { require(msg.sender == _controller); require(_records[token].bound); require(!_finalized); require(denorm >= MIN_WEIGHT); require(denorm <= MAX_WEIGHT); require(balance >= MIN_BALANCE); // Adjust the denorm and totalWeight uint oldWeight = _records[token].denorm; if (denorm > oldWeight) { _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight)); require(_totalWeight <= MAX_TOTAL_WEIGHT); } else if (denorm < oldWeight) { _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm)); } _records[token].denorm = denorm; // Adjust the balance record and actual token balance uint oldBalance = _records[token].balance; _records[token].balance = balance; if (balance > oldBalance) { _pullUnderlying(token, msg.sender, bsub(balance, oldBalance)); } else if (balance < oldBalance) { // In this case liquidity is being withdrawn, so charge EXIT_FEE uint tokenBalanceWithdrawn = bsub(oldBalance, balance); uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE); _pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee)); _pushUnderlying(token, _factory, tokenExitFee); } } function unbind(address token) external _logs_ _lock_ { require(msg.sender == _controller); require(_records[token].bound); require(!_finalized); uint tokenBalance = _records[token].balance; uint tokenExitFee = bmul(tokenBalance, EXIT_FEE); _totalWeight = bsub(_totalWeight, _records[token].denorm); // Swap the token-to-unbind with the last token, // then delete the last token uint index = _records[token].index; uint last = _tokens.length - 1; _tokens[index] = _tokens[last]; _records[_tokens[index]].index = index; _tokens.pop(); _records[token] = Record({ bound: false, index: 0, denorm: 0, balance: 0 }); _pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee)); _pushUnderlying(token, _factory, tokenExitFee); } // Absorb any tokens that have been sent to this contract into the pool function gulp(address token) external _logs_ _lock_ { require(_records[token].bound); 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) external view _viewlock_ returns (uint spotPrice) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); Record storage inRecord = _records[tokenIn]; Record storage outRecord = _records[tokenOut]; return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee); } function getSpotPriceSansFee(address tokenIn, address tokenOut) external view _viewlock_ returns (uint spotPrice) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); Record storage inRecord = _records[tokenIn]; Record storage outRecord = _records[tokenOut]; return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, 0); } function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external _logs_ _lock_ { require(_finalized, "ERR_NOT_FINALIZED"); uint poolTotal = totalSupply(); uint ratio = bdiv(poolAmountOut, poolTotal); require(ratio != 0, "ERR_MATH_APPROX"); for (uint i = 0; i < _tokens.length; i++) { address t = _tokens[i]; uint bal = _records[t].balance; uint tokenAmountIn = bmul(ratio, bal); require(tokenAmountIn != 0, "ERR_MATH_APPROX"); require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN"); _records[t].balance = badd(_records[t].balance, tokenAmountIn); emit LOG_JOIN(msg.sender, t, tokenAmountIn, 0); _pullUnderlying(t, msg.sender, tokenAmountIn); } _mintPoolShare(poolAmountOut); _pushPoolShare(msg.sender, poolAmountOut); } function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external _logs_ _lock_ { require(_finalized, "ERR_NOT_FINALIZED"); uint poolTotal = totalSupply(); uint exitFee = bmul(poolAmountIn, EXIT_FEE); uint pAiAfterExitFee = bsub(poolAmountIn, exitFee); uint ratio = bdiv(pAiAfterExitFee, poolTotal); require(ratio != 0, "ERR_MATH_APPROX"); _pullPoolShare(msg.sender, poolAmountIn); _pushPoolShare(_factory, exitFee); _burnPoolShare(pAiAfterExitFee); for (uint i = 0; i < _tokens.length; i++) { address t = _tokens[i]; uint bal = _records[t].balance; uint tokenAmountOut = bmul(ratio, bal); require(tokenAmountOut != 0, "ERR_MATH_APPROX"); require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT"); _records[t].balance = bsub(_records[t].balance, tokenAmountOut); emit LOG_EXIT(msg.sender, t, tokenAmountOut, 0); _pushUnderlying(t, msg.sender, tokenAmountOut); } } function swapExactAmountIn( address tokenIn, uint tokenAmountIn, address tokenOut, uint minAmountOut, uint maxPrice ) external _logs_ _lock_ returns (uint tokenAmountOut, uint spotPriceAfter) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); Record storage inRecord = _records[address(tokenIn)]; Record storage outRecord = _records[address(tokenOut)]; require(tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); uint spotPriceBefore = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE"); uint tokenInFee; (tokenAmountOut, tokenInFee) = calcOutGivenIn( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, tokenAmountIn, _swapFee ); require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT"); uint reserves = calcReservesFromFee(tokenInFee, _reservesRatio); // Subtract `reserves`. inRecord.balance = bsub(badd(inRecord.balance, tokenAmountIn), reserves); outRecord.balance = bsub(outRecord.balance, tokenAmountOut); spotPriceAfter = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX"); require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE"); require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX"); 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); _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); return (tokenAmountOut, spotPriceAfter); } function swapExactAmountOut( address tokenIn, uint maxAmountIn, address tokenOut, uint tokenAmountOut, uint maxPrice ) external _logs_ _lock_ returns (uint tokenAmountIn, uint spotPriceAfter) { require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); Record storage inRecord = _records[address(tokenIn)]; Record storage outRecord = _records[address(tokenOut)]; require(tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); uint spotPriceBefore = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE"); uint tokenInFee; (tokenAmountIn, tokenInFee) = calcInGivenOut( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, tokenAmountOut, _swapFee ); require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN"); 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); spotPriceAfter = calcSpotPrice( inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee ); require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX"); require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE"); require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX"); 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); _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); return (tokenAmountIn, spotPriceAfter); } function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) external _logs_ _lock_ returns (uint poolAmountOut) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenIn].bound, "ERR_NOT_BOUND"); require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); Record storage inRecord = _records[tokenIn]; uint reserves; (poolAmountOut, reserves) = calcPoolOutGivenSingleIn( inRecord.balance, inRecord.denorm, _totalSupply, _totalWeight, tokenAmountIn, _swapFee, _reservesRatio ); require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT"); 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); _pushPoolShare(msg.sender, poolAmountOut); _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); return poolAmountOut; } function joinswapPoolAmountOut(address tokenIn, uint poolAmountOut, uint maxAmountIn) external _logs_ _lock_ returns (uint tokenAmountIn) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenIn].bound, "ERR_NOT_BOUND"); Record storage inRecord = _records[tokenIn]; tokenAmountIn = calcSingleInGivenPoolOut( inRecord.balance, inRecord.denorm, _totalSupply, _totalWeight, poolAmountOut, _swapFee ); require(tokenAmountIn != 0, "ERR_MATH_APPROX"); require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN"); require(tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); uint tokenAmountInZeroFee = calcSingleInGivenPoolOut( inRecord.balance, inRecord.denorm, _totalSupply, _totalWeight, poolAmountOut, 0 ); uint reserves = calcReserves( tokenAmountIn, tokenAmountInZeroFee, _reservesRatio ); 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); _pushPoolShare(msg.sender, poolAmountOut); _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); return tokenAmountIn; } function exitswapPoolAmountIn(address tokenOut, uint poolAmountIn, uint minAmountOut) external _logs_ _lock_ returns (uint tokenAmountOut) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); Record storage outRecord = _records[tokenOut]; tokenAmountOut = calcSingleOutGivenPoolIn( outRecord.balance, outRecord.denorm, _totalSupply, _totalWeight, poolAmountIn, _swapFee ); require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT"); require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); 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); 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); _burnPoolShare(bsub(poolAmountIn, exitFee)); _pushPoolShare(_factory, exitFee); _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); return tokenAmountOut; } function exitswapExternAmountOut(address tokenOut, uint tokenAmountOut, uint maxPoolAmountIn) external _logs_ _lock_ returns (uint poolAmountIn) { require(_finalized, "ERR_NOT_FINALIZED"); require(_records[tokenOut].bound, "ERR_NOT_BOUND"); require(tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); Record storage outRecord = _records[tokenOut]; uint reserves; (poolAmountIn, reserves) = calcPoolInGivenSingleOut( outRecord.balance, outRecord.denorm, _totalSupply, _totalWeight, tokenAmountOut, _swapFee, _reservesRatio ); require(poolAmountIn != 0, "ERR_MATH_APPROX"); require(poolAmountIn <= maxPoolAmountIn, "ERR_LIMIT_IN"); outRecord.balance = bsub(bsub(outRecord.balance, tokenAmountOut), reserves); uint exitFee = bmul(poolAmountIn, EXIT_FEE); 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); _burnPoolShare(bsub(poolAmountIn, exitFee)); _pushPoolShare(_factory, exitFee); _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); return poolAmountIn; } function drainTotalReserves(address reservesAddress) external _logs_ _lock_ { require(msg.sender == _factory); for (uint i = 0; i < _tokens.length; i++) { address t = _tokens[i]; uint tokenAmountOut = totalReserves[t]; totalReserves[t] = 0; emit LOG_DRAIN_RESERVES(reservesAddress, t, tokenAmountOut); _pushUnderlying(t, reservesAddress, tokenAmountOut); } } // == // '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); } }
Find difference