Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
BPool.sol
생성일
6년 전
비교 결과 만료 없음
초기화
내보내기
공유
설명
111 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
740 행
복사
167 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
791 행
복사
// 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);
}
}
저장된 비교 결과
원본
파일 열기
// 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); } }
수정본
파일 열기
// 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); } }
비교하기