ETH Price: $1,976.88 (-5.14%)

Transaction Decoder

Block:
11364920 at Dec-01-2020 06:03:29 AM +UTC
Transaction Fee:
0.006231804 ETH $12.32
Gas Used:
90,316 Gas / 69 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x1fc260Eb...F44b560e4
1.91221664070283065 Eth
Nonce: 9939
1.90598483670283065 Eth
Nonce: 9940
0.006231804
(Spark Pool)
7.447175762422256715 Eth7.453407566422256715 Eth0.006231804

Execution Trace

0x7ee8ab2a8d890c000acc87bf6e22e2ad383e23ce.865a6b4f( )
  • 0xdbb0ed37a0e6db1c1fe3b21350f69a6114b88d02.ed2f0c53( )
    • Keep3rV1.balanceOf( account=0x87fEbfb3AC5791034fD5EF1a615e9d9627C2665D ) => ( 5746884300375728413927 )
    • BPool.getBalance( token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ) => ( 464524152877613749650 )
    • BPool.getBalance( token=0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e ) => ( 10916030796038112754 )
    • BPool.getDenormalizedWeight( token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ) => ( 7500000000000000000 )
    • BPool.getDenormalizedWeight( token=0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e ) => ( 7500000000000000000 )
    • BPool.STATICCALL( )
    • BPool.calcOutGivenIn( tokenBalanceIn=464524152877613749650, tokenWeightIn=7500000000000000000, tokenBalanceOut=10916030796038112754, tokenWeightOut=7500000000000000000, tokenAmountIn=734053542364105621, swapFee=3000000000000000 ) => ( tokenAmountOut=17171002948972086 )
    • Balancer: Pool 394.f8b2cb4f( )
    • Balancer: Pool 394.f8b2cb4f( )
    • Balancer: Pool 394.948d8ce6( )
    • Balancer: Pool 394.948d8ce6( )
    • Balancer: Pool 394.STATICCALL( )
    • Balancer: Pool 394.ba9530a6( )
    • UniswapV2Pair.STATICCALL( )
      File 1 of 3: Keep3rV1
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.12;
      
      // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
      // Subject to the MIT license.
      
      /**
       * @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(uint a, uint b) internal pure returns (uint) {
              uint c = a + b;
              require(c >= a, "add: +");
      
              return c;
          }
      
          /**
           * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
              uint c = a + b;
              require(c >= a, errorMessage);
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot underflow.
           */
          function sub(uint a, uint b) internal pure returns (uint) {
              return sub(a, b, "sub: -");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot underflow.
           */
          function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
              require(b <= a, errorMessage);
              uint 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(uint a, uint b) internal pure returns (uint) {
              // 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;
              }
      
              uint c = a * b;
              require(c / a == b, "mul: *");
      
              return c;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
              // 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;
              }
      
              uint c = a * b;
              require(c / a == b, errorMessage);
      
              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(uint a, uint b) internal pure returns (uint) {
              return div(a, b, "div: /");
          }
      
          /**
           * @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.
           */
          function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint 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(uint a, uint b) internal pure returns (uint) {
              return mod(a, b, "mod: %");
          }
      
          /**
           * @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.
           */
          function mod(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      contract ReentrancyGuard {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
      
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant _NOT_ENTERED = 1;
          uint256 private constant _ENTERED = 2;
      
          uint256 private _status;
      
          constructor () internal {
              _status = _NOT_ENTERED;
          }
      
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and make it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              // On the first call to nonReentrant, _notEntered will be true
              require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
      
              // Any calls to nonReentrant after this point will fail
              _status = _ENTERED;
      
              _;
      
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              _status = _NOT_ENTERED;
          }
      }
      
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
      
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
      
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: 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
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
      
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
      
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
              // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
              // for accounts without code, i.e. `keccak256('')`
              bytes32 codehash;
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              // solhint-disable-next-line no-inline-assembly
              assembly { codehash := extcodehash(account) }
              return (codehash != accountHash && codehash != 0x0);
          }
      
          /**
           * @dev Converts an `address` into `address payable`. Note that this is
           * simply a type cast: the actual underlying value is not changed.
           *
           * _Available since v2.4.0._
           */
          function toPayable(address account) internal pure returns (address payable) {
              return address(uint160(account));
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           *
           * _Available since v2.4.0._
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient");
      
              // solhint-disable-next-line avoid-call-value
              (bool success, ) = recipient.call{value:amount}("");
              require(success, "Address: reverted");
          }
      }
      
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using SafeMath for uint256;
          using Address for address;
      
          function safeTransfer(IERC20 token, address to, uint256 value) internal {
              callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
      
          function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
              callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
      
          function safeApprove(IERC20 token, address spender, uint256 value) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              // solhint-disable-next-line max-line-length
              require((value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
      
          function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 newAllowance = token.allowance(address(this), spender).add(value);
              callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
      
          function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: < 0");
              callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
      
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves.
      
              // A Solidity high level call has three parts:
              //  1. The target address is checked to verify it contains contract code
              //  2. The call itself is made, and success asserted
              //  3. The return value is decoded, which in turn checks the size of the returned data.
              // solhint-disable-next-line max-line-length
              require(address(token).isContract(), "SafeERC20: !contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = address(token).call(data);
              require(success, "SafeERC20: low-level call failed");
      
              if (returndata.length > 0) { // Return data is optional
                  // solhint-disable-next-line max-line-length
                  require(abi.decode(returndata, (bool)), "SafeERC20: !succeed");
              }
          }
      }
      
      library Keep3rV1Library {
          function getReserve(address pair, address reserve) external view returns (uint) {
              (uint _r0, uint _r1,) = IUniswapV2Pair(pair).getReserves();
              if (IUniswapV2Pair(pair).token0() == reserve) {
                  return _r0;
              } else if (IUniswapV2Pair(pair).token1() == reserve) {
                  return _r1;
              } else {
                  return 0;
              }
          }
      }
      
      interface IUniswapV2Pair {
          event Approval(address indexed owner, address indexed spender, uint value);
          event Transfer(address indexed from, address indexed to, uint value);
      
          function name() external pure returns (string memory);
          function symbol() external pure returns (string memory);
          function decimals() external pure returns (uint8);
          function totalSupply() external view returns (uint);
          function balanceOf(address owner) external view returns (uint);
          function allowance(address owner, address spender) external view returns (uint);
      
          function approve(address spender, uint value) external returns (bool);
          function transfer(address to, uint value) external returns (bool);
          function transferFrom(address from, address to, uint value) external returns (bool);
      
          function DOMAIN_SEPARATOR() external view returns (bytes32);
          function PERMIT_TYPEHASH() external pure returns (bytes32);
          function nonces(address owner) external view returns (uint);
      
          function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
      
          event Mint(address indexed sender, uint amount0, uint amount1);
          event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
          event Swap(
              address indexed sender,
              uint amount0In,
              uint amount1In,
              uint amount0Out,
              uint amount1Out,
              address indexed to
          );
          event Sync(uint112 reserve0, uint112 reserve1);
      
          function MINIMUM_LIQUIDITY() external pure returns (uint);
          function factory() external view returns (address);
          function token0() external view returns (address);
          function token1() external view returns (address);
          function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
          function price0CumulativeLast() external view returns (uint);
          function price1CumulativeLast() external view returns (uint);
          function kLast() external view returns (uint);
      
          function mint(address to) external returns (uint liquidity);
          function burn(address to) external returns (uint amount0, uint amount1);
          function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
          function skim(address to) external;
          function sync() external;
      
          function initialize(address, address) external;
      }
      
      interface IGovernance {
          function proposeJob(address job) external;
      }
      
      interface IKeep3rV1Helper {
          function getQuoteLimit(uint gasUsed) external view returns (uint);
      }
      
      contract Keep3rV1 is ReentrancyGuard {
          using SafeMath for uint;
          using SafeERC20 for IERC20;
      
          /// @notice Keep3r Helper to set max prices for the ecosystem
          IKeep3rV1Helper public KPRH;
      
          /// @notice EIP-20 token name for this token
          string public constant name = "Keep3rV1";
      
          /// @notice EIP-20 token symbol for this token
          string public constant symbol = "KP3R";
      
          /// @notice EIP-20 token decimals for this token
          uint8 public constant decimals = 18;
      
          /// @notice Total number of tokens in circulation
          uint public totalSupply = 0; // Initial 0
      
          /// @notice A record of each accounts delegate
          mapping (address => address) public delegates;
      
          /// @notice A record of votes checkpoints for each account, by index
          mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
      
          /// @notice The number of checkpoints for each account
          mapping (address => uint32) public numCheckpoints;
      
          mapping (address => mapping (address => uint)) internal allowances;
          mapping (address => uint) internal balances;
      
          /// @notice The EIP-712 typehash for the contract's domain
          bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint chainId,address verifyingContract)");
          bytes32 public immutable DOMAINSEPARATOR;
      
          /// @notice The EIP-712 typehash for the delegation struct used by the contract
          bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint nonce,uint expiry)");
      
          /// @notice The EIP-712 typehash for the permit struct used by the contract
          bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint value,uint nonce,uint deadline)");
      
      
          /// @notice A record of states for signing / validating signatures
          mapping (address => uint) public nonces;
      
          /// @notice An event thats emitted when an account changes its delegate
          event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
      
          /// @notice An event thats emitted when a delegate account's vote balance changes
          event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
      
          /// @notice A checkpoint for marking number of votes from a given block
          struct Checkpoint {
              uint32 fromBlock;
              uint votes;
          }
      
          /**
           * @notice Delegate votes from `msg.sender` to `delegatee`
           * @param delegatee The address to delegate votes to
           */
          function delegate(address delegatee) public {
              _delegate(msg.sender, delegatee);
          }
      
          /**
           * @notice Delegates votes from signatory to `delegatee`
           * @param delegatee The address to delegate votes to
           * @param nonce The contract state required to match the signature
           * @param expiry The time at which to expire the signature
           * @param v The recovery byte of the signature
           * @param r Half of the ECDSA signature pair
           * @param s Half of the ECDSA signature pair
           */
          function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public {
              bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
              bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAINSEPARATOR, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), "delegateBySig: sig");
              require(nonce == nonces[signatory]++, "delegateBySig: nonce");
              require(now <= expiry, "delegateBySig: expired");
              _delegate(signatory, delegatee);
          }
      
          /**
           * @notice Gets the current votes balance for `account`
           * @param account The address to get votes balance
           * @return The number of current votes for `account`
           */
          function getCurrentVotes(address account) external view returns (uint) {
              uint32 nCheckpoints = numCheckpoints[account];
              return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
          }
      
          /**
           * @notice Determine the prior number of votes for an account as of a block number
           * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
           * @param account The address of the account to check
           * @param blockNumber The block number to get the vote balance at
           * @return The number of votes the account had as of the given block
           */
          function getPriorVotes(address account, uint blockNumber) public view returns (uint) {
              require(blockNumber < block.number, "getPriorVotes:");
      
              uint32 nCheckpoints = numCheckpoints[account];
              if (nCheckpoints == 0) {
                  return 0;
              }
      
              // First check most recent balance
              if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                  return checkpoints[account][nCheckpoints - 1].votes;
              }
      
              // Next check implicit zero balance
              if (checkpoints[account][0].fromBlock > blockNumber) {
                  return 0;
              }
      
              uint32 lower = 0;
              uint32 upper = nCheckpoints - 1;
              while (upper > lower) {
                  uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                  Checkpoint memory cp = checkpoints[account][center];
                  if (cp.fromBlock == blockNumber) {
                      return cp.votes;
                  } else if (cp.fromBlock < blockNumber) {
                      lower = center;
                  } else {
                      upper = center - 1;
                  }
              }
              return checkpoints[account][lower].votes;
          }
      
          function _delegate(address delegator, address delegatee) internal {
              address currentDelegate = delegates[delegator];
              uint delegatorBalance = votes[delegator].add(bonds[delegator][address(this)]);
              delegates[delegator] = delegatee;
      
              emit DelegateChanged(delegator, currentDelegate, delegatee);
      
              _moveDelegates(currentDelegate, delegatee, delegatorBalance);
          }
      
          function _moveDelegates(address srcRep, address dstRep, uint amount) internal {
              if (srcRep != dstRep && amount > 0) {
                  if (srcRep != address(0)) {
                      uint32 srcRepNum = numCheckpoints[srcRep];
                      uint srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                      uint srcRepNew = srcRepOld.sub(amount, "_moveVotes: underflows");
                      _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                  }
      
                  if (dstRep != address(0)) {
                      uint32 dstRepNum = numCheckpoints[dstRep];
                      uint dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                      uint dstRepNew = dstRepOld.add(amount);
                      _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                  }
              }
          }
      
          function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint oldVotes, uint newVotes) internal {
            uint32 blockNumber = safe32(block.number, "_writeCheckpoint: 32 bits");
      
            if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
                checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
            } else {
                checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
                numCheckpoints[delegatee] = nCheckpoints + 1;
            }
      
            emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
          }
      
          function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
              require(n < 2**32, errorMessage);
              return uint32(n);
          }
      
          /// @notice The standard EIP-20 transfer event
          event Transfer(address indexed from, address indexed to, uint amount);
      
          /// @notice The standard EIP-20 approval event
          event Approval(address indexed owner, address indexed spender, uint amount);
      
          /// @notice Submit a job
          event SubmitJob(address indexed job, address indexed liquidity, address indexed provider, uint block, uint credit);
      
          /// @notice Apply credit to a job
          event ApplyCredit(address indexed job, address indexed liquidity, address indexed provider, uint block, uint credit);
      
          /// @notice Remove credit for a job
          event RemoveJob(address indexed job, address indexed liquidity, address indexed provider, uint block, uint credit);
      
          /// @notice Unbond credit for a job
          event UnbondJob(address indexed job, address indexed liquidity, address indexed provider, uint block, uint credit);
      
          /// @notice Added a Job
          event JobAdded(address indexed job, uint block, address governance);
      
          /// @notice Removed a job
          event JobRemoved(address indexed job, uint block, address governance);
      
          /// @notice Worked a job
          event KeeperWorked(address indexed credit, address indexed job, address indexed keeper, uint block, uint amount);
      
          /// @notice Keeper bonding
          event KeeperBonding(address indexed keeper, uint block, uint active, uint bond);
      
          /// @notice Keeper bonded
          event KeeperBonded(address indexed keeper, uint block, uint activated, uint bond);
      
          /// @notice Keeper unbonding
          event KeeperUnbonding(address indexed keeper, uint block, uint deactive, uint bond);
      
          /// @notice Keeper unbound
          event KeeperUnbound(address indexed keeper, uint block, uint deactivated, uint bond);
      
          /// @notice Keeper slashed
          event KeeperSlashed(address indexed keeper, address indexed slasher, uint block, uint slash);
      
          /// @notice Keeper disputed
          event KeeperDispute(address indexed keeper, uint block);
      
          /// @notice Keeper resolved
          event KeeperResolved(address indexed keeper, uint block);
      
          event AddCredit(address indexed credit, address indexed job, address indexed creditor, uint block, uint amount);
      
          /// @notice 1 day to bond to become a keeper
          uint constant public BOND = 3 days;
          /// @notice 14 days to unbond to remove funds from being a keeper
          uint constant public UNBOND = 14 days;
          /// @notice 3 days till liquidity can be bound
          uint constant public LIQUIDITYBOND = 3 days;
      
          /// @notice direct liquidity fee 0.3%
          uint constant public FEE = 30;
          uint constant public BASE = 10000;
      
          /// @notice address used for ETH transfers
          address constant public ETH = address(0xE);
      
          /// @notice tracks all current bondings (time)
          mapping(address => mapping(address => uint)) public bondings;
          /// @notice tracks all current unbondings (time)
          mapping(address => mapping(address => uint)) public unbondings;
          /// @notice allows for partial unbonding
          mapping(address => mapping(address => uint)) public partialUnbonding;
          /// @notice tracks all current pending bonds (amount)
          mapping(address => mapping(address => uint)) public pendingbonds;
          /// @notice tracks how much a keeper has bonded
          mapping(address => mapping(address => uint)) public bonds;
          /// @notice tracks underlying votes (that don't have bond)
          mapping(address => uint) public votes;
      
          /// @notice total bonded (totalSupply for bonds)
          uint public totalBonded = 0;
          /// @notice tracks when a keeper was first registered
          mapping(address => uint) public firstSeen;
      
          /// @notice tracks if a keeper has a pending dispute
          mapping(address => bool) public disputes;
      
          /// @notice tracks last job performed for a keeper
          mapping(address => uint) public lastJob;
          /// @notice tracks the total job executions for a keeper
          mapping(address => uint) public workCompleted;
          /// @notice list of all jobs registered for the keeper system
          mapping(address => bool) public jobs;
          /// @notice the current credit available for a job
          mapping(address => mapping(address => uint)) public credits;
          /// @notice the balances for the liquidity providers
          mapping(address => mapping(address => mapping(address => uint))) public liquidityProvided;
          /// @notice liquidity unbonding days
          mapping(address => mapping(address => mapping(address => uint))) public liquidityUnbonding;
          /// @notice liquidity unbonding amounts
          mapping(address => mapping(address => mapping(address => uint))) public liquidityAmountsUnbonding;
          /// @notice job proposal delay
          mapping(address => uint) public jobProposalDelay;
          /// @notice liquidity apply date
          mapping(address => mapping(address => mapping(address => uint))) public liquidityApplied;
          /// @notice liquidity amount to apply
          mapping(address => mapping(address => mapping(address => uint))) public liquidityAmount;
      
          /// @notice list of all current keepers
          mapping(address => bool) public keepers;
          /// @notice blacklist of keepers not allowed to participate
          mapping(address => bool) public blacklist;
      
          /// @notice traversable array of keepers to make external management easier
          address[] public keeperList;
          /// @notice traversable array of jobs to make external management easier
          address[] public jobList;
      
          /// @notice governance address for the governance contract
          address public governance;
          address public pendingGovernance;
      
          /// @notice the liquidity token supplied by users paying for jobs
          mapping(address => bool) public liquidityAccepted;
      
          address[] public liquidityPairs;
      
          uint internal _gasUsed;
      
          constructor(address _kph) public {
              // Set governance for this token
              governance = msg.sender;
              DOMAINSEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), _getChainId(), address(this)));
              KPRH = IKeep3rV1Helper(_kph);
          }
      
          /**
           * @notice Add ETH credit to a job to be paid out for work
           * @param job the job being credited
           */
          function addCreditETH(address job) external payable {
              require(jobs[job], "addCreditETH: !job");
              uint _fee = msg.value.mul(FEE).div(BASE);
              credits[job][ETH] = credits[job][ETH].add(msg.value.sub(_fee));
              payable(governance).transfer(_fee);
      
              emit AddCredit(ETH, job, msg.sender, block.number, msg.value);
          }
      
          /**
           * @notice Add credit to a job to be paid out for work
           * @param credit the credit being assigned to the job
           * @param job the job being credited
           * @param amount the amount of credit being added to the job
           */
          function addCredit(address credit, address job, uint amount) external nonReentrant {
              require(jobs[job], "addCreditETH: !job");
              uint _before = IERC20(credit).balanceOf(address(this));
              IERC20(credit).safeTransferFrom(msg.sender, address(this), amount);
              uint _received = IERC20(credit).balanceOf(address(this)).sub(_before);
              uint _fee = _received.mul(FEE).div(BASE);
              credits[job][credit] = credits[job][credit].add(_received.sub(_fee));
              IERC20(credit).safeTransfer(governance, _fee);
      
              emit AddCredit(credit, job, msg.sender, block.number, _received);
          }
      
          /**
           * @notice Add non transferable votes for governance
           * @param voter to add the votes to
           * @param amount of votes to add
           */
          function addVotes(address voter, uint amount) external {
              require(msg.sender == governance, "addVotes: !gov");
              _activate(voter, address(this));
              votes[voter] = votes[voter].add(amount);
              totalBonded = totalBonded.add(amount);
              _moveDelegates(address(0), delegates[voter], amount);
          }
      
          /**
           * @notice Remove non transferable votes for governance
           * @param voter to subtract the votes
           * @param amount of votes to remove
           */
          function removeVotes(address voter, uint amount) external {
              require(msg.sender == governance, "addVotes: !gov");
              votes[voter] = votes[voter].sub(amount);
              totalBonded = totalBonded.sub(amount);
              _moveDelegates(delegates[voter], address(0), amount);
          }
      
          /**
           * @notice Add credit to a job to be paid out for work
           * @param job the job being credited
           * @param amount the amount of credit being added to the job
           */
          function addKPRCredit(address job, uint amount) external {
              require(msg.sender == governance, "addKPRCredit: !gov");
              require(jobs[job], "addKPRCredit: !job");
              credits[job][address(this)] = credits[job][address(this)].add(amount);
              _mint(address(this), amount);
              emit AddCredit(address(this), job, msg.sender, block.number, amount);
          }
      
          /**
           * @notice Approve a liquidity pair for being accepted in future
           * @param liquidity the liquidity no longer accepted
           */
          function approveLiquidity(address liquidity) external {
              require(msg.sender == governance, "approveLiquidity: !gov");
              require(!liquidityAccepted[liquidity], "approveLiquidity: !pair");
              liquidityAccepted[liquidity] = true;
              liquidityPairs.push(liquidity);
          }
      
          /**
           * @notice Revoke a liquidity pair from being accepted in future
           * @param liquidity the liquidity no longer accepted
           */
          function revokeLiquidity(address liquidity) external {
              require(msg.sender == governance, "revokeLiquidity: !gov");
              liquidityAccepted[liquidity] = false;
          }
      
          /**
           * @notice Displays all accepted liquidity pairs
           */
          function pairs() external view returns (address[] memory) {
              return liquidityPairs;
          }
      
          /**
           * @notice Allows liquidity providers to submit jobs
           * @param liquidity the liquidity being added
           * @param job the job to assign credit to
           * @param amount the amount of liquidity tokens to use
           */
          function addLiquidityToJob(address liquidity, address job, uint amount) external nonReentrant {
              require(liquidityAccepted[liquidity], "addLiquidityToJob: !pair");
              IERC20(liquidity).safeTransferFrom(msg.sender, address(this), amount);
              liquidityProvided[msg.sender][liquidity][job] = liquidityProvided[msg.sender][liquidity][job].add(amount);
      
              liquidityApplied[msg.sender][liquidity][job] = now.add(LIQUIDITYBOND);
              liquidityAmount[msg.sender][liquidity][job] = liquidityAmount[msg.sender][liquidity][job].add(amount);
      
              if (!jobs[job] && jobProposalDelay[job] < now) {
                  IGovernance(governance).proposeJob(job);
                  jobProposalDelay[job] = now.add(UNBOND);
              }
              emit SubmitJob(job, liquidity, msg.sender, block.number, amount);
          }
      
          /**
           * @notice Applies the credit provided in addLiquidityToJob to the job
           * @param provider the liquidity provider
           * @param liquidity the pair being added as liquidity
           * @param job the job that is receiving the credit
           */
          function applyCreditToJob(address provider, address liquidity, address job) external {
              require(liquidityAccepted[liquidity], "addLiquidityToJob: !pair");
              require(liquidityApplied[provider][liquidity][job] != 0, "credit: no bond");
              require(liquidityApplied[provider][liquidity][job] < now, "credit: bonding");
              uint _liquidity = Keep3rV1Library.getReserve(liquidity, address(this));
              uint _credit = _liquidity.mul(liquidityAmount[provider][liquidity][job]).div(IERC20(liquidity).totalSupply());
              _mint(address(this), _credit);
              credits[job][address(this)] = credits[job][address(this)].add(_credit);
              liquidityAmount[provider][liquidity][job] = 0;
      
              emit ApplyCredit(job, liquidity, provider, block.number, _credit);
          }
      
          /**
           * @notice Unbond liquidity for a job
           * @param liquidity the pair being unbound
           * @param job the job being unbound from
           * @param amount the amount of liquidity being removed
           */
          function unbondLiquidityFromJob(address liquidity, address job, uint amount) external {
              require(liquidityAmount[msg.sender][liquidity][job] == 0, "credit: pending credit");
              liquidityUnbonding[msg.sender][liquidity][job] = now.add(UNBOND);
              liquidityAmountsUnbonding[msg.sender][liquidity][job] = liquidityAmountsUnbonding[msg.sender][liquidity][job].add(amount);
              require(liquidityAmountsUnbonding[msg.sender][liquidity][job] <= liquidityProvided[msg.sender][liquidity][job], "unbondLiquidityFromJob: insufficient funds");
      
              uint _liquidity = Keep3rV1Library.getReserve(liquidity, address(this));
              uint _credit = _liquidity.mul(amount).div(IERC20(liquidity).totalSupply());
              if (_credit > credits[job][address(this)]) {
                  _burn(address(this), credits[job][address(this)]);
                  credits[job][address(this)] = 0;
              } else {
                  _burn(address(this), _credit);
                  credits[job][address(this)] = credits[job][address(this)].sub(_credit);
              }
      
              emit UnbondJob(job, liquidity, msg.sender, block.number, amount);
          }
      
          /**
           * @notice Allows liquidity providers to remove liquidity
           * @param liquidity the pair being unbound
           * @param job the job being unbound from
           */
          function removeLiquidityFromJob(address liquidity, address job) external {
              require(liquidityUnbonding[msg.sender][liquidity][job] != 0, "removeJob: unbond");
              require(liquidityUnbonding[msg.sender][liquidity][job] < now, "removeJob: unbonding");
              uint _amount = liquidityAmountsUnbonding[msg.sender][liquidity][job];
              liquidityProvided[msg.sender][liquidity][job] = liquidityProvided[msg.sender][liquidity][job].sub(_amount);
              liquidityAmountsUnbonding[msg.sender][liquidity][job] = 0;
              IERC20(liquidity).safeTransfer(msg.sender, _amount);
      
              emit RemoveJob(job, liquidity, msg.sender, block.number, _amount);
          }
      
          /**
           * @notice Allows governance to mint new tokens to treasury
           * @param amount the amount of tokens to mint to treasury
           */
          function mint(uint amount) external {
              require(msg.sender == governance, "mint: !gov");
              _mint(governance, amount);
          }
      
          /**
           * @notice burn owned tokens
           * @param amount the amount of tokens to burn
           */
          function burn(uint amount) external {
              _burn(msg.sender, amount);
          }
      
          function _mint(address dst, uint amount) internal {
              // mint the amount
              totalSupply = totalSupply.add(amount);
              // transfer the amount to the recipient
              balances[dst] = balances[dst].add(amount);
              emit Transfer(address(0), dst, amount);
          }
      
          function _burn(address dst, uint amount) internal {
              require(dst != address(0), "_burn: zero address");
              balances[dst] = balances[dst].sub(amount, "_burn: exceeds balance");
              totalSupply = totalSupply.sub(amount);
              emit Transfer(dst, address(0), amount);
          }
      
          /**
           * @notice Implemented by jobs to show that a keeper performed work
           * @param keeper address of the keeper that performed the work
           */
          function worked(address keeper) external {
              workReceipt(keeper, KPRH.getQuoteLimit(_gasUsed.sub(gasleft())));
          }
      
          /**
           * @notice Implemented by jobs to show that a keeper performed work
           * @param keeper address of the keeper that performed the work
           * @param amount the reward that should be allocated
           */
          function workReceipt(address keeper, uint amount) public {
              require(jobs[msg.sender], "workReceipt: !job");
              require(amount <= KPRH.getQuoteLimit(_gasUsed.sub(gasleft())), "workReceipt: max limit");
              credits[msg.sender][address(this)] = credits[msg.sender][address(this)].sub(amount, "workReceipt: insuffient funds");
              lastJob[keeper] = now;
              _reward(keeper, amount);
              workCompleted[keeper] = workCompleted[keeper].add(amount);
              emit KeeperWorked(address(this), msg.sender, keeper, block.number, amount);
          }
      
          /**
           * @notice Implemented by jobs to show that a keeper performed work
           * @param credit the asset being awarded to the keeper
           * @param keeper address of the keeper that performed the work
           * @param amount the reward that should be allocated
           */
          function receipt(address credit, address keeper, uint amount) external {
              require(jobs[msg.sender], "receipt: !job");
              credits[msg.sender][credit] = credits[msg.sender][credit].sub(amount, "workReceipt: insuffient funds");
              lastJob[keeper] = now;
              IERC20(credit).safeTransfer(keeper, amount);
              emit KeeperWorked(credit, msg.sender, keeper, block.number, amount);
          }
      
          /**
           * @notice Implemented by jobs to show that a keeper performed work
           * @param keeper address of the keeper that performed the work
           * @param amount the amount of ETH sent to the keeper
           */
          function receiptETH(address keeper, uint amount) external {
              require(jobs[msg.sender], "receipt: !job");
              credits[msg.sender][ETH] = credits[msg.sender][ETH].sub(amount, "workReceipt: insuffient funds");
              lastJob[keeper] = now;
              payable(keeper).transfer(amount);
              emit KeeperWorked(ETH, msg.sender, keeper, block.number, amount);
          }
      
          function _reward(address _from, uint _amount) internal {
              bonds[_from][address(this)] = bonds[_from][address(this)].add(_amount);
              totalBonded = totalBonded.add(_amount);
              _moveDelegates(address(0), delegates[_from], _amount);
              emit Transfer(msg.sender, _from, _amount);
          }
      
          function _bond(address bonding, address _from, uint _amount) internal {
              bonds[_from][bonding] = bonds[_from][bonding].add(_amount);
              if (bonding == address(this)) {
                  totalBonded = totalBonded.add(_amount);
                  _moveDelegates(address(0), delegates[_from], _amount);
              }
          }
      
          function _unbond(address bonding, address _from, uint _amount) internal {
              bonds[_from][bonding] = bonds[_from][bonding].sub(_amount);
              if (bonding == address(this)) {
                  totalBonded = totalBonded.sub(_amount);
                  _moveDelegates(delegates[_from], address(0), _amount);
              }
      
          }
      
          /**
           * @notice Allows governance to add new job systems
           * @param job address of the contract for which work should be performed
           */
          function addJob(address job) external {
              require(msg.sender == governance, "addJob: !gov");
              require(!jobs[job], "addJob: job known");
              jobs[job] = true;
              jobList.push(job);
              emit JobAdded(job, block.number, msg.sender);
          }
      
          /**
           * @notice Full listing of all jobs ever added
           * @return array blob
           */
          function getJobs() external view returns (address[] memory) {
              return jobList;
          }
      
          /**
           * @notice Allows governance to remove a job from the systems
           * @param job address of the contract for which work should be performed
           */
          function removeJob(address job) external {
              require(msg.sender == governance, "removeJob: !gov");
              jobs[job] = false;
              emit JobRemoved(job, block.number, msg.sender);
          }
      
          /**
           * @notice Allows governance to change the Keep3rHelper for max spend
           * @param _kprh new helper address to set
           */
          function setKeep3rHelper(IKeep3rV1Helper _kprh) external {
              require(msg.sender == governance, "setKeep3rHelper: !gov");
              KPRH = _kprh;
          }
      
          /**
           * @notice Allows governance to change governance (for future upgradability)
           * @param _governance new governance address to set
           */
          function setGovernance(address _governance) external {
              require(msg.sender == governance, "setGovernance: !gov");
              pendingGovernance = _governance;
          }
      
          /**
           * @notice Allows pendingGovernance to accept their role as governance (protection pattern)
           */
          function acceptGovernance() external {
              require(msg.sender == pendingGovernance, "acceptGovernance: !pendingGov");
              governance = pendingGovernance;
          }
      
          /**
           * @notice confirms if the current keeper is registered, can be used for general (non critical) functions
           * @param keeper the keeper being investigated
           * @return true/false if the address is a keeper
           */
          function isKeeper(address keeper) external returns (bool) {
              _gasUsed = gasleft();
              return keepers[keeper];
          }
      
          /**
           * @notice confirms if the current keeper is registered and has a minimum bond, should be used for protected functions
           * @param keeper the keeper being investigated
           * @param minBond the minimum requirement for the asset provided in bond
           * @param earned the total funds earned in the keepers lifetime
           * @param age the age of the keeper in the system
           * @return true/false if the address is a keeper and has more than the bond
           */
          function isMinKeeper(address keeper, uint minBond, uint earned, uint age) external returns (bool) {
              _gasUsed = gasleft();
              return keepers[keeper]
                      && bonds[keeper][address(this)].add(votes[keeper]) >= minBond
                      && workCompleted[keeper] >= earned
                      && now.sub(firstSeen[keeper]) >= age;
          }
      
          /**
           * @notice confirms if the current keeper is registered and has a minimum bond, should be used for protected functions
           * @param keeper the keeper being investigated
           * @param bond the bound asset being evaluated
           * @param minBond the minimum requirement for the asset provided in bond
           * @param earned the total funds earned in the keepers lifetime
           * @param age the age of the keeper in the system
           * @return true/false if the address is a keeper and has more than the bond
           */
          function isBondedKeeper(address keeper, address bond, uint minBond, uint earned, uint age) external returns (bool) {
              _gasUsed = gasleft();
              return keepers[keeper]
                      && bonds[keeper][bond] >= minBond
                      && workCompleted[keeper] >= earned
                      && now.sub(firstSeen[keeper]) >= age;
          }
      
          /**
           * @notice begin the bonding process for a new keeper
           * @param bonding the asset being bound
           * @param amount the amount of bonding asset being bound
           */
          function bond(address bonding, uint amount) external nonReentrant {
              require(!blacklist[msg.sender], "bond: blacklisted");
              bondings[msg.sender][bonding] = now.add(BOND);
              if (bonding == address(this)) {
                  _transferTokens(msg.sender, address(this), amount);
              } else {
                  uint _before = IERC20(bonding).balanceOf(address(this));
                  IERC20(bonding).safeTransferFrom(msg.sender, address(this), amount);
                  amount = IERC20(bonding).balanceOf(address(this)).sub(_before);
              }
              pendingbonds[msg.sender][bonding] = pendingbonds[msg.sender][bonding].add(amount);
              emit KeeperBonding(msg.sender, block.number, bondings[msg.sender][bonding], amount);
          }
      
          /**
           * @notice get full list of keepers in the system
           */
          function getKeepers() external view returns (address[] memory) {
              return keeperList;
          }
      
          /**
           * @notice allows a keeper to activate/register themselves after bonding
           * @param bonding the asset being activated as bond collateral
           */
          function activate(address bonding) external {
              require(!blacklist[msg.sender], "activate: blacklisted");
              require(bondings[msg.sender][bonding] != 0 && bondings[msg.sender][bonding] < now, "activate: bonding");
              _activate(msg.sender, bonding);
          }
          
          function _activate(address keeper, address bonding) internal {
              if (firstSeen[keeper] == 0) {
                firstSeen[keeper] = now;
                keeperList.push(keeper);
                lastJob[keeper] = now;
              }
              keepers[keeper] = true;
              _bond(bonding, keeper, pendingbonds[keeper][bonding]);
              pendingbonds[keeper][bonding] = 0;
              emit KeeperBonded(keeper, block.number, block.timestamp, bonds[keeper][bonding]);
          }
      
          /**
           * @notice begin the unbonding process to stop being a keeper
           * @param bonding the asset being unbound
           * @param amount allows for partial unbonding
           */
          function unbond(address bonding, uint amount) external {
              unbondings[msg.sender][bonding] = now.add(UNBOND);
              _unbond(bonding, msg.sender, amount);
              partialUnbonding[msg.sender][bonding] = partialUnbonding[msg.sender][bonding].add(amount);
              emit KeeperUnbonding(msg.sender, block.number, unbondings[msg.sender][bonding], amount);
          }
      
          /**
           * @notice withdraw funds after unbonding has finished
           * @param bonding the asset to withdraw from the bonding pool
           */
          function withdraw(address bonding) external nonReentrant {
              require(unbondings[msg.sender][bonding] != 0 && unbondings[msg.sender][bonding] < now, "withdraw: unbonding");
              require(!disputes[msg.sender], "withdraw: disputes");
      
              if (bonding == address(this)) {
                  _transferTokens(address(this), msg.sender, partialUnbonding[msg.sender][bonding]);
              } else {
                  IERC20(bonding).safeTransfer(msg.sender, partialUnbonding[msg.sender][bonding]);
              }
              emit KeeperUnbound(msg.sender, block.number, block.timestamp, partialUnbonding[msg.sender][bonding]);
              partialUnbonding[msg.sender][bonding] = 0;
          }
      
          /**
           * @notice allows governance to create a dispute for a given keeper
           * @param keeper the address in dispute
           */
          function dispute(address keeper) external {
              require(msg.sender == governance, "dispute: !gov");
              disputes[keeper] = true;
              emit KeeperDispute(keeper, block.number);
          }
      
          /**
           * @notice allows governance to slash a keeper based on a dispute
           * @param bonded the asset being slashed
           * @param keeper the address being slashed
           * @param amount the amount being slashed
           */
          function slash(address bonded, address keeper, uint amount) public nonReentrant {
              require(msg.sender == governance, "slash: !gov");
              if (bonded == address(this)) {
                  _transferTokens(address(this), governance, amount);
              } else {
                  IERC20(bonded).safeTransfer(governance, amount);
              }
              _unbond(bonded, keeper, amount);
              disputes[keeper] = false;
              emit KeeperSlashed(keeper, msg.sender, block.number, amount);
          }
      
          /**
           * @notice blacklists a keeper from participating in the network
           * @param keeper the address being slashed
           */
          function revoke(address keeper) external {
              require(msg.sender == governance, "slash: !gov");
              keepers[keeper] = false;
              blacklist[keeper] = true;
              slash(address(this), keeper, bonds[keeper][address(this)]);
          }
      
          /**
           * @notice allows governance to resolve a dispute on a keeper
           * @param keeper the address cleared
           */
          function resolve(address keeper) external {
              require(msg.sender == governance, "resolve: !gov");
              disputes[keeper] = false;
              emit KeeperResolved(keeper, block.number);
          }
      
          /**
           * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
           * @param account The address of the account holding the funds
           * @param spender The address of the account spending the funds
           * @return The number of tokens approved
           */
          function allowance(address account, address spender) external view returns (uint) {
              return allowances[account][spender];
          }
      
          /**
           * @notice Approve `spender` to transfer up to `amount` from `src`
           * @dev This will overwrite the approval amount for `spender`
           *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
           * @param spender The address of the account which may transfer tokens
           * @param amount The number of tokens that are approved (2^256-1 means infinite)
           * @return Whether or not the approval succeeded
           */
          function approve(address spender, uint amount) public returns (bool) {
              allowances[msg.sender][spender] = amount;
      
              emit Approval(msg.sender, spender, amount);
              return true;
          }
      
          /**
           * @notice Triggers an approval from owner to spends
           * @param owner The address to approve from
           * @param spender The address to be approved
           * @param amount The number of tokens that are approved (2^256-1 means infinite)
           * @param deadline The time at which to expire the signature
           * @param v The recovery byte of the signature
           * @param r Half of the ECDSA signature pair
           * @param s Half of the ECDSA signature pair
           */
          function permit(address owner, address spender, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
              bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, nonces[owner]++, deadline));
              bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAINSEPARATOR, structHash));
              address signatory = ecrecover(digest, v, r, s);
              require(signatory != address(0), "permit: signature");
              require(signatory == owner, "permit: unauthorized");
              require(now <= deadline, "permit: expired");
      
              allowances[owner][spender] = amount;
      
              emit Approval(owner, spender, amount);
          }
      
          /**
           * @notice Get the number of tokens held by the `account`
           * @param account The address of the account to get the balance of
           * @return The number of tokens held
           */
          function balanceOf(address account) external view returns (uint) {
              return balances[account];
          }
      
          /**
           * @notice Transfer `amount` tokens from `msg.sender` to `dst`
           * @param dst The address of the destination account
           * @param amount The number of tokens to transfer
           * @return Whether or not the transfer succeeded
           */
          function transfer(address dst, uint amount) public returns (bool) {
              _transferTokens(msg.sender, dst, amount);
              return true;
          }
      
          /**
           * @notice Transfer `amount` tokens from `src` to `dst`
           * @param src The address of the source account
           * @param dst The address of the destination account
           * @param amount The number of tokens to transfer
           * @return Whether or not the transfer succeeded
           */
          function transferFrom(address src, address dst, uint amount) external returns (bool) {
              address spender = msg.sender;
              uint spenderAllowance = allowances[src][spender];
      
              if (spender != src && spenderAllowance != uint(-1)) {
                  uint newAllowance = spenderAllowance.sub(amount, "transferFrom: exceeds spender allowance");
                  allowances[src][spender] = newAllowance;
      
                  emit Approval(src, spender, newAllowance);
              }
      
              _transferTokens(src, dst, amount);
              return true;
          }
      
          function _transferTokens(address src, address dst, uint amount) internal {
              require(src != address(0), "_transferTokens: zero address");
              require(dst != address(0), "_transferTokens: zero address");
      
              balances[src] = balances[src].sub(amount, "_transferTokens: exceeds balance");
              balances[dst] = balances[dst].add(amount, "_transferTokens: overflows");
              emit Transfer(src, dst, amount);
          }
      
          function _getChainId() internal pure returns (uint) {
              uint chainId;
              assembly { chainId := chainid() }
              return chainId;
          }
      }

      File 2 of 3: 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 3 of 3: UniswapV2Pair
      // File: contracts/interfaces/IUniswapV2Pair.sol
      
      pragma solidity >=0.5.0;
      
      interface IUniswapV2Pair {
          event Approval(address indexed owner, address indexed spender, uint value);
          event Transfer(address indexed from, address indexed to, uint value);
      
          function name() external pure returns (string memory);
          function symbol() external pure returns (string memory);
          function decimals() external pure returns (uint8);
          function totalSupply() external view returns (uint);
          function balanceOf(address owner) external view returns (uint);
          function allowance(address owner, address spender) external view returns (uint);
      
          function approve(address spender, uint value) external returns (bool);
          function transfer(address to, uint value) external returns (bool);
          function transferFrom(address from, address to, uint value) external returns (bool);
      
          function DOMAIN_SEPARATOR() external view returns (bytes32);
          function PERMIT_TYPEHASH() external pure returns (bytes32);
          function nonces(address owner) external view returns (uint);
      
          function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
      
          event Mint(address indexed sender, uint amount0, uint amount1);
          event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
          event Swap(
              address indexed sender,
              uint amount0In,
              uint amount1In,
              uint amount0Out,
              uint amount1Out,
              address indexed to
          );
          event Sync(uint112 reserve0, uint112 reserve1);
      
          function MINIMUM_LIQUIDITY() external pure returns (uint);
          function factory() external view returns (address);
          function token0() external view returns (address);
          function token1() external view returns (address);
          function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
          function price0CumulativeLast() external view returns (uint);
          function price1CumulativeLast() external view returns (uint);
          function kLast() external view returns (uint);
      
          function mint(address to) external returns (uint liquidity);
          function burn(address to) external returns (uint amount0, uint amount1);
          function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
          function skim(address to) external;
          function sync() external;
      
          function initialize(address, address) external;
      }
      
      // File: contracts/interfaces/IUniswapV2ERC20.sol
      
      pragma solidity >=0.5.0;
      
      interface IUniswapV2ERC20 {
          event Approval(address indexed owner, address indexed spender, uint value);
          event Transfer(address indexed from, address indexed to, uint value);
      
          function name() external pure returns (string memory);
          function symbol() external pure returns (string memory);
          function decimals() external pure returns (uint8);
          function totalSupply() external view returns (uint);
          function balanceOf(address owner) external view returns (uint);
          function allowance(address owner, address spender) external view returns (uint);
      
          function approve(address spender, uint value) external returns (bool);
          function transfer(address to, uint value) external returns (bool);
          function transferFrom(address from, address to, uint value) external returns (bool);
      
          function DOMAIN_SEPARATOR() external view returns (bytes32);
          function PERMIT_TYPEHASH() external pure returns (bytes32);
          function nonces(address owner) external view returns (uint);
      
          function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
      }
      
      // File: contracts/libraries/SafeMath.sol
      
      pragma solidity =0.5.16;
      
      // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
      
      library SafeMath {
          function add(uint x, uint y) internal pure returns (uint z) {
              require((z = x + y) >= x, 'ds-math-add-overflow');
          }
      
          function sub(uint x, uint y) internal pure returns (uint z) {
              require((z = x - y) <= x, 'ds-math-sub-underflow');
          }
      
          function mul(uint x, uint y) internal pure returns (uint z) {
              require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
          }
      }
      
      // File: contracts/UniswapV2ERC20.sol
      
      pragma solidity =0.5.16;
      
      
      
      contract UniswapV2ERC20 is IUniswapV2ERC20 {
          using SafeMath for uint;
      
          string public constant name = 'Uniswap V2';
          string public constant symbol = 'UNI-V2';
          uint8 public constant decimals = 18;
          uint  public totalSupply;
          mapping(address => uint) public balanceOf;
          mapping(address => mapping(address => uint)) public allowance;
      
          bytes32 public DOMAIN_SEPARATOR;
          // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
          bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
          mapping(address => uint) public nonces;
      
          event Approval(address indexed owner, address indexed spender, uint value);
          event Transfer(address indexed from, address indexed to, uint value);
      
          constructor() public {
              uint chainId;
              assembly {
                  chainId := chainid
              }
              DOMAIN_SEPARATOR = keccak256(
                  abi.encode(
                      keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                      keccak256(bytes(name)),
                      keccak256(bytes('1')),
                      chainId,
                      address(this)
                  )
              );
          }
      
          function _mint(address to, uint value) internal {
              totalSupply = totalSupply.add(value);
              balanceOf[to] = balanceOf[to].add(value);
              emit Transfer(address(0), to, value);
          }
      
          function _burn(address from, uint value) internal {
              balanceOf[from] = balanceOf[from].sub(value);
              totalSupply = totalSupply.sub(value);
              emit Transfer(from, address(0), value);
          }
      
          function _approve(address owner, address spender, uint value) private {
              allowance[owner][spender] = value;
              emit Approval(owner, spender, value);
          }
      
          function _transfer(address from, address to, uint value) private {
              balanceOf[from] = balanceOf[from].sub(value);
              balanceOf[to] = balanceOf[to].add(value);
              emit Transfer(from, to, value);
          }
      
          function approve(address spender, uint value) external returns (bool) {
              _approve(msg.sender, spender, value);
              return true;
          }
      
          function transfer(address to, uint value) external returns (bool) {
              _transfer(msg.sender, to, value);
              return true;
          }
      
          function transferFrom(address from, address to, uint value) external returns (bool) {
              if (allowance[from][msg.sender] != uint(-1)) {
                  allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
              }
              _transfer(from, to, value);
              return true;
          }
      
          function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
              require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
              bytes32 digest = keccak256(
                  abi.encodePacked(
                      '\x19\x01',
                      DOMAIN_SEPARATOR,
                      keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                  )
              );
              address recoveredAddress = ecrecover(digest, v, r, s);
              require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
              _approve(owner, spender, value);
          }
      }
      
      // File: contracts/libraries/Math.sol
      
      pragma solidity =0.5.16;
      
      // a library for performing various math operations
      
      library Math {
          function min(uint x, uint y) internal pure returns (uint z) {
              z = x < y ? x : y;
          }
      
          // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
          function sqrt(uint y) internal pure returns (uint z) {
              if (y > 3) {
                  z = y;
                  uint x = y / 2 + 1;
                  while (x < z) {
                      z = x;
                      x = (y / x + x) / 2;
                  }
              } else if (y != 0) {
                  z = 1;
              }
          }
      }
      
      // File: contracts/libraries/UQ112x112.sol
      
      pragma solidity =0.5.16;
      
      // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
      
      // range: [0, 2**112 - 1]
      // resolution: 1 / 2**112
      
      library UQ112x112 {
          uint224 constant Q112 = 2**112;
      
          // encode a uint112 as a UQ112x112
          function encode(uint112 y) internal pure returns (uint224 z) {
              z = uint224(y) * Q112; // never overflows
          }
      
          // divide a UQ112x112 by a uint112, returning a UQ112x112
          function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
              z = x / uint224(y);
          }
      }
      
      // File: contracts/interfaces/IERC20.sol
      
      pragma solidity >=0.5.0;
      
      interface IERC20 {
          event Approval(address indexed owner, address indexed spender, uint value);
          event Transfer(address indexed from, address indexed to, uint value);
      
          function name() external view returns (string memory);
          function symbol() external view returns (string memory);
          function decimals() external view returns (uint8);
          function totalSupply() external view returns (uint);
          function balanceOf(address owner) external view returns (uint);
          function allowance(address owner, address spender) external view returns (uint);
      
          function approve(address spender, uint value) external returns (bool);
          function transfer(address to, uint value) external returns (bool);
          function transferFrom(address from, address to, uint value) external returns (bool);
      }
      
      // File: contracts/interfaces/IUniswapV2Factory.sol
      
      pragma solidity >=0.5.0;
      
      interface IUniswapV2Factory {
          event PairCreated(address indexed token0, address indexed token1, address pair, uint);
      
          function feeTo() external view returns (address);
          function feeToSetter() external view returns (address);
      
          function getPair(address tokenA, address tokenB) external view returns (address pair);
          function allPairs(uint) external view returns (address pair);
          function allPairsLength() external view returns (uint);
      
          function createPair(address tokenA, address tokenB) external returns (address pair);
      
          function setFeeTo(address) external;
          function setFeeToSetter(address) external;
      }
      
      // File: contracts/interfaces/IUniswapV2Callee.sol
      
      pragma solidity >=0.5.0;
      
      interface IUniswapV2Callee {
          function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
      }
      
      // File: contracts/UniswapV2Pair.sol
      
      pragma solidity =0.5.16;
      
      
      
      
      
      
      
      
      contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
          using SafeMath  for uint;
          using UQ112x112 for uint224;
      
          uint public constant MINIMUM_LIQUIDITY = 10**3;
          bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
      
          address public factory;
          address public token0;
          address public token1;
      
          uint112 private reserve0;           // uses single storage slot, accessible via getReserves
          uint112 private reserve1;           // uses single storage slot, accessible via getReserves
          uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
      
          uint public price0CumulativeLast;
          uint public price1CumulativeLast;
          uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
      
          uint private unlocked = 1;
          modifier lock() {
              require(unlocked == 1, 'UniswapV2: LOCKED');
              unlocked = 0;
              _;
              unlocked = 1;
          }
      
          function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
              _reserve0 = reserve0;
              _reserve1 = reserve1;
              _blockTimestampLast = blockTimestampLast;
          }
      
          function _safeTransfer(address token, address to, uint value) private {
              (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
              require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
          }
      
          event Mint(address indexed sender, uint amount0, uint amount1);
          event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
          event Swap(
              address indexed sender,
              uint amount0In,
              uint amount1In,
              uint amount0Out,
              uint amount1Out,
              address indexed to
          );
          event Sync(uint112 reserve0, uint112 reserve1);
      
          constructor() public {
              factory = msg.sender;
          }
      
          // called once by the factory at time of deployment
          function initialize(address _token0, address _token1) external {
              require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
              token0 = _token0;
              token1 = _token1;
          }
      
          // update reserves and, on the first call per block, price accumulators
          function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
              require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
              uint32 blockTimestamp = uint32(block.timestamp % 2**32);
              uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
              if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                  // * never overflows, and + overflow is desired
                  price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                  price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
              }
              reserve0 = uint112(balance0);
              reserve1 = uint112(balance1);
              blockTimestampLast = blockTimestamp;
              emit Sync(reserve0, reserve1);
          }
      
          // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
          function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
              address feeTo = IUniswapV2Factory(factory).feeTo();
              feeOn = feeTo != address(0);
              uint _kLast = kLast; // gas savings
              if (feeOn) {
                  if (_kLast != 0) {
                      uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                      uint rootKLast = Math.sqrt(_kLast);
                      if (rootK > rootKLast) {
                          uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                          uint denominator = rootK.mul(5).add(rootKLast);
                          uint liquidity = numerator / denominator;
                          if (liquidity > 0) _mint(feeTo, liquidity);
                      }
                  }
              } else if (_kLast != 0) {
                  kLast = 0;
              }
          }
      
          // this low-level function should be called from a contract which performs important safety checks
          function mint(address to) external lock returns (uint liquidity) {
              (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
              uint balance0 = IERC20(token0).balanceOf(address(this));
              uint balance1 = IERC20(token1).balanceOf(address(this));
              uint amount0 = balance0.sub(_reserve0);
              uint amount1 = balance1.sub(_reserve1);
      
              bool feeOn = _mintFee(_reserve0, _reserve1);
              uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
              if (_totalSupply == 0) {
                  liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                 _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
              } else {
                  liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
              }
              require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
              _mint(to, liquidity);
      
              _update(balance0, balance1, _reserve0, _reserve1);
              if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
              emit Mint(msg.sender, amount0, amount1);
          }
      
          // this low-level function should be called from a contract which performs important safety checks
          function burn(address to) external lock returns (uint amount0, uint amount1) {
              (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
              address _token0 = token0;                                // gas savings
              address _token1 = token1;                                // gas savings
              uint balance0 = IERC20(_token0).balanceOf(address(this));
              uint balance1 = IERC20(_token1).balanceOf(address(this));
              uint liquidity = balanceOf[address(this)];
      
              bool feeOn = _mintFee(_reserve0, _reserve1);
              uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
              amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
              amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
              require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
              _burn(address(this), liquidity);
              _safeTransfer(_token0, to, amount0);
              _safeTransfer(_token1, to, amount1);
              balance0 = IERC20(_token0).balanceOf(address(this));
              balance1 = IERC20(_token1).balanceOf(address(this));
      
              _update(balance0, balance1, _reserve0, _reserve1);
              if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
              emit Burn(msg.sender, amount0, amount1, to);
          }
      
          // this low-level function should be called from a contract which performs important safety checks
          function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
              require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
              (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
              require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
      
              uint balance0;
              uint balance1;
              { // scope for _token{0,1}, avoids stack too deep errors
              address _token0 = token0;
              address _token1 = token1;
              require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
              if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
              if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
              if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
              balance0 = IERC20(_token0).balanceOf(address(this));
              balance1 = IERC20(_token1).balanceOf(address(this));
              }
              uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
              uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
              require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
              { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
              uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
              uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
              require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
              }
      
              _update(balance0, balance1, _reserve0, _reserve1);
              emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
          }
      
          // force balances to match reserves
          function skim(address to) external lock {
              address _token0 = token0; // gas savings
              address _token1 = token1; // gas savings
              _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
              _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
          }
      
          // force reserves to match balances
          function sync() external lock {
              _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
          }
      }