ETH Price: $2,019.06 (+1.23%)

Transaction Decoder

Block:
11402165 at Dec-06-2020 11:34:49 PM +UTC
Transaction Fee:
0.00302634 ETH $6.11
Gas Used:
151,317 Gas / 20 Gwei

Emitted Events:

267 blackholeswap.Transfer( from=[Sender] 0xcefcca169357a18eb8c3f230f92b58562e48cae5, to=[Receiver] ExchangeProxy, value=1000000000000000000000 )
268 blackholeswap.Approval( owner=[Receiver] ExchangeProxy, spender=BPool, value=1000000000000000000000 )
269 BPool.0x8201aa3f00000000000000000000000000000000000000000000000000000000( 0x8201aa3f00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000003e66b66fd1d0b02fda6c811da9e0547970db2f21, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000a4, 8201aa3f00000000000000000000000035101c731b1548b5e48bb23f99edbc2f, 5c34193500000000000000000000000000000000000000000000003635c9adc5, dea000000000000000000000000000006b175474e89094c44da98b954eedeac4, 95271d0f00000000000000000000000000000000000000000000000000000000, 00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff, ffffffff00000000000000000000000000000000000000000000000000000000 )
270 BPool.LOG_SWAP( caller=[Receiver] ExchangeProxy, tokenIn=blackholeswap, tokenOut=Dai, tokenAmountIn=1000000000000000000000, tokenAmountOut=1033599611537666371564 )
271 blackholeswap.Transfer( from=[Receiver] ExchangeProxy, to=BPool, value=1000000000000000000000 )
272 Dai.Transfer( src=BPool, dst=[Receiver] ExchangeProxy, wad=1033599611537666371564 )
273 Dai.Transfer( src=[Receiver] ExchangeProxy, dst=[Sender] 0xcefcca169357a18eb8c3f230f92b58562e48cae5, wad=1033599611537666371564 )

Account State Difference:

  Address   Before After State Difference Code
0x1B8874Ba...270D77016
0x35101c73...f5c341935
(Spark Pool)
17.288903410698325901 Eth17.291929750698325901 Eth0.00302634
0x6B175474...495271d0F
0xcefCcA16...62e48CAE5
2.134480937673722293 Eth
Nonce: 3240
2.131454597673722293 Eth
Nonce: 3241
0.00302634

Execution Trace

ExchangeProxy.multihopBatchSwapExactIn( ) => ( totalAmountOut=1033599611537666371564 )
  • blackholeswap.transferFrom( from=0xcefCcA169357a18Eb8c3F230F92b58562e48CAE5, to=0x3E66B66Fd1d0b02fDa6C811Da9E0547970DB2f21, value=1000000000000000000000 ) => ( True )
  • blackholeswap.allowance( owner=0x3E66B66Fd1d0b02fDa6C811Da9E0547970DB2f21, spender=0x1B8874BaceAAfba9eA194a625d12E8b270D77016 ) => ( 0 )
  • blackholeswap.approve( spender=0x1B8874BaceAAfba9eA194a625d12E8b270D77016, value=1000000000000000000000 ) => ( True )
  • BPool.swapExactAmountIn( tokenIn=0x35101c731b1548B5e48bb23F99eDBc2f5c341935, tokenAmountIn=1000000000000000000000, tokenOut=0x6B175474E89094C44Da98b954EedeAC495271d0F, minAmountOut=0, maxPrice=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( tokenAmountOut=1033599611537666371564, spotPriceAfter=970851585929990895 )
    • blackholeswap.transferFrom( from=0x3E66B66Fd1d0b02fDa6C811Da9E0547970DB2f21, to=0x1B8874BaceAAfba9eA194a625d12E8b270D77016, value=1000000000000000000000 ) => ( True )
    • Dai.transfer( dst=0x3E66B66Fd1d0b02fDa6C811Da9E0547970DB2f21, wad=1033599611537666371564 ) => ( True )
    • Dai.transfer( dst=0xcefCcA169357a18Eb8c3F230F92b58562e48CAE5, wad=1033599611537666371564 ) => ( True )
    • blackholeswap.balanceOf( owner=0x3E66B66Fd1d0b02fDa6C811Da9E0547970DB2f21 ) => ( 0 )
      File 1 of 4: ExchangeProxy
      // 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;
      pragma experimental ABIEncoderV2;
      
      pragma solidity ^0.5.0;
      
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           *
           * _Available since v2.4.0._
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      /*
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with GSN meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      contract Context {
          // Empty internal constructor, to prevent people from mistakenly deploying
          // an instance of this contract, which should be used via inheritance.
          constructor () internal { }
          // solhint-disable-previous-line no-empty-blocks
      
          function _msgSender() internal view returns (address payable) {
              return msg.sender;
          }
      
          function _msgData() internal view returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      contract Ownable is Context {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor () internal {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
          }
      
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner(), "Ownable: caller is not the owner");
              _;
          }
      
          /**
           * @dev Returns true if the caller is the current owner.
           */
          function isOwner() public view returns (bool) {
              return _msgSender() == _owner;
          }
      
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      interface PoolInterface {
          function swapExactAmountIn(address, uint, address, uint, uint) external returns (uint, uint);
          function swapExactAmountOut(address, uint, address, uint, uint) external returns (uint, uint);
          function calcInGivenOut(uint, uint, uint, uint, uint, uint) external pure returns (uint);
          function calcOutGivenIn(uint, uint, uint, uint, uint, uint) external pure returns (uint);
          function getDenormalizedWeight(address) external view returns (uint);
          function getBalance(address) external view returns (uint);
          function getSwapFee() external view returns (uint);
      }
      
      interface TokenInterface {
          function balanceOf(address) external view returns (uint);
          function allowance(address, address) external view returns (uint);
          function approve(address, uint) external returns (bool);
          function transfer(address, uint) external returns (bool);
          function transferFrom(address, address, uint) external returns (bool);
          function deposit() external payable;
          function withdraw(uint) external;
      }
      
      interface RegistryInterface {
          function getBestPoolsWithLimit(address, address, uint) external view returns (address[] memory);
      }
      
      contract ExchangeProxy is Ownable {
      
          using SafeMath for uint256;
      
          struct Pool {
              address pool;
              uint    tokenBalanceIn;
              uint    tokenWeightIn;
              uint    tokenBalanceOut;
              uint    tokenWeightOut;
              uint    swapFee;
              uint    effectiveLiquidity;
          }
      
          struct Swap {
              address pool;
              address tokenIn;
              address tokenOut;
              uint    swapAmount; // tokenInAmount / tokenOutAmount
              uint    limitReturnAmount; // minAmountOut / maxAmountIn
              uint    maxPrice;
          }
      
          TokenInterface weth;
          RegistryInterface registry;
          address private constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
          uint private constant BONE = 10**18;
      
          constructor(address _weth) public {
              weth = TokenInterface(_weth);
          }
      
          function setRegistry(address _registry) external onlyOwner {
              registry = RegistryInterface(_registry);
          }
      
          function batchSwapExactIn(
              Swap[] memory swaps,
              TokenInterface tokenIn,
              TokenInterface tokenOut,
              uint totalAmountIn,
              uint minTotalAmountOut
          )
              public payable
              returns (uint totalAmountOut)
          {
              transferFromAll(tokenIn, totalAmountIn);
      
              for (uint i = 0; i < swaps.length; i++) {
                  Swap memory swap = swaps[i];
                  TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
                  PoolInterface pool = PoolInterface(swap.pool);
      
                  if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
                      SwapTokenIn.approve(swap.pool, 0);
                  }
                  SwapTokenIn.approve(swap.pool, swap.swapAmount);
      
                  (uint tokenAmountOut,) = pool.swapExactAmountIn(
                                              swap.tokenIn,
                                              swap.swapAmount,
                                              swap.tokenOut,
                                              swap.limitReturnAmount,
                                              swap.maxPrice
                                          );
                  totalAmountOut = tokenAmountOut.add(totalAmountOut);
              }
      
              require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT");
      
              transferAll(tokenOut, totalAmountOut);
              transferAll(tokenIn, getBalance(tokenIn));
          }
      
          function batchSwapExactOut(
              Swap[] memory swaps,
              TokenInterface tokenIn,
              TokenInterface tokenOut,
              uint maxTotalAmountIn
          )
              public payable
              returns (uint totalAmountIn)
          {
              transferFromAll(tokenIn, maxTotalAmountIn);
      
              for (uint i = 0; i < swaps.length; i++) {
                  Swap memory swap = swaps[i];
                  TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
                  PoolInterface pool = PoolInterface(swap.pool);
      
                  if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
                      SwapTokenIn.approve(swap.pool, 0);
                  }
                  SwapTokenIn.approve(swap.pool, swap.limitReturnAmount);
      
                  (uint tokenAmountIn,) = pool.swapExactAmountOut(
                                              swap.tokenIn,
                                              swap.limitReturnAmount,
                                              swap.tokenOut,
                                              swap.swapAmount,
                                              swap.maxPrice
                                          );
                  totalAmountIn = tokenAmountIn.add(totalAmountIn);
              }
              require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN");
      
              transferAll(tokenOut, getBalance(tokenOut));
              transferAll(tokenIn, getBalance(tokenIn));
      
          }
      
          function multihopBatchSwapExactIn(
              Swap[][] memory swapSequences,
              TokenInterface tokenIn,
              TokenInterface tokenOut,
              uint totalAmountIn,
              uint minTotalAmountOut
          )
              public payable
              returns (uint totalAmountOut)
          {
      
              transferFromAll(tokenIn, totalAmountIn);
      
              for (uint i = 0; i < swapSequences.length; i++) {
                  uint tokenAmountOut;
                  for (uint k = 0; k < swapSequences[i].length; k++) {
                      Swap memory swap = swapSequences[i][k];
                      TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
                      if (k == 1) {
                          // Makes sure that on the second swap the output of the first was used
                          // so there is not intermediate token leftover
                          swap.swapAmount = tokenAmountOut;
                      }
      
                      PoolInterface pool = PoolInterface(swap.pool);
                      if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
                          SwapTokenIn.approve(swap.pool, 0);
                      }
                      SwapTokenIn.approve(swap.pool, swap.swapAmount);
                      (tokenAmountOut,) = pool.swapExactAmountIn(
                                                  swap.tokenIn,
                                                  swap.swapAmount,
                                                  swap.tokenOut,
                                                  swap.limitReturnAmount,
                                                  swap.maxPrice
                                              );
                  }
                  // This takes the amountOut of the last swap
                  totalAmountOut = tokenAmountOut.add(totalAmountOut);
              }
      
              require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT");
      
              transferAll(tokenOut, totalAmountOut);
              transferAll(tokenIn, getBalance(tokenIn));
      
          }
      
          function multihopBatchSwapExactOut(
              Swap[][] memory swapSequences,
              TokenInterface tokenIn,
              TokenInterface tokenOut,
              uint maxTotalAmountIn
          )
              public payable
              returns (uint totalAmountIn)
          {
      
              transferFromAll(tokenIn, maxTotalAmountIn);
      
              for (uint i = 0; i < swapSequences.length; i++) {
                  uint tokenAmountInFirstSwap;
                  // Specific code for a simple swap and a multihop (2 swaps in sequence)
                  if (swapSequences[i].length == 1) {
                      Swap memory swap = swapSequences[i][0];
                      TokenInterface SwapTokenIn = TokenInterface(swap.tokenIn);
      
                      PoolInterface pool = PoolInterface(swap.pool);
                      if (SwapTokenIn.allowance(address(this), swap.pool) > 0) {
                          SwapTokenIn.approve(swap.pool, 0);
                      }
                      SwapTokenIn.approve(swap.pool, swap.limitReturnAmount);
      
                      (tokenAmountInFirstSwap,) = pool.swapExactAmountOut(
                                              swap.tokenIn,
                                              swap.limitReturnAmount,
                                              swap.tokenOut,
                                              swap.swapAmount,
                                              swap.maxPrice
                                          );
                  } else {
                      // Consider we are swapping A -> B and B -> C. The goal is to buy a given amount
                      // of token C. But first we need to buy B with A so we can then buy C with B
                      // To get the exact amount of C we then first need to calculate how much B we'll need:
                      uint intermediateTokenAmount; // This would be token B as described above
                      Swap memory secondSwap = swapSequences[i][1];
                      PoolInterface poolSecondSwap = PoolInterface(secondSwap.pool);
                      intermediateTokenAmount = poolSecondSwap.calcInGivenOut(
                                              poolSecondSwap.getBalance(secondSwap.tokenIn),
                                              poolSecondSwap.getDenormalizedWeight(secondSwap.tokenIn),
                                              poolSecondSwap.getBalance(secondSwap.tokenOut),
                                              poolSecondSwap.getDenormalizedWeight(secondSwap.tokenOut),
                                              secondSwap.swapAmount,
                                              poolSecondSwap.getSwapFee()
                                          );
      
                      //// Buy intermediateTokenAmount of token B with A in the first pool
                      Swap memory firstSwap = swapSequences[i][0];
                      TokenInterface FirstSwapTokenIn = TokenInterface(firstSwap.tokenIn);
                      PoolInterface poolFirstSwap = PoolInterface(firstSwap.pool);
                      if (FirstSwapTokenIn.allowance(address(this), firstSwap.pool) < uint(-1)) {
                          FirstSwapTokenIn.approve(firstSwap.pool, uint(-1));
                      }
      
                      (tokenAmountInFirstSwap,) = poolFirstSwap.swapExactAmountOut(
                                              firstSwap.tokenIn,
                                              firstSwap.limitReturnAmount,
                                              firstSwap.tokenOut,
                                              intermediateTokenAmount, // This is the amount of token B we need
                                              firstSwap.maxPrice
                                          );
      
                      //// Buy the final amount of token C desired
                      TokenInterface SecondSwapTokenIn = TokenInterface(secondSwap.tokenIn);
                      if (SecondSwapTokenIn.allowance(address(this), secondSwap.pool) < uint(-1)) {
                          SecondSwapTokenIn.approve(secondSwap.pool, uint(-1));
                      }
      
                      poolSecondSwap.swapExactAmountOut(
                                              secondSwap.tokenIn,
                                              secondSwap.limitReturnAmount,
                                              secondSwap.tokenOut,
                                              secondSwap.swapAmount,
                                              secondSwap.maxPrice
                                          );
                  }
                  totalAmountIn = tokenAmountInFirstSwap.add(totalAmountIn);
              }
      
              require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN");
      
              transferAll(tokenOut, getBalance(tokenOut));
              transferAll(tokenIn, getBalance(tokenIn));
      
          }
      
          function smartSwapExactIn(
              TokenInterface tokenIn,
              TokenInterface tokenOut,
              uint totalAmountIn,
              uint minTotalAmountOut,
              uint nPools
          )
              public payable
              returns (uint totalAmountOut)
          {
              Swap[] memory swaps;
              if (isETH(tokenIn)) {
                (swaps,) = viewSplitExactIn(address(weth), address(tokenOut), totalAmountIn, nPools);
              } else if (isETH(tokenOut)){
                (swaps,) = viewSplitExactIn(address(tokenIn), address(weth), totalAmountIn, nPools);
              } else {
                (swaps,) = viewSplitExactIn(address(tokenIn), address(tokenOut), totalAmountIn, nPools);
              }
      
              totalAmountOut = batchSwapExactIn(swaps, tokenIn, tokenOut, totalAmountIn, minTotalAmountOut);
          }
      
          function smartSwapExactOut(
              TokenInterface tokenIn,
              TokenInterface tokenOut,
              uint totalAmountOut,
              uint maxTotalAmountIn,
              uint nPools
          )
              public payable
              returns (uint totalAmountIn)
          {
              Swap[] memory swaps;
              if (isETH(tokenIn)) {
                (swaps,) = viewSplitExactOut(address(weth), address(tokenOut), totalAmountOut, nPools);
              } else if (isETH(tokenOut)){
                (swaps,) = viewSplitExactOut(address(tokenIn), address(weth), totalAmountOut, nPools);
              } else {
                (swaps,) = viewSplitExactOut(address(tokenIn), address(tokenOut), totalAmountOut, nPools);
              }
      
              totalAmountIn = batchSwapExactOut(swaps, tokenIn, tokenOut, maxTotalAmountIn);
          }
      
          function viewSplitExactIn(
              address tokenIn,
              address tokenOut,
              uint swapAmount,
              uint nPools
          )
              public view
              returns (Swap[] memory swaps, uint totalOutput)
          {
              address[] memory poolAddresses = registry.getBestPoolsWithLimit(tokenIn, tokenOut, nPools);
      
              Pool[] memory pools = new Pool[](poolAddresses.length);
              uint sumEffectiveLiquidity;
              for (uint i = 0; i < poolAddresses.length; i++) {
                  pools[i] = getPoolData(tokenIn, tokenOut, poolAddresses[i]);
                  sumEffectiveLiquidity = sumEffectiveLiquidity.add(pools[i].effectiveLiquidity);
              }
      
              uint[] memory bestInputAmounts = new uint[](pools.length);
              uint totalInputAmount;
              for (uint i = 0; i < pools.length; i++) {
                  bestInputAmounts[i] = swapAmount.mul(pools[i].effectiveLiquidity).div(sumEffectiveLiquidity);
                  totalInputAmount = totalInputAmount.add(bestInputAmounts[i]);
              }
      
              if (totalInputAmount < swapAmount) {
                  bestInputAmounts[0] = bestInputAmounts[0].add(swapAmount.sub(totalInputAmount));
              } else {
                  bestInputAmounts[0] = bestInputAmounts[0].sub(totalInputAmount.sub(swapAmount));
              }
      
              swaps = new Swap[](pools.length);
      
              for (uint i = 0; i < pools.length; i++) {
                  swaps[i] = Swap({
                              pool: pools[i].pool,
                              tokenIn: tokenIn,
                              tokenOut: tokenOut,
                              swapAmount: bestInputAmounts[i],
                              limitReturnAmount: 0,
                              maxPrice: uint(-1)
                          });
              }
      
              totalOutput = calcTotalOutExactIn(bestInputAmounts, pools);
      
              return (swaps, totalOutput);
          }
      
          function viewSplitExactOut(
              address tokenIn,
              address tokenOut,
              uint swapAmount,
              uint nPools
          )
              public view
              returns (Swap[] memory swaps, uint totalOutput)
          {
              address[] memory poolAddresses = registry.getBestPoolsWithLimit(tokenIn, tokenOut, nPools);
      
              Pool[] memory pools = new Pool[](poolAddresses.length);
              uint sumEffectiveLiquidity;
              for (uint i = 0; i < poolAddresses.length; i++) {
                  pools[i] = getPoolData(tokenIn, tokenOut, poolAddresses[i]);
                  sumEffectiveLiquidity = sumEffectiveLiquidity.add(pools[i].effectiveLiquidity);
              }
      
              uint[] memory bestInputAmounts = new uint[](pools.length);
              uint totalInputAmount;
              for (uint i = 0; i < pools.length; i++) {
                  bestInputAmounts[i] = swapAmount.mul(pools[i].effectiveLiquidity).div(sumEffectiveLiquidity);
                  totalInputAmount = totalInputAmount.add(bestInputAmounts[i]);
              }
              
               if (totalInputAmount < swapAmount) {
                  bestInputAmounts[0] = bestInputAmounts[0].add(swapAmount.sub(totalInputAmount));
              } else {
                  bestInputAmounts[0] = bestInputAmounts[0].sub(totalInputAmount.sub(swapAmount));
              }
      
              swaps = new Swap[](pools.length);
      
              for (uint i = 0; i < pools.length; i++) {
                  swaps[i] = Swap({
                              pool: pools[i].pool,
                              tokenIn: tokenIn,
                              tokenOut: tokenOut,
                              swapAmount: bestInputAmounts[i],
                              limitReturnAmount: uint(-1),
                              maxPrice: uint(-1)
                          });
              }
      
              totalOutput = calcTotalOutExactOut(bestInputAmounts, pools);
      
              return (swaps, totalOutput);
          }
      
          function getPoolData(
              address tokenIn,
              address tokenOut,
              address poolAddress
          )
              internal view
              returns (Pool memory)
          {
              PoolInterface pool = PoolInterface(poolAddress);
              uint tokenBalanceIn = pool.getBalance(tokenIn);
              uint tokenBalanceOut = pool.getBalance(tokenOut);
              uint tokenWeightIn = pool.getDenormalizedWeight(tokenIn);
              uint tokenWeightOut = pool.getDenormalizedWeight(tokenOut);
              uint swapFee = pool.getSwapFee();
      
              uint effectiveLiquidity = calcEffectiveLiquidity(
                                                  tokenWeightIn,
                                                  tokenBalanceOut,
                                                  tokenWeightOut
                                              );
              Pool memory returnPool = Pool({
                  pool: poolAddress,
                  tokenBalanceIn: tokenBalanceIn,
                  tokenWeightIn: tokenWeightIn,
                  tokenBalanceOut: tokenBalanceOut,
                  tokenWeightOut: tokenWeightOut,
                  swapFee: swapFee,
                  effectiveLiquidity: effectiveLiquidity
              });
      
              return returnPool;
          }
      
          function calcEffectiveLiquidity(
              uint tokenWeightIn,
              uint tokenBalanceOut,
              uint tokenWeightOut
          )
              internal pure
              returns (uint effectiveLiquidity)
          {
      
              // Bo * wi/(wi+wo)
              effectiveLiquidity = 
                  tokenWeightIn.mul(BONE).div(
                      tokenWeightOut.add(tokenWeightIn)
                  ).mul(tokenBalanceOut).div(BONE);
      
              return effectiveLiquidity;
          }
      
          function calcTotalOutExactIn(
              uint[] memory bestInputAmounts,
              Pool[] memory bestPools
          )
              internal pure
              returns (uint totalOutput)
          {
              totalOutput = 0;
              for (uint i = 0; i < bestInputAmounts.length; i++) {
                  uint output = PoolInterface(bestPools[i].pool).calcOutGivenIn(
                                      bestPools[i].tokenBalanceIn,
                                      bestPools[i].tokenWeightIn,
                                      bestPools[i].tokenBalanceOut,
                                      bestPools[i].tokenWeightOut,
                                      bestInputAmounts[i],
                                      bestPools[i].swapFee
                                  );
      
                  totalOutput = totalOutput.add(output);
              }
              return totalOutput;
          }
      
          function calcTotalOutExactOut(
              uint[] memory bestInputAmounts,
              Pool[] memory bestPools
          )
              internal pure
              returns (uint totalOutput)
          {
              totalOutput = 0;
              for (uint i = 0; i < bestInputAmounts.length; i++) {
                  uint output = PoolInterface(bestPools[i].pool).calcInGivenOut(
                                      bestPools[i].tokenBalanceIn,
                                      bestPools[i].tokenWeightIn,
                                      bestPools[i].tokenBalanceOut,
                                      bestPools[i].tokenWeightOut,
                                      bestInputAmounts[i],
                                      bestPools[i].swapFee
                                  );
      
                  totalOutput = totalOutput.add(output);
              }
              return totalOutput;
          }
      
          function transferFromAll(TokenInterface token, uint amount) internal returns(bool) {
              if (isETH(token)) {
                  weth.deposit.value(msg.value)();
              } else {
                  require(token.transferFrom(msg.sender, address(this), amount), "ERR_TRANSFER_FAILED");
              }
          }
      
          function getBalance(TokenInterface token) internal view returns (uint) {
              if (isETH(token)) {
                  return weth.balanceOf(address(this));
              } else {
                  return token.balanceOf(address(this));
              }
          }
      
          function transferAll(TokenInterface token, uint amount) internal returns(bool) {
              if (amount == 0) {
                  return true;
              }
      
              if (isETH(token)) {
                  weth.withdraw(amount);
                  (bool xfer,) = msg.sender.call.value(amount)("");
                  require(xfer, "ERR_ETH_FAILED");
              } else {
                  require(token.transfer(msg.sender, amount), "ERR_TRANSFER_FAILED");
              }
          }
      
          function isETH(TokenInterface token) internal pure returns(bool) {
              return (address(token) == ETH_ADDRESS);
          }
      
          function() external payable {}
      }

      File 2 of 4: blackholeswap
      pragma solidity 0.5.16;
      
      library SafeMath {
      
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              if (a == 0) 
                  return 0;
              uint256 c = a * b;
              require(c / a == b);
              return c;
          }
      
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b > 0);
              uint256 c = a / b;
              return c;
          }
      
          function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b > 0);
              uint256 c = a / b;
              if(a % b != 0)
                  c = c + 1;
              return c;
          }
      
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b <= a);
              uint256 c = a - b;
              return c;
          }
      
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a);
              return c;
          }
      
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b != 0);
              return a % b;
          }
      
          int256 constant private INT256_MIN = -2^255;
      
          function mul(int256 a, int256 b) internal pure returns (int256) {
              if (a == 0) 
                  return 0;
              int256 c = a * b;
              require(c / a == b && (a != -1 || b != INT256_MIN));
              return c;
          }
      
          function div(int256 a, int256 b) internal pure returns (int256) {
              require(b != 0 && (b != -1 || a != INT256_MIN));
              int256 c = a / b;
              return c;
          }
      
          function sub(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a - b;
              require((b >= 0 && c <= a) || (b < 0 && c > a));
              return c;
          }
      
          function add(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a + b;
              require((b >= 0 && c >= a) || (b < 0 && c < a));
              return c;
          }
      
          function sqrt(int256 x) internal pure returns (int256) {
              int256 z = add(x / 2, 1);
              int256 y = x;
              while (z < y)
              {
                  y = z;
                  z = ((add((x / z), z)) / 2);
              }
              return y;
          }
      }
      
      
      contract ERC20 {
          using SafeMath for uint256;
      
          mapping (address => uint256) internal _balances;
          mapping (address => mapping (address => uint256)) internal _allowed;
          
          event Transfer(address indexed from, address indexed to, uint256 value);
          event Approval(address indexed owner, address indexed spender, uint256 value);
      
          uint256 internal _totalSupply;
      
          /**
          * @dev Total number of tokens in existence
          */
          function totalSupply() public view returns (uint256) {
              return _totalSupply;
          }
      
          /**
          * @dev Gets the balance of the specified address.
          * @param owner The address to query the balance of.
          * @return A uint256 representing the amount owned by the passed address.
          */
          function balanceOf(address owner) public view returns (uint256) {
              return _balances[owner];
          }
      
          /**
          * @dev Function to check the amount of tokens that an owner allowed to a spender.
          * @param owner address The address which owns the funds.
          * @param spender address The address which will spend the funds.
          * @return A uint256 specifying the amount of tokens still available for the spender.
          */
          function allowance(address owner, address spender) public view returns (uint256) {
              return _allowed[owner][spender];
          }
      
          /**
          * @dev Transfer token to a specified address
          * @param to The address to transfer to.
          * @param value The amount to be transferred.
          */
          function transfer(address to, uint256 value) public returns (bool) {
              _transfer(msg.sender, to, value);
              return true;
          }
      
          /**
          * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
          * Beware that changing an allowance with this method brings the risk that someone may use both the old
          * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
          * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
          * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
          * @param spender The address which will spend the funds.
          * @param value The amount of tokens to be spent.
          */
          function approve(address spender, uint256 value) public returns (bool) {
              _allowed[msg.sender][spender] = value;
              emit Approval(msg.sender, spender, value);
              return true;
          }
      
          /**
          * @dev Transfer tokens from one address to another.
          * Note that while this function emits an Approval event, this is not required as per the specification,
          * and other compliant implementations may not emit the event.
          * @param from address The address which you want to send tokens from
          * @param to address The address which you want to transfer to
          * @param value uint256 the amount of tokens to be transferred
          */
          function transferFrom(address from, address to, uint256 value) public returns (bool) {
              _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
              _transfer(from, to, value);
              return true;
          }
      
          function _transfer(address from, address to, uint256 value) internal {
              require(to != address(0));
              _balances[from] = _balances[from].sub(value);
              _balances[to] = _balances[to].add(value);
              emit Transfer(from, to, value);
          }
      
      }
      
      contract ERC20Mintable is ERC20 {
          string public name;
          string public symbol;
          uint8 public decimals;
      
          function _mint(address to, uint256 amount) internal {
              _balances[to] = _balances[to].add(amount);
              _totalSupply = _totalSupply.add(amount);
              emit Transfer(address(0), to, amount);
          }
      
          function _burn(address from, uint256 amount) internal {
              _balances[from] = _balances[from].sub(amount);
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(from, address(0), amount);
          }
      }
      
      contract CERC20 is ERC20 {
          function borrow(uint256) external returns (uint256);
          function borrowBalanceCurrent(address) external returns (uint256);
          function repayBorrow(uint256) external returns (uint256);
          function mint(uint256) external returns (uint256);
          function redeemUnderlying(uint256) external returns (uint256);
          function balanceOfUnderlying(address) external returns (uint256);
      }
      
      
      interface Comptroller {
          function enterMarkets(address[] calldata) external returns (uint256[] memory);
      }
      
      contract UniswapV2Router02 {
          function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts);
      }
      
      contract blackholeswap is ERC20Mintable {
          using SafeMath for *;
      
          /***********************************|
          |        Variables && Events        |
          |__________________________________*/
      
          Comptroller constant comptroller = Comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
          UniswapV2Router02 constant uniswap = UniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
      
          ERC20 constant Comp = ERC20(0xc00e94Cb662C3520282E6f5717214004A7f26888);
          ERC20 constant Dai = ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
          ERC20 constant USDC = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
          CERC20 constant cDai = CERC20(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643);
          CERC20 constant cUSDC = CERC20(0x39AA39c021dfbaE8faC545936693aC917d5E7563);
      
          event Purchases(address indexed buyer, address indexed sell_token, uint256 inputs, address indexed buy_token, uint256 outputs);
          event AddLiquidity(address indexed provider, uint256 share, int256 DAIAmount, int256 USDCAmount);
          event RemoveLiquidity(address indexed provider, uint256 share, int256 DAIAmount, int256 USDCAmount);
      
          /***********************************|
          |            Constsructor           |
          |__________________________________*/
      
          constructor() public {
              symbol = "BHSc$";
              name = "BlackHoleSwap-Compound DAI/USDC v1";
              decimals = 18;
      
              Dai.approve(address(cDai), uint256(-1));
              USDC.approve(address(cUSDC), uint256(-1));
              Comp.approve(address(uniswap), uint256(-1));
      
              address[] memory cTokens = new address[](2);
              cTokens[0] = address(cDai);
              cTokens[1] = address(cUSDC);
              uint256[] memory errors = comptroller.enterMarkets(cTokens);
              require(errors[0] == 0 && errors[1] == 0, "Comptroller.enterMarkets failed.");
      
              admin = msg.sender;
          }
      
          /***********************************|
          |        Governmence & Params       |
          |__________________________________*/
      
          uint256 public fee = 0.99985e18;
          uint256 public protocolFee = 0;
          uint256 public constant amplifier = 0.75e18;
      
          address private admin;
          address private vault;
      
          function setAdmin(address _admin) external {
              require(msg.sender == admin);
              admin = _admin;
          }
      
          function setParams(uint256 _fee, uint256 _protocolFee) external {
              require(msg.sender == admin);
              require(_fee < 1e18 && _fee >= 0.99e18); //0 < fee <= 1%
              if(_protocolFee > 0)
                  require(uint256(1e18).sub(_fee).div(_protocolFee) >= 3); //protocolFee < 33.3% fee
              fee = _fee;
              protocolFee = _protocolFee;
          }
      
          function setVault(address _vault) external {
              require(msg.sender == admin);
              vault = _vault;
          }
      
          /***********************************|
          |         Getter Functions          |
          |__________________________________*/
      
          function getDaiBalance() public returns (uint256, uint256) {
              if (cDai.balanceOf(address(this)) <= 10)
                  return (0, cDai.borrowBalanceCurrent(address(this)));
              else
                  return (cDai.balanceOfUnderlying(address(this)), cDai.borrowBalanceCurrent(address(this)));
          }
      
          function getUSDCBalance() public returns (uint256, uint256) {
              if (cUSDC.balanceOf(address(this)) <= 10)
                  return (0, cUSDC.borrowBalanceCurrent(address(this)).mul(rate()) );
              else
                  return (cUSDC.balanceOfUnderlying(address(this)).mul(rate()), cUSDC.borrowBalanceCurrent(address(this)).mul(rate()));
          }
      
          // DAI + USDC
          function S() external returns (uint256) {
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
              return(a.add(c).sub(b).sub(d));
          }
      
          function F(int256 _x, int256 x, int256 y) internal pure returns (int256 _y) {
              int256 k;
              int256 c;
              {
                  // u = x + ay, v = y + ax
                  int256 u = x.add(y.mul(int256(amplifier)).div(1e18));
                  int256 v = y.add(x.mul(int256(amplifier)).div(1e18));
                  k = u.mul(v); // k = u * v
                  c = _x.mul(_x).sub( k.mul(1e18).div(int256(amplifier)) ); // c = x^2 - k/a
              }
              
              int256 cst = int256(amplifier).add(1e36.div(int256(amplifier))); // a + 1/a
              int256 b = _x.mul(cst).div(1e18); 
      
              // y^2 + by + c = 0
              // D = b^2 - 4c
              // _y = (-b + sqrt(D)) / 2
      
              int256 D = b.mul(b).sub(c.mul(4));
      
              require(D >= 0, "no root");
      
              _y = (-b).add(D.sqrt()).div(2);
      
          }
      
          function getInputPrice(uint256 input, uint256 a, uint256 b, uint256 c, uint256 d) public pure returns (uint256) {
              int256 x = int256(a).sub(int256(b));
              int256 y = int256(c).sub(int256(d));
              int256 _x = x.add(int256(input));
      
              int256 _y = F(_x, x, y);
      
              return uint256(y.sub(_y));
          }
      
          function getOutputPrice(uint256 output, uint256 a, uint256 b, uint256 c, uint256 d) public pure returns (uint256) {
              int256 x = int256(a).sub(int256(b));
              int256 y = int256(c).sub(int256(d));
              int256 _y = y.sub(int256(output));
      
              int256 _x = F(_y, y, x);
      
              return uint256(_x.sub(x));
          }
      
          function rate() public pure returns (uint256) {
              return 1e12;
          }
      
          /***********************************|
          |        Exchange Functions         |
          |__________________________________*/
          
          function calcFee(uint256 input, uint256 a, uint256 b, uint256 c, uint256 d) internal {
              if(protocolFee > 0) {
                  uint256 _fee = input.mul(protocolFee).mul(_totalSupply).div(1e18).div( a.add(c).sub(b).sub(d) );
                  _mint(vault, _fee);
              }
          }
      
          function dai2usdcIn(uint256 input, uint256 min_output, uint256 deadline) external returns (uint256) {
              require(block.timestamp <= deadline, "EXPIRED");
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
      
              uint256 output = getInputPrice(input.mul(fee).div(1e18), a, b, c, d);
              securityCheck(input, output, a, b, c, d);
              output = output.div(rate());
              require(output >= min_output, "SLIPPAGE_DETECTED");
      
              calcFee(input, a, b, c, d);
      
              doTransferIn(Dai, cDai, b, msg.sender, input);
              doTransferOut(USDC, cUSDC, c.div(rate()), msg.sender, output);
      
              emit Purchases(msg.sender, address(Dai), input, address(USDC), output);
      
              return output;
          }
          
          function usdc2daiIn(uint256 input, uint256 min_output, uint256 deadline) external returns (uint256) {
              require(block.timestamp <= deadline, "EXPIRED");
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
      
              uint256 output = getInputPrice(input.mul(fee).div(1e6), c, d, a, b); // input * rate() * fee / 1e18
              securityCheck(input.mul(rate()), output, c, d, a, b);
              require(output >= min_output, "SLIPPAGE_DETECTED");
              
              calcFee(input.mul(rate()), a, b, c, d);
              
              doTransferIn(USDC, cUSDC, d.div(rate()), msg.sender, input);
              doTransferOut(Dai, cDai, a, msg.sender, output);
      
              emit Purchases(msg.sender, address(USDC), input, address(Dai), output);
      
              return output;
          }
      
          function dai2usdcOut(uint256 max_input, uint256 output, uint256 deadline) external returns (uint256) {
              require(block.timestamp <= deadline, "EXPIRED");
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
      
              uint256 input = getOutputPrice(output.mul(rate()), a, b, c, d);
              securityCheck(input, output.mul(rate()), a, b, c, d);
              input = input.mul(1e18).divCeil(fee);
              require(input <= max_input, "SLIPPAGE_DETECTED");
      
              calcFee(input, a, b, c, d);
      
              doTransferIn(Dai, cDai, b, msg.sender, input);
              doTransferOut(USDC, cUSDC, c.div(rate()), msg.sender, output);
      
              emit Purchases(msg.sender, address(Dai), input, address(USDC), output);
      
              return input;
          }
          
          function usdc2daiOut(uint256 max_input, uint256 output, uint256 deadline) external returns (uint256) {
              require(block.timestamp <= deadline, "EXPIRED");
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
      
              uint256 input = getOutputPrice(output, c, d, a, b);
              securityCheck(input, output, c, d, a, b);
              input = input.mul(1e6).divCeil(fee); // input * 1e18 / fee / 1e12
              require(input <= max_input, "SLIPPAGE_DETECTED");
      
              calcFee(input.mul(rate()), a, b, c, d);
      
              doTransferIn(USDC, cUSDC, d.div(rate()), msg.sender, input);
              doTransferOut(Dai, cDai, a, msg.sender, output);
      
              emit Purchases(msg.sender, address(USDC), input, address(Dai), output);
      
              return input;
          }
          
          function doTransferIn(ERC20 token, CERC20 ctoken, uint256 debt, address from, uint256 amount) internal {
              require(token.transferFrom(from, address(this), amount));
      
              if(debt > 0) {
                  if(debt >= amount) {
                      require(ctoken.repayBorrow(amount) == 0, "ctoken.repayBorrow failed");
                  }
                  else {
                      require(ctoken.repayBorrow(debt) == 0, "ctoken.repayBorrow failed");
                      require(ctoken.mint(amount.sub(debt)) == 0, "ctoken.mint failed");
                  }
              }
              else {
                  require(ctoken.mint(amount) == 0, "ctoken.mint failed");
              }
          }
      
          function doTransferOut(ERC20 token, CERC20 ctoken, uint256 balance, address to, uint256 amount) internal {
              if(balance >= amount) {
                  require(ctoken.redeemUnderlying(amount) == 0, "ctoken.redeemUnderlying failed");
              }
              else {
                  if(balance == 0) {
                      require(ctoken.borrow(amount) == 0, "ctoken.borrow failed");
                  }
                  else {
                      require(ctoken.redeemUnderlying(balance) == 0, "ctoken.redeemUnderlying failed");
                      require(ctoken.borrow(amount.sub(balance)) == 0, "ctoken.borrow failed");
                  }
              }
      
              require(token.transfer(to, amount));
          }
      
          function securityCheck(uint256 input, uint256 output, uint256 a, uint256 b, uint256 c, uint256 d) internal pure {
              if(c < output.add(d))
                  require(output.add(d).sub(c).mul(100) < input.add(a).sub(b).mul(62), "DEBT_TOO_MUCH"); // debt/collateral < 62%
          }
      
          /***********************************|
          |        Liquidity Functions        |
          |__________________________________*/
      
          function addLiquidity(uint256 share, uint256[4] calldata tokens) external returns (uint256 dai_in, uint256 dai_out, uint256 usdc_in, uint256 usdc_out) {
              require(share >= 1e15, 'INVALID_ARGUMENT'); // 1000 * rate()
      
              collectComp();
      
              if (_totalSupply > 0) {
                  (uint256 a, uint256 b) = getDaiBalance();
                  (uint256 c, uint256 d) = getUSDCBalance();
      
                  dai_in = share.mul(a).divCeil(_totalSupply);
                  dai_out = share.mul(b).div(_totalSupply);
                  usdc_in = share.mul(c).divCeil(_totalSupply.mul(rate()));
                  usdc_out = share.mul(d).div(_totalSupply.mul(rate()));
                  require(dai_in <= tokens[0] && dai_out >= tokens[1] && usdc_in <= tokens[2] && usdc_out >= tokens[3], "SLIPPAGE_DETECTED");
                  
                  _mint(msg.sender, share);
      
                  if(dai_in > 0)
                      doTransferIn(Dai, cDai, b, msg.sender, dai_in);
                  if(usdc_in > 0)
                      doTransferIn(USDC, cUSDC, d.div(rate()), msg.sender, usdc_in);
                  if(dai_out > 0)
                      doTransferOut(Dai, cDai, a, msg.sender, dai_out);
                  if(usdc_out > 0)
                      doTransferOut(USDC, cUSDC, c.div(rate()), msg.sender, usdc_out);
      
                  int256 dai_amount = int256(dai_in).sub(int256(dai_out));
                  int256 usdc_amount = int256(usdc_in).sub(int256(usdc_out));
      
                  emit AddLiquidity(msg.sender, share, dai_amount, usdc_amount);
                  return (dai_in, dai_out, usdc_in, usdc_out);
              } else {
                  uint256 dai_amount = share.divCeil(2);
                  uint256 usdc_amount = share.divCeil(rate().mul(2));
      
                  _mint(msg.sender, share);
                  doTransferIn(Dai, cDai, 0, msg.sender, dai_amount);
                  doTransferIn(USDC, cUSDC, 0, msg.sender, usdc_amount);
                  
                  emit AddLiquidity(msg.sender, share, int256(dai_amount), int256(usdc_amount));
                  return (dai_amount, 0, usdc_amount, 0);
              }
          }
      
          function removeLiquidity(uint256 share, uint256[4] calldata tokens) external returns (uint256 dai_in, uint256 dai_out, uint256 usdc_in, uint256 usdc_out) {
              require(share > 0, 'INVALID_ARGUMENT');
      
              collectComp();
      
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
      
              dai_out = share.mul(a).div(_totalSupply);
              dai_in = share.mul(b).divCeil(_totalSupply);
              usdc_out = share.mul(c).div(_totalSupply.mul(rate()));
              usdc_in = share.mul(d).divCeil(_totalSupply.mul(rate()));
              require(dai_in <= tokens[0] && dai_out >= tokens[1] && usdc_in <= tokens[2] && usdc_out >= tokens[3], "SLIPPAGE_DETECTED");
      
              _burn(msg.sender, share);
      
              if(dai_in > 0)
                  doTransferIn(Dai, cDai, b, msg.sender, dai_in);
              if(usdc_in > 0)
                  doTransferIn(USDC, cUSDC, d.div(rate()), msg.sender, usdc_in);
              if(dai_out > 0)
                  doTransferOut(Dai, cDai, a, msg.sender, dai_out);
              if(usdc_out > 0)
                  doTransferOut(USDC, cUSDC, c.div(rate()), msg.sender, usdc_out);
      
              int256 dai_amount = int256(dai_out).sub(int256(dai_in));
              int256 usdc_amount = int256(usdc_out).sub(int256(usdc_in));
      
              emit RemoveLiquidity(msg.sender, share, dai_amount, usdc_amount);
      
              return(dai_in, dai_out, usdc_in, usdc_out);
          }
      
          /***********************************|
          |           Collect Comp            |
          |__________________________________*/
      
          function collectComp() public {
              uint256 _comp = Comp.balanceOf(address(this));
              if(_comp == 0) return;
      
              (uint256 a, uint256 b) = getDaiBalance();
              (uint256 c, uint256 d) = getUSDCBalance();
      
              bool isDai = a.add(d) > c.add(b);
      
              address[] memory path = new address[](3);
              path[0] = address(Comp);
              path[1] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; //weth
              path[2] = isDai ? address(Dai) : address(USDC);
              uint256[] memory amounts = uniswap.swapExactTokensForTokens(_comp, 0, path, address(this), now);
      
              if(isDai)
                  require(cDai.mint(amounts[2]) == 0, "ctoken.mint failed");
              else
                  require(cUSDC.mint(amounts[2]) == 0, "ctoken.mint failed");
      
          }
      
      }

      File 3 of 4: BPool
      {"BColor.sol":{"content":"// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity 0.5.12;\n\ncontract BColor {\n    function getColor()\n        external view\n        returns (bytes32);\n}\n\ncontract BBronze is BColor {\n    function getColor()\n        external view\n        returns (bytes32) {\n            return bytes32(\"BRONZE\");\n        }\n}\n"},"BConst.sol":{"content":"// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity 0.5.12;\n\nimport \"./BColor.sol\";\n\ncontract BConst is BBronze {\n    uint public constant BONE              = 10**18;\n\n    uint public constant MIN_BOUND_TOKENS  = 2;\n    uint public constant MAX_BOUND_TOKENS  = 8;\n\n    uint public constant MIN_FEE           = BONE / 10**6;\n    uint public constant MAX_FEE           = BONE / 10;\n    uint public constant EXIT_FEE          = 0;\n\n    uint public constant MIN_WEIGHT        = BONE;\n    uint public constant MAX_WEIGHT        = BONE * 50;\n    uint public constant MAX_TOTAL_WEIGHT  = BONE * 50;\n    uint public constant MIN_BALANCE       = BONE / 10**12;\n\n    uint public constant INIT_POOL_SUPPLY  = BONE * 100;\n\n    uint public constant MIN_BPOW_BASE     = 1 wei;\n    uint public constant MAX_BPOW_BASE     = (2 * BONE) - 1 wei;\n    uint public constant BPOW_PRECISION    = BONE / 10**10;\n\n    uint public constant MAX_IN_RATIO      = BONE / 2;\n    uint public constant MAX_OUT_RATIO     = (BONE / 3) + 1 wei;\n}\n"},"BMath.sol":{"content":"// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity 0.5.12;\n\nimport \"./BNum.sol\";\n\ncontract BMath is BBronze, BConst, BNum {\n    /**********************************************************************************************\n    // calcSpotPrice                                                                             //\n    // sP = spotPrice                                                                            //\n    // bI = tokenBalanceIn                ( bI / wI )         1                                  //\n    // bO = tokenBalanceOut         sP =  -----------  *  ----------                             //\n    // wI = tokenWeightIn                 ( bO / wO )     ( 1 - sF )                             //\n    // wO = tokenWeightOut                                                                       //\n    // sF = swapFee                                                                              //\n    **********************************************************************************************/\n    function calcSpotPrice(\n        uint tokenBalanceIn,\n        uint tokenWeightIn,\n        uint tokenBalanceOut,\n        uint tokenWeightOut,\n        uint swapFee\n    )\n        public pure\n        returns (uint spotPrice)\n    {\n        uint numer = bdiv(tokenBalanceIn, tokenWeightIn);\n        uint denom = bdiv(tokenBalanceOut, tokenWeightOut);\n        uint ratio = bdiv(numer, denom);\n        uint scale = bdiv(BONE, bsub(BONE, swapFee));\n        return  (spotPrice = bmul(ratio, scale));\n    }\n\n    /**********************************************************************************************\n    // calcOutGivenIn                                                                            //\n    // aO = tokenAmountOut                                                                       //\n    // bO = tokenBalanceOut                                                                      //\n    // bI = tokenBalanceIn              /      /            bI             \\    (wI / wO) \\      //\n    // aI = tokenAmountIn    aO = bO * |  1 - | --------------------------  | ^            |     //\n    // wI = tokenWeightIn               \\      \\ ( bI + ( aI * ( 1 - sF )) /              /      //\n    // wO = tokenWeightOut                                                                       //\n    // sF = swapFee                                                                              //\n    **********************************************************************************************/\n    function calcOutGivenIn(\n        uint tokenBalanceIn,\n        uint tokenWeightIn,\n        uint tokenBalanceOut,\n        uint tokenWeightOut,\n        uint tokenAmountIn,\n        uint swapFee\n    )\n        public pure\n        returns (uint tokenAmountOut)\n    {\n        uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut);\n        uint adjustedIn = bsub(BONE, swapFee);\n        adjustedIn = bmul(tokenAmountIn, adjustedIn);\n        uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));\n        uint foo = bpow(y, weightRatio);\n        uint bar = bsub(BONE, foo);\n        tokenAmountOut = bmul(tokenBalanceOut, bar);\n        return tokenAmountOut;\n    }\n\n    /**********************************************************************************************\n    // calcInGivenOut                                                                            //\n    // aI = tokenAmountIn                                                                        //\n    // bO = tokenBalanceOut               /  /     bO      \\    (wO / wI)      \\                 //\n    // bI = tokenBalanceIn          bI * |  | ------------  | ^            - 1  |                //\n    // aO = tokenAmountOut    aI =        \\  \\ ( bO - aO ) /                   /                 //\n    // wI = tokenWeightIn           --------------------------------------------                 //\n    // wO = tokenWeightOut                          ( 1 - sF )                                   //\n    // sF = swapFee                                                                              //\n    **********************************************************************************************/\n    function calcInGivenOut(\n        uint tokenBalanceIn,\n        uint tokenWeightIn,\n        uint tokenBalanceOut,\n        uint tokenWeightOut,\n        uint tokenAmountOut,\n        uint swapFee\n    )\n        public pure\n        returns (uint tokenAmountIn)\n    {\n        uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn);\n        uint diff = bsub(tokenBalanceOut, tokenAmountOut);\n        uint y = bdiv(tokenBalanceOut, diff);\n        uint foo = bpow(y, weightRatio);\n        foo = bsub(foo, BONE);\n        tokenAmountIn = bsub(BONE, swapFee);\n        tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);\n        return tokenAmountIn;\n    }\n\n    /**********************************************************************************************\n    // calcPoolOutGivenSingleIn                                                                  //\n    // pAo = poolAmountOut         /                                              \\              //\n    // tAi = tokenAmountIn        ///      /     //    wI \\      \\\\       \\     wI \\             //\n    // wI = tokenWeightIn        //| tAi *| 1 - || 1 - --  | * sF || + tBi \\    --  \\            //\n    // tW = totalWeight     pAo=||  \\      \\     \\\\    tW /      //         | ^ tW   | * pS - pS //\n    // tBi = tokenBalanceIn      \\\\  ------------------------------------- /        /            //\n    // pS = poolSupply            \\\\                    tBi               /        /             //\n    // sF = swapFee                \\                                              /              //\n    **********************************************************************************************/\n    function calcPoolOutGivenSingleIn(\n        uint tokenBalanceIn,\n        uint tokenWeightIn,\n        uint poolSupply,\n        uint totalWeight,\n        uint tokenAmountIn,\n        uint swapFee\n    )\n        public pure\n        returns (uint poolAmountOut)\n    {\n        // Charge the trading fee for the proportion of tokenAi\n        ///  which is implicitly traded to the other pool tokens.\n        // That proportion is (1- weightTokenIn)\n        // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);\n        uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);\n        uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); \n        uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));\n\n        uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);\n        uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);\n\n        // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;\n        uint poolRatio = bpow(tokenInRatio, normalizedWeight);\n        uint newPoolSupply = bmul(poolRatio, poolSupply);\n        poolAmountOut = bsub(newPoolSupply, poolSupply);\n        return poolAmountOut;\n    }\n\n    /**********************************************************************************************\n    // calcSingleInGivenPoolOut                                                                  //\n    // tAi = tokenAmountIn              //(pS + pAo)\\     /    1    \\\\                           //\n    // pS = poolSupply                 || ---------  | ^ | --------- || * bI - bI                //\n    // pAo = poolAmountOut              \\\\    pS    /     \\(wI / tW)//                           //\n    // bI = balanceIn          tAi =  --------------------------------------------               //\n    // wI = weightIn                              /      wI  \\                                   //\n    // tW = totalWeight                          |  1 - ----  |  * sF                            //\n    // sF = swapFee                               \\      tW  /                                   //\n    **********************************************************************************************/\n    function calcSingleInGivenPoolOut(\n        uint tokenBalanceIn,\n        uint tokenWeightIn,\n        uint poolSupply,\n        uint totalWeight,\n        uint poolAmountOut,\n        uint swapFee\n    )\n        public pure\n        returns (uint tokenAmountIn)\n    {\n        uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);\n        uint newPoolSupply = badd(poolSupply, poolAmountOut);\n        uint poolRatio = bdiv(newPoolSupply, poolSupply);\n      \n        //uint newBalTi = poolRatio^(1/weightTi) * balTi;\n        uint boo = bdiv(BONE, normalizedWeight); \n        uint tokenInRatio = bpow(poolRatio, boo);\n        uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);\n        uint tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);\n        // Do reverse order of fees charged in joinswap_ExternAmountIn, this way \n        //     ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```\n        //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;\n        uint zar = bmul(bsub(BONE, normalizedWeight), swapFee);\n        tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));\n        return tokenAmountIn;\n    }\n\n    /**********************************************************************************************\n    // calcSingleOutGivenPoolIn                                                                  //\n    // tAo = tokenAmountOut            /      /                                             \\\\   //\n    // bO = tokenBalanceOut           /      // pS - (pAi * (1 - eF)) \\     /    1    \\      \\\\  //\n    // pAi = poolAmountIn            | bO - || ----------------------- | ^ | --------- | * b0 || //\n    // ps = poolSupply                \\      \\\\          pS           /     \\(wO / tW)/      //  //\n    // wI = tokenWeightIn      tAo =   \\      \\                                             //   //\n    // tW = totalWeight                    /     /      wO \\       \\                             //\n    // sF = swapFee                    *  | 1 - |  1 - ---- | * sF  |                            //\n    // eF = exitFee                        \\     \\      tW /       /                             //\n    **********************************************************************************************/\n    function calcSingleOutGivenPoolIn(\n        uint tokenBalanceOut,\n        uint tokenWeightOut,\n        uint poolSupply,\n        uint totalWeight,\n        uint poolAmountIn,\n        uint swapFee\n    )\n        public pure\n        returns (uint tokenAmountOut)\n    {\n        uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);\n        // charge exit fee on the pool token side\n        // pAiAfterExitFee = pAi*(1-exitFee)\n        uint poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE));\n        uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);\n        uint poolRatio = bdiv(newPoolSupply, poolSupply);\n     \n        // newBalTo = poolRatio^(1/weightTo) * balTo;\n        uint tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));\n        uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);\n\n        uint tokenAmountOutBeforeSwapFee = bsub(tokenBalanceOut, newTokenBalanceOut);\n\n        // charge swap fee on the output token side \n        //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)\n        uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); \n        tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));\n        return tokenAmountOut;\n    }\n\n    /**********************************************************************************************\n    // calcPoolInGivenSingleOut                                                                  //\n    // pAi = poolAmountIn               // /               tAo             \\\\     / wO \\     \\   //\n    // bO = tokenBalanceOut            // | bO - -------------------------- |\\   | ---- |     \\  //\n    // tAo = tokenAmountOut      pS - ||   \\     1 - ((1 - (tO / tW)) * sF)/  | ^ \\ tW /  * pS | //\n    // ps = poolSupply                 \\\\ -----------------------------------/                /  //\n    // wO = tokenWeightOut  pAi =       \\\\               bO                 /                /   //\n    // tW = totalWeight           -------------------------------------------------------------  //\n    // sF = swapFee                                        ( 1 - eF )                            //\n    // eF = exitFee                                                                              //\n    **********************************************************************************************/\n    function calcPoolInGivenSingleOut(\n        uint tokenBalanceOut,\n        uint tokenWeightOut,\n        uint poolSupply,\n        uint totalWeight,\n        uint tokenAmountOut,\n        uint swapFee\n    )\n        public pure\n        returns (uint poolAmountIn)\n    {\n\n        // charge swap fee on the output token side \n        uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);\n        //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;\n        uint zoo = bsub(BONE, normalizedWeight);\n        uint zar = bmul(zoo, swapFee); \n        uint tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar));\n\n        uint newTokenBalanceOut = bsub(tokenBalanceOut, tokenAmountOutBeforeSwapFee);\n        uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);\n\n        //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;\n        uint poolRatio = bpow(tokenOutRatio, normalizedWeight);\n        uint newPoolSupply = bmul(poolRatio, poolSupply);\n        uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);\n\n        // charge exit fee on the pool token side\n        // pAi = pAiAfterExitFee/(1-exitFee)\n        poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));\n        return poolAmountIn;\n    }\n\n\n}\n"},"BNum.sol":{"content":"// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity 0.5.12;\n\nimport \"./BConst.sol\";\n\ncontract BNum is BConst {\n\n    function btoi(uint a)\n        internal pure \n        returns (uint)\n    {\n        return a / BONE;\n    }\n\n    function bfloor(uint a)\n        internal pure\n        returns (uint)\n    {\n        return btoi(a) * BONE;\n    }\n\n    function badd(uint a, uint b)\n        internal pure\n        returns (uint)\n    {\n        uint c = a + b;\n        require(c \u003e= a, \"ERR_ADD_OVERFLOW\");\n        return c;\n    }\n\n    function bsub(uint a, uint b)\n        internal pure\n        returns (uint)\n    {\n        (uint c, bool flag) = bsubSign(a, b);\n        require(!flag, \"ERR_SUB_UNDERFLOW\");\n        return c;\n    }\n\n    function bsubSign(uint a, uint b)\n        internal pure\n        returns (uint, bool)\n    {\n        if (a \u003e= b) {\n            return (a - b, false);\n        } else {\n            return (b - a, true);\n        }\n    }\n\n    function bmul(uint a, uint b)\n        internal pure\n        returns (uint)\n    {\n        uint c0 = a * b;\n        require(a == 0 || c0 / a == b, \"ERR_MUL_OVERFLOW\");\n        uint c1 = c0 + (BONE / 2);\n        require(c1 \u003e= c0, \"ERR_MUL_OVERFLOW\");\n        uint c2 = c1 / BONE;\n        return c2;\n    }\n\n    function bdiv(uint a, uint b)\n        internal pure\n        returns (uint)\n    {\n        require(b != 0, \"ERR_DIV_ZERO\");\n        uint c0 = a * BONE;\n        require(a == 0 || c0 / a == BONE, \"ERR_DIV_INTERNAL\"); // bmul overflow\n        uint c1 = c0 + (b / 2);\n        require(c1 \u003e= c0, \"ERR_DIV_INTERNAL\"); //  badd require\n        uint c2 = c1 / b;\n        return c2;\n    }\n\n    // DSMath.wpow\n    function bpowi(uint a, uint n)\n        internal pure\n        returns (uint)\n    {\n        uint z = n % 2 != 0 ? a : BONE;\n\n        for (n /= 2; n != 0; n /= 2) {\n            a = bmul(a, a);\n\n            if (n % 2 != 0) {\n                z = bmul(z, a);\n            }\n        }\n        return z;\n    }\n\n    // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).\n    // Use `bpowi` for `b^e` and `bpowK` for k iterations\n    // of approximation of b^0.w\n    function bpow(uint base, uint exp)\n        internal pure\n        returns (uint)\n    {\n        require(base \u003e= MIN_BPOW_BASE, \"ERR_BPOW_BASE_TOO_LOW\");\n        require(base \u003c= MAX_BPOW_BASE, \"ERR_BPOW_BASE_TOO_HIGH\");\n\n        uint whole  = bfloor(exp);   \n        uint remain = bsub(exp, whole);\n\n        uint wholePow = bpowi(base, btoi(whole));\n\n        if (remain == 0) {\n            return wholePow;\n        }\n\n        uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);\n        return bmul(wholePow, partialResult);\n    }\n\n    function bpowApprox(uint base, uint exp, uint precision)\n        internal pure\n        returns (uint)\n    {\n        // term 0:\n        uint a     = exp;\n        (uint x, bool xneg)  = bsubSign(base, BONE);\n        uint term = BONE;\n        uint sum   = term;\n        bool negative = false;\n\n\n        // term(k) = numer / denom \n        //         = (product(a - i - 1, i=1--\u003ek) * x^k) / (k!)\n        // each iteration, multiply previous term by (a-(k-1)) * x / k\n        // continue until term is less than precision\n        for (uint i = 1; term \u003e= precision; i++) {\n            uint bigK = i * BONE;\n            (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE));\n            term = bmul(term, bmul(c, x));\n            term = bdiv(term, bigK);\n            if (term == 0) break;\n\n            if (xneg) negative = !negative;\n            if (cneg) negative = !negative;\n            if (negative) {\n                sum = bsub(sum, term);\n            } else {\n                sum = badd(sum, term);\n            }\n        }\n\n        return sum;\n    }\n\n}\n"},"BPool.sol":{"content":"// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity 0.5.12;\n\nimport \"./BToken.sol\";\nimport \"./BMath.sol\";\n\ncontract BPool is BBronze, BToken, BMath {\n\n    struct Record {\n        bool bound;   // is token bound to pool\n        uint index;   // private\n        uint denorm;  // denormalized weight\n        uint balance;\n    }\n\n    event LOG_SWAP(\n        address indexed caller,\n        address indexed tokenIn,\n        address indexed tokenOut,\n        uint256         tokenAmountIn,\n        uint256         tokenAmountOut\n    );\n\n    event LOG_JOIN(\n        address indexed caller,\n        address indexed tokenIn,\n        uint256         tokenAmountIn\n    );\n\n    event LOG_EXIT(\n        address indexed caller,\n        address indexed tokenOut,\n        uint256         tokenAmountOut\n    );\n\n    event LOG_CALL(\n        bytes4  indexed sig,\n        address indexed caller,\n        bytes           data\n    ) anonymous;\n\n    modifier _logs_() {\n        emit LOG_CALL(msg.sig, msg.sender, msg.data);\n        _;\n    }\n\n    modifier _lock_() {\n        require(!_mutex, \"ERR_REENTRY\");\n        _mutex = true;\n        _;\n        _mutex = false;\n    }\n\n    modifier _viewlock_() {\n        require(!_mutex, \"ERR_REENTRY\");\n        _;\n    }\n\n    bool private _mutex;\n\n    address private _factory;    // BFactory address to push token exitFee to\n    address private _controller; // has CONTROL role\n    bool private _publicSwap; // true if PUBLIC can call SWAP functions\n\n    // `setSwapFee` and `finalize` require CONTROL\n    // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`\n    uint private _swapFee;\n    bool private _finalized;\n\n    address[] private _tokens;\n    mapping(address=\u003eRecord) private  _records;\n    uint private _totalWeight;\n\n    constructor() public {\n        _controller = msg.sender;\n        _factory = msg.sender;\n        _swapFee = MIN_FEE;\n        _publicSwap = false;\n        _finalized = false;\n    }\n\n    function isPublicSwap()\n        external view\n        returns (bool)\n    {\n        return _publicSwap;\n    }\n\n    function isFinalized()\n        external view\n        returns (bool)\n    {\n        return _finalized;\n    }\n\n    function isBound(address t)\n        external view\n        returns (bool)\n    {\n        return _records[t].bound;\n    }\n\n    function getNumTokens()\n        external view\n        returns (uint) \n    {\n        return _tokens.length;\n    }\n\n    function getCurrentTokens()\n        external view _viewlock_\n        returns (address[] memory tokens)\n    {\n        return _tokens;\n    }\n\n    function getFinalTokens()\n        external view\n        _viewlock_\n        returns (address[] memory tokens)\n    {\n        require(_finalized, \"ERR_NOT_FINALIZED\");\n        return _tokens;\n    }\n\n    function getDenormalizedWeight(address token)\n        external view\n        _viewlock_\n        returns (uint)\n    {\n\n        require(_records[token].bound, \"ERR_NOT_BOUND\");\n        return _records[token].denorm;\n    }\n\n    function getTotalDenormalizedWeight()\n        external view\n        _viewlock_\n        returns (uint)\n    {\n        return _totalWeight;\n    }\n\n    function getNormalizedWeight(address token)\n        external view\n        _viewlock_\n        returns (uint)\n    {\n\n        require(_records[token].bound, \"ERR_NOT_BOUND\");\n        uint denorm = _records[token].denorm;\n        return bdiv(denorm, _totalWeight);\n    }\n\n    function getBalance(address token)\n        external view\n        _viewlock_\n        returns (uint)\n    {\n\n        require(_records[token].bound, \"ERR_NOT_BOUND\");\n        return _records[token].balance;\n    }\n\n    function getSwapFee()\n        external view\n        _viewlock_\n        returns (uint)\n    {\n        return _swapFee;\n    }\n\n    function getController()\n        external view\n        _viewlock_\n        returns (address)\n    {\n        return _controller;\n    }\n\n    function setSwapFee(uint swapFee)\n        external\n        _logs_\n        _lock_\n    { \n        require(!_finalized, \"ERR_IS_FINALIZED\");\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        require(swapFee \u003e= MIN_FEE, \"ERR_MIN_FEE\");\n        require(swapFee \u003c= MAX_FEE, \"ERR_MAX_FEE\");\n        _swapFee = swapFee;\n    }\n\n    function setController(address manager)\n        external\n        _logs_\n        _lock_\n    {\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        _controller = manager;\n    }\n\n    function setPublicSwap(bool public_)\n        external\n        _logs_\n        _lock_\n    {\n        require(!_finalized, \"ERR_IS_FINALIZED\");\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        _publicSwap = public_;\n    }\n\n    function finalize()\n        external\n        _logs_\n        _lock_\n    {\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        require(!_finalized, \"ERR_IS_FINALIZED\");\n        require(_tokens.length \u003e= MIN_BOUND_TOKENS, \"ERR_MIN_TOKENS\");\n\n        _finalized = true;\n        _publicSwap = true;\n\n        _mintPoolShare(INIT_POOL_SUPPLY);\n        _pushPoolShare(msg.sender, INIT_POOL_SUPPLY);\n    }\n\n\n    function bind(address token, uint balance, uint denorm)\n        external\n        _logs_\n        // _lock_  Bind does not lock because it jumps to `rebind`, which does\n    {\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        require(!_records[token].bound, \"ERR_IS_BOUND\");\n        require(!_finalized, \"ERR_IS_FINALIZED\");\n\n        require(_tokens.length \u003c MAX_BOUND_TOKENS, \"ERR_MAX_TOKENS\");\n\n        _records[token] = Record({\n            bound: true,\n            index: _tokens.length,\n            denorm: 0,    // balance and denorm will be validated\n            balance: 0   // and set by `rebind`\n        });\n        _tokens.push(token);\n        rebind(token, balance, denorm);\n    }\n\n    function rebind(address token, uint balance, uint denorm)\n        public\n        _logs_\n        _lock_\n    {\n\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        require(_records[token].bound, \"ERR_NOT_BOUND\");\n        require(!_finalized, \"ERR_IS_FINALIZED\");\n\n        require(denorm \u003e= MIN_WEIGHT, \"ERR_MIN_WEIGHT\");\n        require(denorm \u003c= MAX_WEIGHT, \"ERR_MAX_WEIGHT\");\n        require(balance \u003e= MIN_BALANCE, \"ERR_MIN_BALANCE\");\n\n        // Adjust the denorm and totalWeight\n        uint oldWeight = _records[token].denorm;\n        if (denorm \u003e oldWeight) {\n            _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));\n            require(_totalWeight \u003c= MAX_TOTAL_WEIGHT, \"ERR_MAX_TOTAL_WEIGHT\");\n        } else if (denorm \u003c oldWeight) {\n            _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));\n        }        \n        _records[token].denorm = denorm;\n\n        // Adjust the balance record and actual token balance\n        uint oldBalance = _records[token].balance;\n        _records[token].balance = balance;\n        if (balance \u003e oldBalance) {\n            _pullUnderlying(token, msg.sender, bsub(balance, oldBalance));\n        } else if (balance \u003c oldBalance) {\n            // In this case liquidity is being withdrawn, so charge EXIT_FEE\n            uint tokenBalanceWithdrawn = bsub(oldBalance, balance);\n            uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE);\n            _pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee));\n            _pushUnderlying(token, _factory, tokenExitFee);\n        }\n    }\n\n    function unbind(address token)\n        external\n        _logs_\n        _lock_\n    {\n\n        require(msg.sender == _controller, \"ERR_NOT_CONTROLLER\");\n        require(_records[token].bound, \"ERR_NOT_BOUND\");\n        require(!_finalized, \"ERR_IS_FINALIZED\");\n\n        uint tokenBalance = _records[token].balance;\n        uint tokenExitFee = bmul(tokenBalance, EXIT_FEE);\n\n        _totalWeight = bsub(_totalWeight, _records[token].denorm);\n\n        // Swap the token-to-unbind with the last token,\n        // then delete the last token\n        uint index = _records[token].index;\n        uint last = _tokens.length - 1;\n        _tokens[index] = _tokens[last];\n        _records[_tokens[index]].index = index;\n        _tokens.pop();\n        _records[token] = Record({\n            bound: false,\n            index: 0,\n            denorm: 0,\n            balance: 0\n        });\n\n        _pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee));\n        _pushUnderlying(token, _factory, tokenExitFee);\n    }\n\n    // Absorb any tokens that have been sent to this contract into the pool\n    function gulp(address token)\n        external\n        _logs_\n        _lock_\n    {\n        require(_records[token].bound, \"ERR_NOT_BOUND\");\n        _records[token].balance = IERC20(token).balanceOf(address(this));\n    }\n\n    function getSpotPrice(address tokenIn, address tokenOut)\n        external view\n        _viewlock_\n        returns (uint spotPrice)\n    {\n        require(_records[tokenIn].bound, \"ERR_NOT_BOUND\");\n        require(_records[tokenOut].bound, \"ERR_NOT_BOUND\");\n        Record storage inRecord = _records[tokenIn];\n        Record storage outRecord = _records[tokenOut];\n        return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee);\n    }\n\n    function getSpotPriceSansFee(address tokenIn, address tokenOut)\n        external view\n        _viewlock_\n        returns (uint spotPrice)\n    {\n        require(_records[tokenIn].bound, \"ERR_NOT_BOUND\");\n        require(_records[tokenOut].bound, \"ERR_NOT_BOUND\");\n        Record storage inRecord = _records[tokenIn];\n        Record storage outRecord = _records[tokenOut];\n        return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, 0);\n    }\n\n    function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn)\n        external\n        _logs_\n        _lock_\n    {\n        require(_finalized, \"ERR_NOT_FINALIZED\");\n\n        uint poolTotal = totalSupply();\n        uint ratio = bdiv(poolAmountOut, poolTotal);\n        require(ratio != 0, \"ERR_MATH_APPROX\");\n\n        for (uint i = 0; i \u003c _tokens.length; i++) {\n            address t = _tokens[i];\n            uint bal = _records[t].balance;\n            uint tokenAmountIn = bmul(ratio, bal);\n            require(tokenAmountIn != 0, \"ERR_MATH_APPROX\");\n            require(tokenAmountIn \u003c= maxAmountsIn[i], \"ERR_LIMIT_IN\");\n            _records[t].balance = badd(_records[t].balance, tokenAmountIn);\n            emit LOG_JOIN(msg.sender, t, tokenAmountIn);\n            _pullUnderlying(t, msg.sender, tokenAmountIn);\n        }\n        _mintPoolShare(poolAmountOut);\n        _pushPoolShare(msg.sender, poolAmountOut);\n    }\n\n    function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut)\n        external\n        _logs_\n        _lock_\n    {\n        require(_finalized, \"ERR_NOT_FINALIZED\");\n\n        uint poolTotal = totalSupply();\n        uint exitFee = bmul(poolAmountIn, EXIT_FEE);\n        uint pAiAfterExitFee = bsub(poolAmountIn, exitFee);\n        uint ratio = bdiv(pAiAfterExitFee, poolTotal);\n        require(ratio != 0, \"ERR_MATH_APPROX\");\n\n        _pullPoolShare(msg.sender, poolAmountIn);\n        _pushPoolShare(_factory, exitFee);\n        _burnPoolShare(pAiAfterExitFee);\n\n        for (uint i = 0; i \u003c _tokens.length; i++) {\n            address t = _tokens[i];\n            uint bal = _records[t].balance;\n            uint tokenAmountOut = bmul(ratio, bal);\n            require(tokenAmountOut != 0, \"ERR_MATH_APPROX\");\n            require(tokenAmountOut \u003e= minAmountsOut[i], \"ERR_LIMIT_OUT\");\n            _records[t].balance = bsub(_records[t].balance, tokenAmountOut);\n            emit LOG_EXIT(msg.sender, t, tokenAmountOut);\n            _pushUnderlying(t, msg.sender, tokenAmountOut);\n        }\n\n    }\n\n\n    function swapExactAmountIn(\n        address tokenIn,\n        uint tokenAmountIn,\n        address tokenOut,\n        uint minAmountOut,\n        uint maxPrice\n    )\n        external\n        _logs_\n        _lock_\n        returns (uint tokenAmountOut, uint spotPriceAfter)\n    {\n\n        require(_records[tokenIn].bound, \"ERR_NOT_BOUND\");\n        require(_records[tokenOut].bound, \"ERR_NOT_BOUND\");\n        require(_publicSwap, \"ERR_SWAP_NOT_PUBLIC\");\n\n        Record storage inRecord = _records[address(tokenIn)];\n        Record storage outRecord = _records[address(tokenOut)];\n\n        require(tokenAmountIn \u003c= bmul(inRecord.balance, MAX_IN_RATIO), \"ERR_MAX_IN_RATIO\");\n\n        uint spotPriceBefore = calcSpotPrice(\n                                    inRecord.balance,\n                                    inRecord.denorm,\n                                    outRecord.balance,\n                                    outRecord.denorm,\n                                    _swapFee\n                                );\n        require(spotPriceBefore \u003c= maxPrice, \"ERR_BAD_LIMIT_PRICE\");\n\n        tokenAmountOut = calcOutGivenIn(\n                            inRecord.balance,\n                            inRecord.denorm,\n                            outRecord.balance,\n                            outRecord.denorm,\n                            tokenAmountIn,\n                            _swapFee\n                        );\n        require(tokenAmountOut \u003e= minAmountOut, \"ERR_LIMIT_OUT\");\n\n        inRecord.balance = badd(inRecord.balance, tokenAmountIn);\n        outRecord.balance = bsub(outRecord.balance, tokenAmountOut);\n\n        spotPriceAfter = calcSpotPrice(\n                                inRecord.balance,\n                                inRecord.denorm,\n                                outRecord.balance,\n                                outRecord.denorm,\n                                _swapFee\n                            );\n        require(spotPriceAfter \u003e= spotPriceBefore, \"ERR_MATH_APPROX\");     \n        require(spotPriceAfter \u003c= maxPrice, \"ERR_LIMIT_PRICE\");\n        require(spotPriceBefore \u003c= bdiv(tokenAmountIn, tokenAmountOut), \"ERR_MATH_APPROX\");\n\n        emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);\n\n        _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);\n        _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);\n\n        return (tokenAmountOut, spotPriceAfter);\n    }\n\n    function swapExactAmountOut(\n        address tokenIn,\n        uint maxAmountIn,\n        address tokenOut,\n        uint tokenAmountOut,\n        uint maxPrice\n    )\n        external\n        _logs_\n        _lock_ \n        returns (uint tokenAmountIn, uint spotPriceAfter)\n    {\n        require(_records[tokenIn].bound, \"ERR_NOT_BOUND\");\n        require(_records[tokenOut].bound, \"ERR_NOT_BOUND\");\n        require(_publicSwap, \"ERR_SWAP_NOT_PUBLIC\");\n\n        Record storage inRecord = _records[address(tokenIn)];\n        Record storage outRecord = _records[address(tokenOut)];\n\n        require(tokenAmountOut \u003c= bmul(outRecord.balance, MAX_OUT_RATIO), \"ERR_MAX_OUT_RATIO\");\n\n        uint spotPriceBefore = calcSpotPrice(\n                                    inRecord.balance,\n                                    inRecord.denorm,\n                                    outRecord.balance,\n                                    outRecord.denorm,\n                                    _swapFee\n                                );\n        require(spotPriceBefore \u003c= maxPrice, \"ERR_BAD_LIMIT_PRICE\");\n\n        tokenAmountIn = calcInGivenOut(\n                            inRecord.balance,\n                            inRecord.denorm,\n                            outRecord.balance,\n                            outRecord.denorm,\n                            tokenAmountOut,\n                            _swapFee\n                        );\n        require(tokenAmountIn \u003c= maxAmountIn, \"ERR_LIMIT_IN\");\n\n        inRecord.balance = badd(inRecord.balance, tokenAmountIn);\n        outRecord.balance = bsub(outRecord.balance, tokenAmountOut);\n\n        spotPriceAfter = calcSpotPrice(\n                                inRecord.balance,\n                                inRecord.denorm,\n                                outRecord.balance,\n                                outRecord.denorm,\n                                _swapFee\n                            );\n        require(spotPriceAfter \u003e= spotPriceBefore, \"ERR_MATH_APPROX\");\n        require(spotPriceAfter \u003c= maxPrice, \"ERR_LIMIT_PRICE\");\n        require(spotPriceBefore \u003c= bdiv(tokenAmountIn, tokenAmountOut), \"ERR_MATH_APPROX\");\n\n        emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);\n\n        _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);\n        _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);\n\n        return (tokenAmountIn, spotPriceAfter);\n    }\n\n\n    function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut)\n        external\n        _logs_\n        _lock_\n        returns (uint poolAmountOut)\n\n    {        \n        require(_finalized, \"ERR_NOT_FINALIZED\");\n        require(_records[tokenIn].bound, \"ERR_NOT_BOUND\");\n        require(tokenAmountIn \u003c= bmul(_records[tokenIn].balance, MAX_IN_RATIO), \"ERR_MAX_IN_RATIO\");\n\n        Record storage inRecord = _records[tokenIn];\n\n        poolAmountOut = calcPoolOutGivenSingleIn(\n                            inRecord.balance,\n                            inRecord.denorm,\n                            _totalSupply,\n                            _totalWeight,\n                            tokenAmountIn,\n                            _swapFee\n                        );\n\n        require(poolAmountOut \u003e= minPoolAmountOut, \"ERR_LIMIT_OUT\");\n\n        inRecord.balance = badd(inRecord.balance, tokenAmountIn);\n\n        emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);\n\n        _mintPoolShare(poolAmountOut);\n        _pushPoolShare(msg.sender, poolAmountOut);\n        _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);\n\n        return poolAmountOut;\n    }\n\n    function joinswapPoolAmountOut(address tokenIn, uint poolAmountOut, uint maxAmountIn)\n        external\n        _logs_\n        _lock_\n        returns (uint tokenAmountIn)\n    {\n        require(_finalized, \"ERR_NOT_FINALIZED\");\n        require(_records[tokenIn].bound, \"ERR_NOT_BOUND\");\n\n        Record storage inRecord = _records[tokenIn];\n\n        tokenAmountIn = calcSingleInGivenPoolOut(\n                            inRecord.balance,\n                            inRecord.denorm,\n                            _totalSupply,\n                            _totalWeight,\n                            poolAmountOut,\n                            _swapFee\n                        );\n\n        require(tokenAmountIn != 0, \"ERR_MATH_APPROX\");\n        require(tokenAmountIn \u003c= maxAmountIn, \"ERR_LIMIT_IN\");\n        \n        require(tokenAmountIn \u003c= bmul(_records[tokenIn].balance, MAX_IN_RATIO), \"ERR_MAX_IN_RATIO\");\n\n        inRecord.balance = badd(inRecord.balance, tokenAmountIn);\n\n        emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);\n\n        _mintPoolShare(poolAmountOut);\n        _pushPoolShare(msg.sender, poolAmountOut);\n        _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);\n\n        return tokenAmountIn;\n    }\n\n    function exitswapPoolAmountIn(address tokenOut, uint poolAmountIn, uint minAmountOut)\n        external\n        _logs_\n        _lock_\n        returns (uint tokenAmountOut)\n    {\n        require(_finalized, \"ERR_NOT_FINALIZED\");\n        require(_records[tokenOut].bound, \"ERR_NOT_BOUND\");\n\n        Record storage outRecord = _records[tokenOut];\n\n        tokenAmountOut = calcSingleOutGivenPoolIn(\n                            outRecord.balance,\n                            outRecord.denorm,\n                            _totalSupply,\n                            _totalWeight,\n                            poolAmountIn,\n                            _swapFee\n                        );\n\n        require(tokenAmountOut \u003e= minAmountOut, \"ERR_LIMIT_OUT\");\n        \n        require(tokenAmountOut \u003c= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), \"ERR_MAX_OUT_RATIO\");\n\n        outRecord.balance = bsub(outRecord.balance, tokenAmountOut);\n\n        uint exitFee = bmul(poolAmountIn, EXIT_FEE);\n\n        emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);\n\n        _pullPoolShare(msg.sender, poolAmountIn);\n        _burnPoolShare(bsub(poolAmountIn, exitFee));\n        _pushPoolShare(_factory, exitFee);\n        _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);\n\n        return tokenAmountOut;\n    }\n\n    function exitswapExternAmountOut(address tokenOut, uint tokenAmountOut, uint maxPoolAmountIn)\n        external\n        _logs_\n        _lock_\n        returns (uint poolAmountIn)\n    {\n        require(_finalized, \"ERR_NOT_FINALIZED\");\n        require(_records[tokenOut].bound, \"ERR_NOT_BOUND\");\n        require(tokenAmountOut \u003c= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), \"ERR_MAX_OUT_RATIO\");\n\n        Record storage outRecord = _records[tokenOut];\n\n        poolAmountIn = calcPoolInGivenSingleOut(\n                            outRecord.balance,\n                            outRecord.denorm,\n                            _totalSupply,\n                            _totalWeight,\n                            tokenAmountOut,\n                            _swapFee\n                        );\n\n        require(poolAmountIn != 0, \"ERR_MATH_APPROX\");\n        require(poolAmountIn \u003c= maxPoolAmountIn, \"ERR_LIMIT_IN\");\n\n        outRecord.balance = bsub(outRecord.balance, tokenAmountOut);\n\n        uint exitFee = bmul(poolAmountIn, EXIT_FEE);\n\n        emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);\n\n        _pullPoolShare(msg.sender, poolAmountIn);\n        _burnPoolShare(bsub(poolAmountIn, exitFee));\n        _pushPoolShare(_factory, exitFee);\n        _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);        \n\n        return poolAmountIn;\n    }\n\n\n    // ==\n    // \u0027Underlying\u0027 token-manipulation functions make external calls but are NOT locked\n    // You must `_lock_` or otherwise ensure reentry-safety\n\n    function _pullUnderlying(address erc20, address from, uint amount)\n        internal\n    {\n        bool xfer = IERC20(erc20).transferFrom(from, address(this), amount);\n        require(xfer, \"ERR_ERC20_FALSE\");\n    }\n\n    function _pushUnderlying(address erc20, address to, uint amount)\n        internal\n    {\n        bool xfer = IERC20(erc20).transfer(to, amount);\n        require(xfer, \"ERR_ERC20_FALSE\");\n    }\n\n    function _pullPoolShare(address from, uint amount)\n        internal\n    {\n        _pull(from, amount);\n    }\n\n    function _pushPoolShare(address to, uint amount)\n        internal\n    {\n        _push(to, amount);\n    }\n\n    function _mintPoolShare(uint amount)\n        internal\n    {\n        _mint(amount);\n    }\n\n    function _burnPoolShare(uint amount)\n        internal\n    {\n        _burn(amount);\n    }\n\n}\n"},"BToken.sol":{"content":"// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity 0.5.12;\n\nimport \"./BNum.sol\";\n\n// Highly opinionated token implementation\n\ninterface IERC20 {\n    event Approval(address indexed src, address indexed dst, uint amt);\n    event Transfer(address indexed src, address indexed dst, uint amt);\n\n    function totalSupply() external view returns (uint);\n    function balanceOf(address whom) external view returns (uint);\n    function allowance(address src, address dst) external view returns (uint);\n\n    function approve(address dst, uint amt) external returns (bool);\n    function transfer(address dst, uint amt) external returns (bool);\n    function transferFrom(\n        address src, address dst, uint amt\n    ) external returns (bool);\n}\n\ncontract BTokenBase is BNum {\n\n    mapping(address =\u003e uint)                   internal _balance;\n    mapping(address =\u003e mapping(address=\u003euint)) internal _allowance;\n    uint internal _totalSupply;\n\n    event Approval(address indexed src, address indexed dst, uint amt);\n    event Transfer(address indexed src, address indexed dst, uint amt);\n\n    function _mint(uint amt) internal {\n        _balance[address(this)] = badd(_balance[address(this)], amt);\n        _totalSupply = badd(_totalSupply, amt);\n        emit Transfer(address(0), address(this), amt);\n    }\n\n    function _burn(uint amt) internal {\n        require(_balance[address(this)] \u003e= amt, \"ERR_INSUFFICIENT_BAL\");\n        _balance[address(this)] = bsub(_balance[address(this)], amt);\n        _totalSupply = bsub(_totalSupply, amt);\n        emit Transfer(address(this), address(0), amt);\n    }\n\n    function _move(address src, address dst, uint amt) internal {\n        require(_balance[src] \u003e= amt, \"ERR_INSUFFICIENT_BAL\");\n        _balance[src] = bsub(_balance[src], amt);\n        _balance[dst] = badd(_balance[dst], amt);\n        emit Transfer(src, dst, amt);\n    }\n\n    function _push(address to, uint amt) internal {\n        _move(address(this), to, amt);\n    }\n\n    function _pull(address from, uint amt) internal {\n        _move(from, address(this), amt);\n    }\n}\n\ncontract BToken is BTokenBase, IERC20 {\n\n    string  private _name     = \"Balancer Pool Token\";\n    string  private _symbol   = \"BPT\";\n    uint8   private _decimals = 18;\n\n    function name() public view returns (string memory) {\n        return _name;\n    }\n\n    function symbol() public view returns (string memory) {\n        return _symbol;\n    }\n\n    function decimals() public view returns(uint8) {\n        return _decimals;\n    }\n\n    function allowance(address src, address dst) external view returns (uint) {\n        return _allowance[src][dst];\n    }\n\n    function balanceOf(address whom) external view returns (uint) {\n        return _balance[whom];\n    }\n\n    function totalSupply() public view returns (uint) {\n        return _totalSupply;\n    }\n\n    function approve(address dst, uint amt) external returns (bool) {\n        _allowance[msg.sender][dst] = amt;\n        emit Approval(msg.sender, dst, amt);\n        return true;\n    }\n\n    function increaseApproval(address dst, uint amt) external returns (bool) {\n        _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);\n        emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);\n        return true;\n    }\n\n    function decreaseApproval(address dst, uint amt) external returns (bool) {\n        uint oldValue = _allowance[msg.sender][dst];\n        if (amt \u003e oldValue) {\n            _allowance[msg.sender][dst] = 0;\n        } else {\n            _allowance[msg.sender][dst] = bsub(oldValue, amt);\n        }\n        emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);\n        return true;\n    }\n\n    function transfer(address dst, uint amt) external returns (bool) {\n        _move(msg.sender, dst, amt);\n        return true;\n    }\n\n    function transferFrom(address src, address dst, uint amt) external returns (bool) {\n        require(msg.sender == src || amt \u003c= _allowance[src][msg.sender], \"ERR_BTOKEN_BAD_CALLER\");\n        _move(src, dst, amt);\n        if (msg.sender != src \u0026\u0026 _allowance[src][msg.sender] != uint256(-1)) {\n            _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);\n            emit Approval(msg.sender, dst, _allowance[src][msg.sender]);\n        }\n        return true;\n    }\n}\n"}}

      File 4 of 4: Dai
      // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
      pragma solidity =0.5.12;
      
      ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
      // 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; */
      
      contract LibNote {
          event LogNote(
              bytes4   indexed  sig,
              address  indexed  usr,
              bytes32  indexed  arg1,
              bytes32  indexed  arg2,
              bytes             data
          ) anonymous;
      
          modifier note {
              _;
              assembly {
                  // log an 'anonymous' event with a constant 6 words of calldata
                  // and four indexed topics: selector, caller, arg1 and arg2
                  let mark := msize                         // end of memory ensures zero
                  mstore(0x40, add(mark, 288))              // update free memory pointer
                  mstore(mark, 0x20)                        // bytes type data offset
                  mstore(add(mark, 0x20), 224)              // bytes size (padded)
                  calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                  log4(mark, 288,                           // calldata
                       shl(224, shr(224, calldataload(0))), // msg.sig
                       caller,                              // msg.sender
                       calldataload(4),                     // arg1
                       calldataload(36)                     // arg2
                      )
              }
          }
      }
      
      ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
      // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
      
      // This program is free software: you can redistribute it and/or modify
      // it under the terms of the GNU Affero 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 Affero General Public License for more details.
      //
      // You should have received a copy of the GNU Affero General Public License
      // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      
      /* pragma solidity 0.5.12; */
      
      /* import "./lib.sol"; */
      
      contract Dai is LibNote {
          // --- Auth ---
          mapping (address => uint) public wards;
          function rely(address guy) external note auth { wards[guy] = 1; }
          function deny(address guy) external note auth { wards[guy] = 0; }
          modifier auth {
              require(wards[msg.sender] == 1, "Dai/not-authorized");
              _;
          }
      
          // --- ERC20 Data ---
          string  public constant name     = "Dai Stablecoin";
          string  public constant symbol   = "DAI";
          string  public constant version  = "1";
          uint8   public constant decimals = 18;
          uint256 public totalSupply;
      
          mapping (address => uint)                      public balanceOf;
          mapping (address => mapping (address => uint)) public allowance;
          mapping (address => uint)                      public nonces;
      
          event Approval(address indexed src, address indexed guy, uint wad);
          event Transfer(address indexed src, address indexed dst, uint wad);
      
          // --- Math ---
          function add(uint x, uint y) internal pure returns (uint z) {
              require((z = x + y) >= x);
          }
          function sub(uint x, uint y) internal pure returns (uint z) {
              require((z = x - y) <= x);
          }
      
          // --- EIP712 niceties ---
          bytes32 public DOMAIN_SEPARATOR;
          // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
          bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
      
          constructor(uint256 chainId_) public {
              wards[msg.sender] = 1;
              DOMAIN_SEPARATOR = keccak256(abi.encode(
                  keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                  keccak256(bytes(name)),
                  keccak256(bytes(version)),
                  chainId_,
                  address(this)
              ));
          }
      
          // --- Token ---
          function transfer(address dst, uint wad) external returns (bool) {
              return transferFrom(msg.sender, dst, wad);
          }
          function transferFrom(address src, address dst, uint wad)
              public returns (bool)
          {
              require(balanceOf[src] >= wad, "Dai/insufficient-balance");
              if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                  require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
                  allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
              }
              balanceOf[src] = sub(balanceOf[src], wad);
              balanceOf[dst] = add(balanceOf[dst], wad);
              emit Transfer(src, dst, wad);
              return true;
          }
          function mint(address usr, uint wad) external auth {
              balanceOf[usr] = add(balanceOf[usr], wad);
              totalSupply    = add(totalSupply, wad);
              emit Transfer(address(0), usr, wad);
          }
          function burn(address usr, uint wad) external {
              require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
              if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
                  require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
                  allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
              }
              balanceOf[usr] = sub(balanceOf[usr], wad);
              totalSupply    = sub(totalSupply, wad);
              emit Transfer(usr, address(0), wad);
          }
          function approve(address usr, uint wad) external returns (bool) {
              allowance[msg.sender][usr] = wad;
              emit Approval(msg.sender, usr, wad);
              return true;
          }
      
          // --- Alias ---
          function push(address usr, uint wad) external {
              transferFrom(msg.sender, usr, wad);
          }
          function pull(address usr, uint wad) external {
              transferFrom(usr, msg.sender, wad);
          }
          function move(address src, address dst, uint wad) external {
              transferFrom(src, dst, wad);
          }
      
          // --- Approve by signature ---
          function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                          bool allowed, uint8 v, bytes32 r, bytes32 s) external
          {
              bytes32 digest =
                  keccak256(abi.encodePacked(
                      "\x19\x01",
                      DOMAIN_SEPARATOR,
                      keccak256(abi.encode(PERMIT_TYPEHASH,
                                           holder,
                                           spender,
                                           nonce,
                                           expiry,
                                           allowed))
              ));
      
              require(holder != address(0), "Dai/invalid-address-0");
              require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
              require(expiry == 0 || now <= expiry, "Dai/permit-expired");
              require(nonce == nonces[holder]++, "Dai/invalid-nonce");
              uint wad = allowed ? uint(-1) : 0;
              allowance[holder][spender] = wad;
              emit Approval(holder, spender, wad);
          }
      }