ETH Price: $2,472.51 (+0.78%)

Transaction Decoder

Block:
15256376 at Aug-01-2022 11:28:27 AM +UTC
Transaction Fee:
0.000220083954418592 ETH $0.54
Gas Used:
32,897 Gas / 6.690091936 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
0x01e66950...03E2ef5Ab
0.274501706977245164 Eth
Nonce: 489
0.274281623022826572 Eth
Nonce: 490
0.000220083954418592
0xe89bBC13...66C452857
(Ethermine)
571.711455263513061209 Eth571.711504609013061209 Eth0.0000493455

Execution Trace

BPool.transfer( dst=0xDF1c35c3d5178e9d98043b35a6737Bd861c191c9, amt=80000000000000000000 ) => ( True )
  • BPool.transfer( dst=0xDF1c35c3d5178e9d98043b35a6737Bd861c191c9, amt=80000000000000000000 ) => ( True )
    File 1 of 2: BPool
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    contract BConst {
        uint public constant BONE              = 10**18;
        uint public constant MIN_BOUND_TOKENS  = 2;
        uint public constant MAX_BOUND_TOKENS  = 8;
        uint public constant MIN_FEE           = BONE / 10**6;
        uint public constant MAX_FEE           = BONE / 10;
        uint public constant EXIT_FEE          = 0;
        uint public constant MIN_WEIGHT        = BONE;
        uint public constant MAX_WEIGHT        = BONE * 50;
        uint public constant MAX_TOTAL_WEIGHT  = BONE * 50;
        uint public constant MIN_BALANCE       = BONE / 10**12;
        uint public constant INIT_POOL_SUPPLY  = BONE * 100;
        uint public constant MIN_BPOW_BASE     = 1 wei;
        uint public constant MAX_BPOW_BASE     = (2 * BONE) - 1 wei;
        uint public constant BPOW_PRECISION    = BONE / 10**10;
        uint public constant MAX_IN_RATIO      = BONE / 2;
        uint public constant MAX_OUT_RATIO     = (BONE / 3) + 1 wei;
    }
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    import './BNum.sol';
    contract BMath is BConst, BNum {
        /**********************************************************************************************
        // calcSpotPrice                                                                             //
        // sP = spotPrice                                                                            //
        // bI = tokenBalanceIn                ( bI / wI )         1                                  //
        // bO = tokenBalanceOut         sP =  -----------  *  ----------                             //
        // wI = tokenWeightIn                 ( bO / wO )     ( 1 - sF )                             //
        // wO = tokenWeightOut                                                                       //
        // sF = swapFee                                                                              //
        **********************************************************************************************/
        function calcSpotPrice(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint swapFee
        )
            public pure
            returns (uint spotPrice)
        {
            uint numer = bdiv(tokenBalanceIn, tokenWeightIn);
            uint denom = bdiv(tokenBalanceOut, tokenWeightOut);
            uint ratio = bdiv(numer, denom);
            uint scale = bdiv(BONE, bsub(BONE, swapFee));
            return  (spotPrice = bmul(ratio, scale));
        }
        /**********************************************************************************************
        // calcOutGivenIn                                                                            //
        // aO = tokenAmountOut                                                                       //
        // bO = tokenBalanceOut                                                                      //
        // bI = tokenBalanceIn              /      /            bI             \\    (wI / wO) \\      //
        // aI = tokenAmountIn    aO = bO * |  1 - | --------------------------  | ^            |     //
        // wI = tokenWeightIn               \\      \\ ( bI + ( aI * ( 1 - sF )) /              /      //
        // wO = tokenWeightOut                                                                       //
        // sF = swapFee                                                                              //
        **********************************************************************************************/
        function calcOutGivenIn(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint tokenAmountIn,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountOut)
        {
            uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
            uint adjustedIn = bsub(BONE, swapFee);
            adjustedIn = bmul(tokenAmountIn, adjustedIn);
            uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
            uint foo = bpow(y, weightRatio);
            uint bar = bsub(BONE, foo);
            tokenAmountOut = bmul(tokenBalanceOut, bar);
            return tokenAmountOut;
        }
        /**********************************************************************************************
        // calcInGivenOut                                                                            //
        // aI = tokenAmountIn                                                                        //
        // bO = tokenBalanceOut               /  /     bO      \\    (wO / wI)      \\                 //
        // bI = tokenBalanceIn          bI * |  | ------------  | ^            - 1  |                //
        // aO = tokenAmountOut    aI =        \\  \\ ( bO - aO ) /                   /                 //
        // wI = tokenWeightIn           --------------------------------------------                 //
        // wO = tokenWeightOut                          ( 1 - sF )                                   //
        // sF = swapFee                                                                              //
        **********************************************************************************************/
        function calcInGivenOut(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint tokenAmountOut,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountIn)
        {
            uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
            uint diff = bsub(tokenBalanceOut, tokenAmountOut);
            uint y = bdiv(tokenBalanceOut, diff);
            uint foo = bpow(y, weightRatio);
            foo = bsub(foo, BONE);
            tokenAmountIn = bsub(BONE, swapFee);
            tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);
            return tokenAmountIn;
        }
        /**********************************************************************************************
        // calcPoolOutGivenSingleIn                                                                  //
        // pAo = poolAmountOut         /                                              \\              //
        // tAi = tokenAmountIn        ///      /     //    wI \\      \\\\       \\     wI \\             //
        // wI = tokenWeightIn        //| tAi *| 1 - || 1 - --  | * sF || + tBi \\    --  \\            //
        // tW = totalWeight     pAo=||  \\      \\     \\\\    tW /      //         | ^ tW   | * pS - pS //
        // tBi = tokenBalanceIn      \\\\  ------------------------------------- /        /            //
        // pS = poolSupply            \\\\                    tBi               /        /             //
        // sF = swapFee                \\                                              /              //
        **********************************************************************************************/
        function calcPoolOutGivenSingleIn(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint poolSupply,
            uint totalWeight,
            uint tokenAmountIn,
            uint swapFee
        )
            public pure
            returns (uint poolAmountOut)
        {
            // Charge the trading fee for the proportion of tokenAi
            ///  which is implicitly traded to the other pool tokens.
            // That proportion is (1- weightTokenIn)
            // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
            uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
            uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); 
            uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));
            uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
            uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);
            // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;
            uint poolRatio = bpow(tokenInRatio, normalizedWeight);
            uint newPoolSupply = bmul(poolRatio, poolSupply);
            poolAmountOut = bsub(newPoolSupply, poolSupply);
            return poolAmountOut;
        }
        /**********************************************************************************************
        // calcSingleInGivenPoolOut                                                                  //
        // tAi = tokenAmountIn              //(pS + pAo)\\     /    1    \\\\                           //
        // pS = poolSupply                 || ---------  | ^ | --------- || * bI - bI                //
        // pAo = poolAmountOut              \\\\    pS    /     \\(wI / tW)//                           //
        // bI = balanceIn          tAi =  --------------------------------------------               //
        // wI = weightIn                              /      wI  \\                                   //
        // tW = totalWeight                          |  1 - ----  |  * sF                            //
        // sF = swapFee                               \\      tW  /                                   //
        **********************************************************************************************/
        function calcSingleInGivenPoolOut(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint poolSupply,
            uint totalWeight,
            uint poolAmountOut,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountIn)
        {
            uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
            uint newPoolSupply = badd(poolSupply, poolAmountOut);
            uint poolRatio = bdiv(newPoolSupply, poolSupply);
          
            //uint newBalTi = poolRatio^(1/weightTi) * balTi;
            uint boo = bdiv(BONE, normalizedWeight); 
            uint tokenInRatio = bpow(poolRatio, boo);
            uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
            uint tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);
            // Do reverse order of fees charged in joinswap_ExternAmountIn, this way 
            //     ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```
            //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;
            uint zar = bmul(bsub(BONE, normalizedWeight), swapFee);
            tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));
            return tokenAmountIn;
        }
        /**********************************************************************************************
        // calcSingleOutGivenPoolIn                                                                  //
        // tAo = tokenAmountOut            /      /                                             \\\\   //
        // bO = tokenBalanceOut           /      // pS - (pAi * (1 - eF)) \\     /    1    \\      \\\\  //
        // pAi = poolAmountIn            | bO - || ----------------------- | ^ | --------- | * b0 || //
        // ps = poolSupply                \\      \\\\          pS           /     \\(wO / tW)/      //  //
        // wI = tokenWeightIn      tAo =   \\      \\                                             //   //
        // tW = totalWeight                    /     /      wO \\       \\                             //
        // sF = swapFee                    *  | 1 - |  1 - ---- | * sF  |                            //
        // eF = exitFee                        \\     \\      tW /       /                             //
        **********************************************************************************************/
        function calcSingleOutGivenPoolIn(
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint poolSupply,
            uint totalWeight,
            uint poolAmountIn,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountOut)
        {
            uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
            // charge exit fee on the pool token side
            // pAiAfterExitFee = pAi*(1-exitFee)
            uint poolAmountInAfterExitFee = bmul(
                poolAmountIn, 
                bsub(BONE, EXIT_FEE)
            );
            uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
            uint poolRatio = bdiv(newPoolSupply, poolSupply);
         
            // newBalTo = poolRatio^(1/weightTo) * balTo;
            uint tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));
            uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);
            uint tokenAmountOutBeforeSwapFee = bsub(
                tokenBalanceOut, 
                newTokenBalanceOut
            );
            // charge swap fee on the output token side 
            //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)
            uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); 
            tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));
            return tokenAmountOut;
        }
        /**********************************************************************************************
        // calcPoolInGivenSingleOut                                                                  //
        // pAi = poolAmountIn               // /               tAo             \\\\     / wO \\     \\   //
        // bO = tokenBalanceOut            // | bO - -------------------------- |\\   | ---- |     \\  //
        // tAo = tokenAmountOut      pS - ||   \\     1 - ((1 - (tO / tW)) * sF)/  | ^ \\ tW /  * pS | //
        // ps = poolSupply                 \\\\ -----------------------------------/                /  //
        // wO = tokenWeightOut  pAi =       \\\\               bO                 /                /   //
        // tW = totalWeight           -------------------------------------------------------------  //
        // sF = swapFee                                        ( 1 - eF )                            //
        // eF = exitFee                                                                              //
        **********************************************************************************************/
        function calcPoolInGivenSingleOut(
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint poolSupply,
            uint totalWeight,
            uint tokenAmountOut,
            uint swapFee
        )
            public pure
            returns (uint poolAmountIn)
        {
            // charge swap fee on the output token side 
            uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
            //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;
            uint zoo = bsub(BONE, normalizedWeight);
            uint zar = bmul(zoo, swapFee); 
            uint tokenAmountOutBeforeSwapFee = bdiv(
                tokenAmountOut, 
                bsub(BONE, zar)
            );
            uint newTokenBalanceOut = bsub(
                tokenBalanceOut, 
                tokenAmountOutBeforeSwapFee
            );
            uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);
            //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;
            uint poolRatio = bpow(tokenOutRatio, normalizedWeight);
            uint newPoolSupply = bmul(poolRatio, poolSupply);
            uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);
            // charge exit fee on the pool token side
            // pAi = pAiAfterExitFee/(1-exitFee)
            poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));
            return poolAmountIn;
        }
    }
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    import './BConst.sol';
    contract BNum is BConst {
        function btoi(uint a)
            internal pure 
            returns (uint)
        {
            return a / BONE;
        }
        function bfloor(uint a)
            internal pure
            returns (uint)
        {
            return btoi(a) * BONE;
        }
        function badd(uint a, uint b)
            internal pure
            returns (uint)
        {
            uint c = a + b;
            require(c >= a, 'ERR_ADD_OVERFLOW');
            return c;
        }
        function bsub(uint a, uint b)
            internal pure
            returns (uint)
        {
            (uint c, bool flag) = bsubSign(a, b);
            require(!flag, 'ERR_SUB_UNDERFLOW');
            return c;
        }
        function bsubSign(uint a, uint b)
            internal pure
            returns (uint, bool)
        {
            if (a >= b) {
                return (a - b, false);
            } else {
                return (b - a, true);
            }
        }
        function bmul(uint a, uint b)
            internal pure
            returns (uint)
        {
            uint c0 = a * b;
            require(a == 0 || c0 / a == b, 'ERR_MUL_OVERFLOW');
            uint c1 = c0 + (BONE / 2);
            require(c1 >= c0, 'ERR_MUL_OVERFLOW');
            uint c2 = c1 / BONE;
            return c2;
        }
        function bdiv(uint a, uint b)
            internal pure
            returns (uint)
        {
            require(b != 0, 'ERR_DIV_ZERO');
            uint c0 = a * BONE;
            require(a == 0 || c0 / a == BONE, 'ERR_DIV_INTERNAL'); // bmul overflow
            uint c1 = c0 + (b / 2);
            require(c1 >= c0, 'ERR_DIV_INTERNAL'); //  badd require
            uint c2 = c1 / b;
            return c2;
        }
        // DSMath.wpow
        function bpowi(uint a, uint n)
            internal pure
            returns (uint)
        {
            uint b = a;
            uint z = n % 2 != 0 ? b : BONE;
            for (n /= 2; n != 0; n /= 2) {
                b = bmul(b, b);
                if (n % 2 != 0) {
                    z = bmul(z, b);
                }
            }
            return z;
        }
        // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
        // Use `bpowi` for `b^e` and `bpowK` for k iterations
        // of approximation of b^0.w
        function bpow(uint base, uint exp)
            internal pure
            returns (uint)
        {
            require(base >= MIN_BPOW_BASE, 'ERR_BPOW_BASE_TOO_LOW');
            require(base <= MAX_BPOW_BASE, 'ERR_BPOW_BASE_TOO_HIGH');
            uint whole  = bfloor(exp);   
            uint remain = bsub(exp, whole);
            uint wholePow = bpowi(base, btoi(whole));
            if (remain == 0) {
                return wholePow;
            }
            uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);
            return bmul(wholePow, partialResult);
        }
        function bpowApprox(uint base, uint exp, uint precision)
            internal pure
            returns (uint)
        {
            // term 0:
            uint a     = exp;
            (uint x, bool xneg)  = bsubSign(base, BONE);
            uint term = BONE;
            uint sum   = term;
            bool negative = false;
            // term(k) = numer / denom 
            //         = (product(a - i - 1, i=1-->k) * x^k) / (k!)
            // each iteration, multiply previous term by (a-(k-1)) * x / k
            // continue until term is less than precision
            for (uint i = 1; term >= precision; i++) {
                uint bigK = i * BONE;
                (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
                term = bmul(term, bmul(c, x));
                term = bdiv(term, bigK);
                if (term == 0) break;
                if (xneg) negative = !negative;
                if (cneg) negative = !negative;
                if (negative) {
                    sum = bsub(sum, term);
                } else {
                    sum = badd(sum, term);
                }
            }
            return sum;
        }
    }
    pragma solidity ^0.5.7;
    // Copyright BigchainDB GmbH and Ocean Protocol contributors
    // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
    // Code is Apache-2.0 and docs are CC-BY-4.0
    import './BToken.sol';
    import './BMath.sol';
    /**
    * @title BPool
    *  
    * @dev Used by the (Ocean version) BFactory contract as a bytecode reference to
    *      deploy new BPools.
    *
    * This contract is is nearly identical to the BPool.sol contract at [1]
    *  The only difference is the "Proxy contract functionality" section 
    *  given below. We'd inherit from BPool if we could, for simplicity.
    *  But we can't, because the proxy section needs to access private
    *  variables declared in BPool, and Solidity disallows this. Therefore
    *  the best we can do for now is clearly demarcate the proxy section. 
    *
    *  [1] https://github.com/balancer-labs/balancer-core/contracts/.
    */
    contract BPool is BToken, BMath {
        struct Record {
            bool bound;   // is token bound to pool
            uint index;   // private
            uint denorm;  // denormalized weight
            uint balance;
        }
        event LOG_SWAP(
            address indexed caller,
            address indexed tokenIn,
            address indexed tokenOut,
            uint256         tokenAmountIn,
            uint256         tokenAmountOut
        );
        event LOG_JOIN(
            address indexed caller,
            address indexed tokenIn,
            uint256         tokenAmountIn
        );
        event LOG_EXIT(
            address indexed caller,
            address indexed tokenOut,
            uint256         tokenAmountOut
        );
        event LOG_CALL(
            bytes4  indexed sig,
            address indexed caller,
            bytes           data
        ) anonymous;
        modifier _logs_() {
            emit LOG_CALL(msg.sig, msg.sender, msg.data);
            _;
        }
        modifier _lock_() {
            require(
                !_mutex, 
                'ERR_REENTRY'
            );
            _mutex = true;
            _;
            _mutex = false;
        }
        modifier _viewlock_() {
            require(!_mutex, 'ERR_REENTRY');
            _;
        }
        
        bool private _mutex;
        address private _factory;    // BFactory address to push token exitFee to
        address private _controller; // has CONTROL role
        bool private _publicSwap; // true if PUBLIC can call SWAP functions
        // `setSwapFee` and `finalize` require CONTROL
        // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`
        uint private _swapFee;
        bool private _finalized;
        address[] private _tokens;
        mapping(address=>Record) private  _records;
        uint private _totalWeight;
        //-----------------------------------------------------------------------
        //Proxy contract functionality: begin
        bool private initialized = false; 
        modifier onlyNotInitialized() {
            require(
                !initialized, 
                'ERR_ALREADY_INITIALIZED'
            );
            _;
        }
        function isInitialized() external view returns(bool) {
            return initialized;
        }
        
        // Called prior to contract deployment
        constructor() public {
            _initialize(msg.sender, msg.sender, MIN_FEE, false, false);
        }
        
        // Called prior to contract initialization (e.g creating new BPool instance)
        // Calls private _initialize function. Only if contract is not initialized.
        function initialize(
            address controller, 
            address factory, 
            uint swapFee,
            bool publicSwap,
            bool finalized
        )
            external
            onlyNotInitialized
            returns(bool)
        {
            require(
                controller != address(0),
                'ERR_INVALID_CONTROLLER_ADDRESS'
            );
            require(
                factory != address(0),
                'ERR_INVALID_FACTORY_ADDRESS'
            );
            require(swapFee >= MIN_FEE, 'ERR_MIN_FEE');
            require(swapFee <= MAX_FEE, 'ERR_MAX_FEE');
            return _initialize(controller, factory, swapFee, publicSwap, finalized);
        }
    \t
        // Private function called on contract initialization.
        function _initialize(
            address controller, 
            address factory, 
            uint swapFee,
            bool publicSwap, 
            bool finalized
        )
            private
            returns(bool)
        {
            _controller = controller;
            _factory = factory;
            _swapFee = swapFee;
            _publicSwap = publicSwap;
            _finalized = finalized;
    \t
            initialized = true;
            return initialized;
        }
        function setup(
            address dataTokenAaddress, 
            uint256 dataTokenAmount,
            uint256 dataTokenWeight,
            address baseTokenAddress, 
            uint256 baseTokenAmount,
            uint256 baseTokenWeight,
            uint256 swapFee
        )
            external
            _logs_
        {
            require(
                dataTokenAaddress != address(0),
                'ERR_INVALID_DATATOKEN_ADDRESS'
            );
            require(
                baseTokenAddress != address(0),
                'ERR_INVALID_BASETOKEN_ADDRESS'
            );
            // other inputs will be validated prior
            // calling the below functions
            // bind data token
            bind(
                dataTokenAaddress,
                dataTokenAmount,
                dataTokenWeight
            );
            emit LOG_JOIN(msg.sender, dataTokenAaddress, dataTokenAmount);
            // bind base token
            bind(
                baseTokenAddress,
                baseTokenAmount,
                baseTokenWeight
            );
            emit LOG_JOIN(msg.sender, baseTokenAddress, baseTokenAmount);
            setSwapFee(swapFee);
            // finalize
            finalize();
        }
        
        //Proxy contract functionality: end
        //-----------------------------------------------------------------------
        function isPublicSwap()
            external view
            returns (bool)
        {
            return _publicSwap;
        }
        function isFinalized()
            external view
            returns (bool)
        {
            return _finalized;
        }
        function isBound(address t)
            external view
            returns (bool)
        {
            return _records[t].bound;
        }
        function getNumTokens()
            external view
            returns (uint) 
        {
            return _tokens.length;
        }
        function getCurrentTokens()
            external view _viewlock_
            returns (address[] memory tokens)
        {
            return _tokens;
        }
        function getFinalTokens()
            external view
            _viewlock_
            returns (address[] memory tokens)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            return _tokens;
        }
        function getDenormalizedWeight(address token)
            external view
            _viewlock_
            returns (uint)
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            return _records[token].denorm;
        }
        function getTotalDenormalizedWeight()
            external view
            _viewlock_
            returns (uint)
        {
            return _totalWeight;
        }
        function getNormalizedWeight(address token)
            external view
            _viewlock_
            returns (uint)
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            uint denorm = _records[token].denorm;
            return bdiv(denorm, _totalWeight);
        }
        function getBalance(address token)
            external view
            _viewlock_
            returns (uint)
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            return _records[token].balance;
        }
        function getSwapFee()
            external view
            _viewlock_
            returns (uint)
        {
            return _swapFee;
        }
        function getController()
            external view
            _viewlock_
            returns (address)
        {
            return _controller;
        }
        function setSwapFee(uint swapFee)
            public
            _logs_
            _lock_
        { 
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(swapFee >= MIN_FEE, 'ERR_MIN_FEE');
            require(swapFee <= MAX_FEE, 'ERR_MAX_FEE');
            _swapFee = swapFee;
        }
        function setController(address manager)
            external
            _logs_
            _lock_
        {
            require(
                manager != address(0),
                'ERR_INVALID_MANAGER_ADDRESS'
            );
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            _controller = manager;
        }
    \t
        function setPublicSwap(bool public_)
            public
            _logs_
            _lock_
        {
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            _publicSwap = public_;
        }
        function finalize()
            public
            _logs_
            _lock_
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(_tokens.length >= MIN_BOUND_TOKENS, 'ERR_MIN_TOKENS');
            _finalized = true;
            _publicSwap = true;
            _mintPoolShare(INIT_POOL_SUPPLY);
            _pushPoolShare(msg.sender, INIT_POOL_SUPPLY);
        }
        function bind(address token, uint balance, uint denorm)
            public
            _logs_
            // _lock_  Bind does not lock because it jumps to `rebind`, which does
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(!_records[token].bound, 'ERR_IS_BOUND');
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(_tokens.length < MAX_BOUND_TOKENS, 'ERR_MAX_TOKENS');
            _records[token] = Record({
                bound: true,
                index: _tokens.length,
                denorm: 0,
                // balance and denorm will be validated
                balance: 0   // and set by `rebind`
            });
            _tokens.push(token);
            rebind(token, balance, denorm);
        }
        function rebind(address token, uint balance, uint denorm)
            public
            _logs_
            _lock_
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(_records[token].bound, 'ERR_NOT_BOUND');
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(denorm >= MIN_WEIGHT, 'ERR_MIN_WEIGHT');
            require(denorm <= MAX_WEIGHT, 'ERR_MAX_WEIGHT');
            require(balance >= MIN_BALANCE, 'ERR_MIN_BALANCE');
            // Adjust the denorm and totalWeight
            uint oldWeight = _records[token].denorm;
            if (denorm > oldWeight) {
                _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));
                require(_totalWeight <= MAX_TOTAL_WEIGHT, 'ERR_MAX_TOTAL_WEIGHT');
            } else if (denorm < oldWeight) {
                _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));
            }        
            _records[token].denorm = denorm;
            // Adjust the balance record and actual token balance
            uint oldBalance = _records[token].balance;
            _records[token].balance = balance;
            if (balance > oldBalance) {
                _pullUnderlying(token, msg.sender, bsub(balance, oldBalance));
            } else if (balance < oldBalance) {
                // In this case liquidity is being withdrawn, so charge EXIT_FEE
                uint tokenBalanceWithdrawn = bsub(oldBalance, balance);
                uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE);
                _pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee));
                _pushUnderlying(token, _factory, tokenExitFee);
            }
        }
        function unbind(address token)
            external
            _logs_
            _lock_
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(_records[token].bound, 'ERR_NOT_BOUND');
            require(!_finalized, 'ERR_IS_FINALIZED');
            uint tokenBalance = _records[token].balance;
            uint tokenExitFee = bmul(tokenBalance, EXIT_FEE);
            _totalWeight = bsub(_totalWeight, _records[token].denorm);
            // Swap the token-to-unbind with the last token,
            // then delete the last token
            uint index = _records[token].index;
            uint last = _tokens.length - 1;
            _tokens[index] = _tokens[last];
            _records[_tokens[index]].index = index;
            _tokens.pop();
            _records[token] = Record({
                bound: false,
                index: 0,
                denorm: 0,
                balance: 0
            });
            _pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee));
            _pushUnderlying(token, _factory, tokenExitFee);
        }
        // Absorb any tokens that have been sent to this contract into the pool
        function gulp(address token)
            external
            _logs_
            _lock_
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            _records[token].balance = IERC20(token).balanceOf(address(this));
        }
        function getSpotPrice(address tokenIn, address tokenOut)
            external view
            _viewlock_
            returns (uint spotPrice)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            Record storage inRecord = _records[tokenIn];
            Record storage outRecord = _records[tokenOut];
            return calcSpotPrice(
                inRecord.balance, 
                inRecord.denorm, 
                outRecord.balance, 
                outRecord.denorm, 
                _swapFee
            );
        }
        function getSpotPriceSansFee(address tokenIn, address tokenOut)
            external view
            _viewlock_
            returns (uint spotPrice)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            Record storage inRecord = _records[tokenIn];
            Record storage outRecord = _records[tokenOut];
            return calcSpotPrice(
                inRecord.balance, 
                inRecord.denorm, 
                outRecord.balance, 
                outRecord.denorm, 
                0
            );
        }
        function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn)
            external
            _logs_
            _lock_
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            uint poolTotal = totalSupply();
            uint ratio = bdiv(poolAmountOut, poolTotal);
            require(ratio != 0, 'ERR_MATH_APPROX');
            for (uint i = 0; i < _tokens.length; i++) {
                address t = _tokens[i];
                uint bal = _records[t].balance;
                uint tokenAmountIn = bmul(ratio, bal);
                require(tokenAmountIn != 0, 'ERR_MATH_APPROX');
                require(tokenAmountIn <= maxAmountsIn[i], 'ERR_LIMIT_IN');
                _records[t].balance = badd(_records[t].balance, tokenAmountIn);
                emit LOG_JOIN(msg.sender, t, tokenAmountIn);
                _pullUnderlying(t, msg.sender, tokenAmountIn);
            }
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(msg.sender, poolAmountOut);
        }
        function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut)
            external
            _logs_
            _lock_
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            uint poolTotal = totalSupply();
            uint exitFee = bmul(poolAmountIn, EXIT_FEE);
            uint pAiAfterExitFee = bsub(poolAmountIn, exitFee);
            uint ratio = bdiv(pAiAfterExitFee, poolTotal);
            require(ratio != 0, 'ERR_MATH_APPROX');
            _pullPoolShare(msg.sender, poolAmountIn);
            _pushPoolShare(_factory, exitFee);
            _burnPoolShare(pAiAfterExitFee);
            for (uint i = 0; i < _tokens.length; i++) {
                address t = _tokens[i];
                uint bal = _records[t].balance;
                uint tokenAmountOut = bmul(ratio, bal);
                require(tokenAmountOut != 0, 'ERR_MATH_APPROX');
                require(tokenAmountOut >= minAmountsOut[i], 'ERR_LIMIT_OUT');
                _records[t].balance = bsub(_records[t].balance, tokenAmountOut);
                emit LOG_EXIT(msg.sender, t, tokenAmountOut);
                _pushUnderlying(t, msg.sender, tokenAmountOut);
            }
        }
        function swapExactAmountIn(
            address tokenIn,
            uint tokenAmountIn,
            address tokenOut,
            uint minAmountOut,
            uint maxPrice
        )
            external
            _logs_
            _lock_
            returns (uint tokenAmountOut, uint spotPriceAfter)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            require(_publicSwap, 'ERR_SWAP_NOT_PUBLIC');
            Record storage inRecord = _records[address(tokenIn)];
            Record storage outRecord = _records[address(tokenOut)];
            require(
                tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), 
                'ERR_MAX_IN_RATIO'
            );
            uint spotPriceBefore = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            require(spotPriceBefore <= maxPrice, 'ERR_BAD_LIMIT_PRICE');
            tokenAmountOut = calcOutGivenIn(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                tokenAmountIn,
                _swapFee
            );
            require(tokenAmountOut >= minAmountOut, 'ERR_LIMIT_OUT');
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            spotPriceAfter = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            require(spotPriceAfter >= spotPriceBefore, 'ERR_MATH_APPROX');     
            require(spotPriceAfter <= maxPrice, 'ERR_LIMIT_PRICE');
            require(
                spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), 
                'ERR_MATH_APPROX'
            );
            emit LOG_SWAP(
                msg.sender, 
                tokenIn, 
                tokenOut, 
                tokenAmountIn, 
                tokenAmountOut
            );
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
            return (tokenAmountOut, spotPriceAfter);
        }
        function swapExactAmountOut(
            address tokenIn,
            uint maxAmountIn,
            address tokenOut,
            uint tokenAmountOut,
            uint maxPrice
        )
            external
            _logs_
            _lock_ 
            returns (uint tokenAmountIn, uint spotPriceAfter)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            require(_publicSwap, 'ERR_SWAP_NOT_PUBLIC');
            Record storage inRecord = _records[address(tokenIn)];
            Record storage outRecord = _records[address(tokenOut)];
            require(
                tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), 
                'ERR_MAX_OUT_RATIO'
            );
            uint spotPriceBefore = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            
            require(spotPriceBefore <= maxPrice, 'ERR_BAD_LIMIT_PRICE');
            tokenAmountIn = calcInGivenOut(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                tokenAmountOut,
                _swapFee
            );
            require(tokenAmountIn <= maxAmountIn, 'ERR_LIMIT_IN');
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            spotPriceAfter = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            require(spotPriceAfter >= spotPriceBefore, 'ERR_MATH_APPROX');
            require(spotPriceAfter <= maxPrice, 'ERR_LIMIT_PRICE');
            require(
                spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), 
                'ERR_MATH_APPROX'
            );
            emit LOG_SWAP(
                msg.sender, 
                tokenIn, 
                tokenOut, 
                tokenAmountIn, 
                tokenAmountOut
            );
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
            return (tokenAmountIn, spotPriceAfter);
        }
        function joinswapExternAmountIn(
            address tokenIn, 
            uint tokenAmountIn, 
            uint minPoolAmountOut
        )
            external
            _logs_
            _lock_
            returns (uint poolAmountOut)
        {        
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(
                tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), 
                'ERR_MAX_IN_RATIO'
            );
            Record storage inRecord = _records[tokenIn];
            poolAmountOut = calcPoolOutGivenSingleIn(
                inRecord.balance,
                inRecord.denorm,
                _totalSupply,
                _totalWeight,
                tokenAmountIn,
                _swapFee
            );
            require(poolAmountOut >= minPoolAmountOut, 'ERR_LIMIT_OUT');
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(msg.sender, poolAmountOut);
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            return poolAmountOut;
        }
        function joinswapPoolAmountOut(
            address tokenIn, 
            uint poolAmountOut, 
            uint maxAmountIn
        )
            external
            _logs_
            _lock_
            returns (uint tokenAmountIn)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            Record storage inRecord = _records[tokenIn];
            tokenAmountIn = calcSingleInGivenPoolOut(
                inRecord.balance,
                inRecord.denorm,
                _totalSupply,
                _totalWeight,
                poolAmountOut,
                _swapFee
            );
            require(tokenAmountIn != 0, 'ERR_MATH_APPROX');
            require(tokenAmountIn <= maxAmountIn, 'ERR_LIMIT_IN');
            
            require(
                tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), 
                'ERR_MAX_IN_RATIO'
            );
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(msg.sender, poolAmountOut);
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            return tokenAmountIn;
        }
        function exitswapPoolAmountIn(
            address tokenOut, 
            uint poolAmountIn, 
            uint minAmountOut
        )
            external
            _logs_
            _lock_
            returns (uint tokenAmountOut)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            Record storage outRecord = _records[tokenOut];
            tokenAmountOut = calcSingleOutGivenPoolIn(
                outRecord.balance,
                outRecord.denorm,
                _totalSupply,
                _totalWeight,
                poolAmountIn,
                _swapFee
            );
            require(tokenAmountOut >= minAmountOut, 'ERR_LIMIT_OUT');
            
            require(
                tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), 
                'ERR_MAX_OUT_RATIO'
            );
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            uint exitFee = bmul(poolAmountIn, EXIT_FEE);
            emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
            _pullPoolShare(msg.sender, poolAmountIn);
            _burnPoolShare(bsub(poolAmountIn, exitFee));
            _pushPoolShare(_factory, exitFee);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
            return tokenAmountOut;
        }
        function exitswapExternAmountOut(
            address tokenOut, 
            uint tokenAmountOut, 
            uint maxPoolAmountIn
        )
            external
            _logs_
            _lock_
            returns (uint poolAmountIn)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            require(
                tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), 
                'ERR_MAX_OUT_RATIO'
            );
            Record storage outRecord = _records[tokenOut];
            poolAmountIn = calcPoolInGivenSingleOut(
                outRecord.balance,
                outRecord.denorm,
                _totalSupply,
                _totalWeight,
                tokenAmountOut,
                _swapFee
            );
            require(poolAmountIn != 0, 'ERR_MATH_APPROX');
            require(poolAmountIn <= maxPoolAmountIn, 'ERR_LIMIT_IN');
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            uint exitFee = bmul(poolAmountIn, EXIT_FEE);
            emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
            _pullPoolShare(msg.sender, poolAmountIn);
            _burnPoolShare(bsub(poolAmountIn, exitFee));
            _pushPoolShare(_factory, exitFee);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);        
            return poolAmountIn;
        }
        // ==
        // 'Underlying' token-manipulation functions make external calls but are NOT locked
        // You must `_lock_` or otherwise ensure reentry-safety
        function _pullUnderlying(address erc20, address from, uint amount)
            internal
        {
            bool xfer = IERC20(erc20).transferFrom(from, address(this), amount);
            require(xfer, 'ERR_ERC20_FALSE');
        }
        function _pushUnderlying(address erc20, address to, uint amount)
            internal
        {
            bool xfer = IERC20(erc20).transfer(to, amount);
            require(xfer, 'ERR_ERC20_FALSE');
        }
        function _pullPoolShare(address from, uint amount)
            internal
        {
            _pull(from, amount);
        }
        function _pushPoolShare(address to, uint amount)
            internal
        {
            _push(to, amount);
        }
        function _mintPoolShare(uint amount)
            internal
        {
            _mint(amount);
        }
        function _burnPoolShare(uint amount)
            internal
        {
            _burn(amount);
        }
    }
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    import './BNum.sol';
    import 'openzeppelin-solidity/contracts/token/ERC20/IERC20.sol';
    // Highly opinionated token implementation
    // interface IERC20 {
    //     event Approval(address indexed src, address indexed dst, uint amt);
    //     event Transfer(address indexed src, address indexed dst, uint amt);
    //     function totalSupply() external view returns (uint);
    //     function balanceOf(address whom) external view returns (uint);
    //     function allowance(address src, address dst) external view returns (uint);
    //     function approve(address dst, uint amt) external returns (bool);
    //     function transfer(address dst, uint amt) external returns (bool);
    //     function transferFrom(
    //         address src, address dst, uint amt
    //     ) external returns (bool);
    // }
    contract BTokenBase is BNum {
        mapping(address => uint)                   internal _balance;
        mapping(address => mapping(address=>uint)) internal _allowance;
        uint internal _totalSupply;
        event Approval(address indexed src, address indexed dst, uint amt);
        event Transfer(address indexed src, address indexed dst, uint amt);
        function _mint(uint amt) internal {
            _balance[address(this)] = badd(_balance[address(this)], amt);
            _totalSupply = badd(_totalSupply, amt);
            emit Transfer(address(0), address(this), amt);
        }
        function _burn(uint amt) internal {
            require(
                _balance[address(this)] >= amt, 
                'ERR_INSUFFICIENT_BAL'
            );
            _balance[address(this)] = bsub(_balance[address(this)], amt);
            _totalSupply = bsub(_totalSupply, amt);
            emit Transfer(address(this), address(0), amt);
        }
        function _move(address src, address dst, uint amt) internal {
            require(_balance[src] >= amt, 'ERR_INSUFFICIENT_BAL');
            _balance[src] = bsub(_balance[src], amt);
            _balance[dst] = badd(_balance[dst], amt);
            emit Transfer(src, dst, amt);
        }
        function _push(address to, uint amt) internal {
            _move(address(this), to, amt);
        }
        function _pull(address from, uint amt) internal {
            _move(from, address(this), amt);
        }
    }
    contract BToken is BTokenBase, IERC20 {
        string  private _name     = 'Balancer Pool Token';
        string  private _symbol   = 'BPT';
        uint8   private _decimals = 18;
        function name() public view returns (string memory) {
            return _name;
        }
        function symbol() public view returns (string memory) {
            return _symbol;
        }
        function decimals() public view returns(uint8) {
            return _decimals;
        }
        function allowance(address src, address dst) external view returns (uint) {
            return _allowance[src][dst];
        }
        function balanceOf(address whom) external view returns (uint) {
            return _balance[whom];
        }
        function totalSupply() public view returns (uint) {
            return _totalSupply;
        }
        function approve(address dst, uint amt) external returns (bool) {
            _allowance[msg.sender][dst] = amt;
            emit Approval(msg.sender, dst, amt);
            return true;
        }
        function increaseApproval(address dst, uint amt) external returns (bool) {
            _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
            emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
            return true;
        }
        function decreaseApproval(address dst, uint amt) external returns (bool) {
            uint oldValue = _allowance[msg.sender][dst];
            if (amt > oldValue) {
                _allowance[msg.sender][dst] = 0;
            } else {
                _allowance[msg.sender][dst] = bsub(oldValue, amt);
            }
            emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
            return true;
        }
        function transfer(address dst, uint amt) external returns (bool) {
            _move(msg.sender, dst, amt);
            return true;
        }
        function transferFrom(
            address src, 
            address dst, 
            uint amt
        ) 
        external 
        returns (bool) 
        {
            require(
                msg.sender == src || amt <= _allowance[src][msg.sender], 
                'ERR_BTOKEN_BAD_CALLER'
            );
            _move(src, dst, amt);
            if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) {
                _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
                emit Approval(msg.sender, dst, _allowance[src][msg.sender]);
            }
            return true;
        }
    }
    pragma solidity ^0.5.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
     * the optional functions; to access them see {ERC20Detailed}.
     */
    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);
    }
    

    File 2 of 2: BPool
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    contract BConst {
        uint public constant BONE              = 10**18;
        uint public constant MIN_BOUND_TOKENS  = 2;
        uint public constant MAX_BOUND_TOKENS  = 8;
        uint public constant MIN_FEE           = BONE / 10**6;
        uint public constant MAX_FEE           = BONE / 10;
        uint public constant EXIT_FEE          = 0;
        uint public constant MIN_WEIGHT        = BONE;
        uint public constant MAX_WEIGHT        = BONE * 50;
        uint public constant MAX_TOTAL_WEIGHT  = BONE * 50;
        uint public constant MIN_BALANCE       = BONE / 10**12;
        uint public constant INIT_POOL_SUPPLY  = BONE * 100;
        uint public constant MIN_BPOW_BASE     = 1 wei;
        uint public constant MAX_BPOW_BASE     = (2 * BONE) - 1 wei;
        uint public constant BPOW_PRECISION    = BONE / 10**10;
        uint public constant MAX_IN_RATIO      = BONE / 2;
        uint public constant MAX_OUT_RATIO     = (BONE / 3) + 1 wei;
    }
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    import './BNum.sol';
    contract BMath is BConst, BNum {
        /**********************************************************************************************
        // calcSpotPrice                                                                             //
        // sP = spotPrice                                                                            //
        // bI = tokenBalanceIn                ( bI / wI )         1                                  //
        // bO = tokenBalanceOut         sP =  -----------  *  ----------                             //
        // wI = tokenWeightIn                 ( bO / wO )     ( 1 - sF )                             //
        // wO = tokenWeightOut                                                                       //
        // sF = swapFee                                                                              //
        **********************************************************************************************/
        function calcSpotPrice(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint swapFee
        )
            public pure
            returns (uint spotPrice)
        {
            uint numer = bdiv(tokenBalanceIn, tokenWeightIn);
            uint denom = bdiv(tokenBalanceOut, tokenWeightOut);
            uint ratio = bdiv(numer, denom);
            uint scale = bdiv(BONE, bsub(BONE, swapFee));
            return  (spotPrice = bmul(ratio, scale));
        }
        /**********************************************************************************************
        // calcOutGivenIn                                                                            //
        // aO = tokenAmountOut                                                                       //
        // bO = tokenBalanceOut                                                                      //
        // bI = tokenBalanceIn              /      /            bI             \\    (wI / wO) \\      //
        // aI = tokenAmountIn    aO = bO * |  1 - | --------------------------  | ^            |     //
        // wI = tokenWeightIn               \\      \\ ( bI + ( aI * ( 1 - sF )) /              /      //
        // wO = tokenWeightOut                                                                       //
        // sF = swapFee                                                                              //
        **********************************************************************************************/
        function calcOutGivenIn(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint tokenAmountIn,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountOut)
        {
            uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
            uint adjustedIn = bsub(BONE, swapFee);
            adjustedIn = bmul(tokenAmountIn, adjustedIn);
            uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
            uint foo = bpow(y, weightRatio);
            uint bar = bsub(BONE, foo);
            tokenAmountOut = bmul(tokenBalanceOut, bar);
            return tokenAmountOut;
        }
        /**********************************************************************************************
        // calcInGivenOut                                                                            //
        // aI = tokenAmountIn                                                                        //
        // bO = tokenBalanceOut               /  /     bO      \\    (wO / wI)      \\                 //
        // bI = tokenBalanceIn          bI * |  | ------------  | ^            - 1  |                //
        // aO = tokenAmountOut    aI =        \\  \\ ( bO - aO ) /                   /                 //
        // wI = tokenWeightIn           --------------------------------------------                 //
        // wO = tokenWeightOut                          ( 1 - sF )                                   //
        // sF = swapFee                                                                              //
        **********************************************************************************************/
        function calcInGivenOut(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint tokenAmountOut,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountIn)
        {
            uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
            uint diff = bsub(tokenBalanceOut, tokenAmountOut);
            uint y = bdiv(tokenBalanceOut, diff);
            uint foo = bpow(y, weightRatio);
            foo = bsub(foo, BONE);
            tokenAmountIn = bsub(BONE, swapFee);
            tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);
            return tokenAmountIn;
        }
        /**********************************************************************************************
        // calcPoolOutGivenSingleIn                                                                  //
        // pAo = poolAmountOut         /                                              \\              //
        // tAi = tokenAmountIn        ///      /     //    wI \\      \\\\       \\     wI \\             //
        // wI = tokenWeightIn        //| tAi *| 1 - || 1 - --  | * sF || + tBi \\    --  \\            //
        // tW = totalWeight     pAo=||  \\      \\     \\\\    tW /      //         | ^ tW   | * pS - pS //
        // tBi = tokenBalanceIn      \\\\  ------------------------------------- /        /            //
        // pS = poolSupply            \\\\                    tBi               /        /             //
        // sF = swapFee                \\                                              /              //
        **********************************************************************************************/
        function calcPoolOutGivenSingleIn(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint poolSupply,
            uint totalWeight,
            uint tokenAmountIn,
            uint swapFee
        )
            public pure
            returns (uint poolAmountOut)
        {
            // Charge the trading fee for the proportion of tokenAi
            ///  which is implicitly traded to the other pool tokens.
            // That proportion is (1- weightTokenIn)
            // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
            uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
            uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); 
            uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));
            uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
            uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);
            // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;
            uint poolRatio = bpow(tokenInRatio, normalizedWeight);
            uint newPoolSupply = bmul(poolRatio, poolSupply);
            poolAmountOut = bsub(newPoolSupply, poolSupply);
            return poolAmountOut;
        }
        /**********************************************************************************************
        // calcSingleInGivenPoolOut                                                                  //
        // tAi = tokenAmountIn              //(pS + pAo)\\     /    1    \\\\                           //
        // pS = poolSupply                 || ---------  | ^ | --------- || * bI - bI                //
        // pAo = poolAmountOut              \\\\    pS    /     \\(wI / tW)//                           //
        // bI = balanceIn          tAi =  --------------------------------------------               //
        // wI = weightIn                              /      wI  \\                                   //
        // tW = totalWeight                          |  1 - ----  |  * sF                            //
        // sF = swapFee                               \\      tW  /                                   //
        **********************************************************************************************/
        function calcSingleInGivenPoolOut(
            uint tokenBalanceIn,
            uint tokenWeightIn,
            uint poolSupply,
            uint totalWeight,
            uint poolAmountOut,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountIn)
        {
            uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
            uint newPoolSupply = badd(poolSupply, poolAmountOut);
            uint poolRatio = bdiv(newPoolSupply, poolSupply);
          
            //uint newBalTi = poolRatio^(1/weightTi) * balTi;
            uint boo = bdiv(BONE, normalizedWeight); 
            uint tokenInRatio = bpow(poolRatio, boo);
            uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
            uint tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);
            // Do reverse order of fees charged in joinswap_ExternAmountIn, this way 
            //     ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```
            //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;
            uint zar = bmul(bsub(BONE, normalizedWeight), swapFee);
            tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));
            return tokenAmountIn;
        }
        /**********************************************************************************************
        // calcSingleOutGivenPoolIn                                                                  //
        // tAo = tokenAmountOut            /      /                                             \\\\   //
        // bO = tokenBalanceOut           /      // pS - (pAi * (1 - eF)) \\     /    1    \\      \\\\  //
        // pAi = poolAmountIn            | bO - || ----------------------- | ^ | --------- | * b0 || //
        // ps = poolSupply                \\      \\\\          pS           /     \\(wO / tW)/      //  //
        // wI = tokenWeightIn      tAo =   \\      \\                                             //   //
        // tW = totalWeight                    /     /      wO \\       \\                             //
        // sF = swapFee                    *  | 1 - |  1 - ---- | * sF  |                            //
        // eF = exitFee                        \\     \\      tW /       /                             //
        **********************************************************************************************/
        function calcSingleOutGivenPoolIn(
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint poolSupply,
            uint totalWeight,
            uint poolAmountIn,
            uint swapFee
        )
            public pure
            returns (uint tokenAmountOut)
        {
            uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
            // charge exit fee on the pool token side
            // pAiAfterExitFee = pAi*(1-exitFee)
            uint poolAmountInAfterExitFee = bmul(
                poolAmountIn, 
                bsub(BONE, EXIT_FEE)
            );
            uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
            uint poolRatio = bdiv(newPoolSupply, poolSupply);
         
            // newBalTo = poolRatio^(1/weightTo) * balTo;
            uint tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));
            uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);
            uint tokenAmountOutBeforeSwapFee = bsub(
                tokenBalanceOut, 
                newTokenBalanceOut
            );
            // charge swap fee on the output token side 
            //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)
            uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); 
            tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));
            return tokenAmountOut;
        }
        /**********************************************************************************************
        // calcPoolInGivenSingleOut                                                                  //
        // pAi = poolAmountIn               // /               tAo             \\\\     / wO \\     \\   //
        // bO = tokenBalanceOut            // | bO - -------------------------- |\\   | ---- |     \\  //
        // tAo = tokenAmountOut      pS - ||   \\     1 - ((1 - (tO / tW)) * sF)/  | ^ \\ tW /  * pS | //
        // ps = poolSupply                 \\\\ -----------------------------------/                /  //
        // wO = tokenWeightOut  pAi =       \\\\               bO                 /                /   //
        // tW = totalWeight           -------------------------------------------------------------  //
        // sF = swapFee                                        ( 1 - eF )                            //
        // eF = exitFee                                                                              //
        **********************************************************************************************/
        function calcPoolInGivenSingleOut(
            uint tokenBalanceOut,
            uint tokenWeightOut,
            uint poolSupply,
            uint totalWeight,
            uint tokenAmountOut,
            uint swapFee
        )
            public pure
            returns (uint poolAmountIn)
        {
            // charge swap fee on the output token side 
            uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
            //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;
            uint zoo = bsub(BONE, normalizedWeight);
            uint zar = bmul(zoo, swapFee); 
            uint tokenAmountOutBeforeSwapFee = bdiv(
                tokenAmountOut, 
                bsub(BONE, zar)
            );
            uint newTokenBalanceOut = bsub(
                tokenBalanceOut, 
                tokenAmountOutBeforeSwapFee
            );
            uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);
            //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;
            uint poolRatio = bpow(tokenOutRatio, normalizedWeight);
            uint newPoolSupply = bmul(poolRatio, poolSupply);
            uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);
            // charge exit fee on the pool token side
            // pAi = pAiAfterExitFee/(1-exitFee)
            poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));
            return poolAmountIn;
        }
    }
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    import './BConst.sol';
    contract BNum is BConst {
        function btoi(uint a)
            internal pure 
            returns (uint)
        {
            return a / BONE;
        }
        function bfloor(uint a)
            internal pure
            returns (uint)
        {
            return btoi(a) * BONE;
        }
        function badd(uint a, uint b)
            internal pure
            returns (uint)
        {
            uint c = a + b;
            require(c >= a, 'ERR_ADD_OVERFLOW');
            return c;
        }
        function bsub(uint a, uint b)
            internal pure
            returns (uint)
        {
            (uint c, bool flag) = bsubSign(a, b);
            require(!flag, 'ERR_SUB_UNDERFLOW');
            return c;
        }
        function bsubSign(uint a, uint b)
            internal pure
            returns (uint, bool)
        {
            if (a >= b) {
                return (a - b, false);
            } else {
                return (b - a, true);
            }
        }
        function bmul(uint a, uint b)
            internal pure
            returns (uint)
        {
            uint c0 = a * b;
            require(a == 0 || c0 / a == b, 'ERR_MUL_OVERFLOW');
            uint c1 = c0 + (BONE / 2);
            require(c1 >= c0, 'ERR_MUL_OVERFLOW');
            uint c2 = c1 / BONE;
            return c2;
        }
        function bdiv(uint a, uint b)
            internal pure
            returns (uint)
        {
            require(b != 0, 'ERR_DIV_ZERO');
            uint c0 = a * BONE;
            require(a == 0 || c0 / a == BONE, 'ERR_DIV_INTERNAL'); // bmul overflow
            uint c1 = c0 + (b / 2);
            require(c1 >= c0, 'ERR_DIV_INTERNAL'); //  badd require
            uint c2 = c1 / b;
            return c2;
        }
        // DSMath.wpow
        function bpowi(uint a, uint n)
            internal pure
            returns (uint)
        {
            uint b = a;
            uint z = n % 2 != 0 ? b : BONE;
            for (n /= 2; n != 0; n /= 2) {
                b = bmul(b, b);
                if (n % 2 != 0) {
                    z = bmul(z, b);
                }
            }
            return z;
        }
        // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
        // Use `bpowi` for `b^e` and `bpowK` for k iterations
        // of approximation of b^0.w
        function bpow(uint base, uint exp)
            internal pure
            returns (uint)
        {
            require(base >= MIN_BPOW_BASE, 'ERR_BPOW_BASE_TOO_LOW');
            require(base <= MAX_BPOW_BASE, 'ERR_BPOW_BASE_TOO_HIGH');
            uint whole  = bfloor(exp);   
            uint remain = bsub(exp, whole);
            uint wholePow = bpowi(base, btoi(whole));
            if (remain == 0) {
                return wholePow;
            }
            uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);
            return bmul(wholePow, partialResult);
        }
        function bpowApprox(uint base, uint exp, uint precision)
            internal pure
            returns (uint)
        {
            // term 0:
            uint a     = exp;
            (uint x, bool xneg)  = bsubSign(base, BONE);
            uint term = BONE;
            uint sum   = term;
            bool negative = false;
            // term(k) = numer / denom 
            //         = (product(a - i - 1, i=1-->k) * x^k) / (k!)
            // each iteration, multiply previous term by (a-(k-1)) * x / k
            // continue until term is less than precision
            for (uint i = 1; term >= precision; i++) {
                uint bigK = i * BONE;
                (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
                term = bmul(term, bmul(c, x));
                term = bdiv(term, bigK);
                if (term == 0) break;
                if (xneg) negative = !negative;
                if (cneg) negative = !negative;
                if (negative) {
                    sum = bsub(sum, term);
                } else {
                    sum = badd(sum, term);
                }
            }
            return sum;
        }
    }
    pragma solidity ^0.5.7;
    // Copyright BigchainDB GmbH and Ocean Protocol contributors
    // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
    // Code is Apache-2.0 and docs are CC-BY-4.0
    import './BToken.sol';
    import './BMath.sol';
    /**
    * @title BPool
    *  
    * @dev Used by the (Ocean version) BFactory contract as a bytecode reference to
    *      deploy new BPools.
    *
    * This contract is is nearly identical to the BPool.sol contract at [1]
    *  The only difference is the "Proxy contract functionality" section 
    *  given below. We'd inherit from BPool if we could, for simplicity.
    *  But we can't, because the proxy section needs to access private
    *  variables declared in BPool, and Solidity disallows this. Therefore
    *  the best we can do for now is clearly demarcate the proxy section. 
    *
    *  [1] https://github.com/balancer-labs/balancer-core/contracts/.
    */
    contract BPool is BToken, BMath {
        struct Record {
            bool bound;   // is token bound to pool
            uint index;   // private
            uint denorm;  // denormalized weight
            uint balance;
        }
        event LOG_SWAP(
            address indexed caller,
            address indexed tokenIn,
            address indexed tokenOut,
            uint256         tokenAmountIn,
            uint256         tokenAmountOut
        );
        event LOG_JOIN(
            address indexed caller,
            address indexed tokenIn,
            uint256         tokenAmountIn
        );
        event LOG_EXIT(
            address indexed caller,
            address indexed tokenOut,
            uint256         tokenAmountOut
        );
        event LOG_CALL(
            bytes4  indexed sig,
            address indexed caller,
            bytes           data
        ) anonymous;
        modifier _logs_() {
            emit LOG_CALL(msg.sig, msg.sender, msg.data);
            _;
        }
        modifier _lock_() {
            require(
                !_mutex, 
                'ERR_REENTRY'
            );
            _mutex = true;
            _;
            _mutex = false;
        }
        modifier _viewlock_() {
            require(!_mutex, 'ERR_REENTRY');
            _;
        }
        
        bool private _mutex;
        address private _factory;    // BFactory address to push token exitFee to
        address private _controller; // has CONTROL role
        bool private _publicSwap; // true if PUBLIC can call SWAP functions
        // `setSwapFee` and `finalize` require CONTROL
        // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`
        uint private _swapFee;
        bool private _finalized;
        address[] private _tokens;
        mapping(address=>Record) private  _records;
        uint private _totalWeight;
        //-----------------------------------------------------------------------
        //Proxy contract functionality: begin
        bool private initialized = false; 
        modifier onlyNotInitialized() {
            require(
                !initialized, 
                'ERR_ALREADY_INITIALIZED'
            );
            _;
        }
        function isInitialized() external view returns(bool) {
            return initialized;
        }
        
        // Called prior to contract deployment
        constructor() public {
            _initialize(msg.sender, msg.sender, MIN_FEE, false, false);
        }
        
        // Called prior to contract initialization (e.g creating new BPool instance)
        // Calls private _initialize function. Only if contract is not initialized.
        function initialize(
            address controller, 
            address factory, 
            uint swapFee,
            bool publicSwap,
            bool finalized
        )
            external
            onlyNotInitialized
            returns(bool)
        {
            require(
                controller != address(0),
                'ERR_INVALID_CONTROLLER_ADDRESS'
            );
            require(
                factory != address(0),
                'ERR_INVALID_FACTORY_ADDRESS'
            );
            require(swapFee >= MIN_FEE, 'ERR_MIN_FEE');
            require(swapFee <= MAX_FEE, 'ERR_MAX_FEE');
            return _initialize(controller, factory, swapFee, publicSwap, finalized);
        }
    \t
        // Private function called on contract initialization.
        function _initialize(
            address controller, 
            address factory, 
            uint swapFee,
            bool publicSwap, 
            bool finalized
        )
            private
            returns(bool)
        {
            _controller = controller;
            _factory = factory;
            _swapFee = swapFee;
            _publicSwap = publicSwap;
            _finalized = finalized;
    \t
            initialized = true;
            return initialized;
        }
        function setup(
            address dataTokenAaddress, 
            uint256 dataTokenAmount,
            uint256 dataTokenWeight,
            address baseTokenAddress, 
            uint256 baseTokenAmount,
            uint256 baseTokenWeight,
            uint256 swapFee
        )
            external
            _logs_
        {
            require(
                dataTokenAaddress != address(0),
                'ERR_INVALID_DATATOKEN_ADDRESS'
            );
            require(
                baseTokenAddress != address(0),
                'ERR_INVALID_BASETOKEN_ADDRESS'
            );
            // other inputs will be validated prior
            // calling the below functions
            // bind data token
            bind(
                dataTokenAaddress,
                dataTokenAmount,
                dataTokenWeight
            );
            emit LOG_JOIN(msg.sender, dataTokenAaddress, dataTokenAmount);
            // bind base token
            bind(
                baseTokenAddress,
                baseTokenAmount,
                baseTokenWeight
            );
            emit LOG_JOIN(msg.sender, baseTokenAddress, baseTokenAmount);
            setSwapFee(swapFee);
            // finalize
            finalize();
        }
        
        //Proxy contract functionality: end
        //-----------------------------------------------------------------------
        function isPublicSwap()
            external view
            returns (bool)
        {
            return _publicSwap;
        }
        function isFinalized()
            external view
            returns (bool)
        {
            return _finalized;
        }
        function isBound(address t)
            external view
            returns (bool)
        {
            return _records[t].bound;
        }
        function getNumTokens()
            external view
            returns (uint) 
        {
            return _tokens.length;
        }
        function getCurrentTokens()
            external view _viewlock_
            returns (address[] memory tokens)
        {
            return _tokens;
        }
        function getFinalTokens()
            external view
            _viewlock_
            returns (address[] memory tokens)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            return _tokens;
        }
        function getDenormalizedWeight(address token)
            external view
            _viewlock_
            returns (uint)
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            return _records[token].denorm;
        }
        function getTotalDenormalizedWeight()
            external view
            _viewlock_
            returns (uint)
        {
            return _totalWeight;
        }
        function getNormalizedWeight(address token)
            external view
            _viewlock_
            returns (uint)
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            uint denorm = _records[token].denorm;
            return bdiv(denorm, _totalWeight);
        }
        function getBalance(address token)
            external view
            _viewlock_
            returns (uint)
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            return _records[token].balance;
        }
        function getSwapFee()
            external view
            _viewlock_
            returns (uint)
        {
            return _swapFee;
        }
        function getController()
            external view
            _viewlock_
            returns (address)
        {
            return _controller;
        }
        function setSwapFee(uint swapFee)
            public
            _logs_
            _lock_
        { 
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(swapFee >= MIN_FEE, 'ERR_MIN_FEE');
            require(swapFee <= MAX_FEE, 'ERR_MAX_FEE');
            _swapFee = swapFee;
        }
        function setController(address manager)
            external
            _logs_
            _lock_
        {
            require(
                manager != address(0),
                'ERR_INVALID_MANAGER_ADDRESS'
            );
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            _controller = manager;
        }
    \t
        function setPublicSwap(bool public_)
            public
            _logs_
            _lock_
        {
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            _publicSwap = public_;
        }
        function finalize()
            public
            _logs_
            _lock_
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(_tokens.length >= MIN_BOUND_TOKENS, 'ERR_MIN_TOKENS');
            _finalized = true;
            _publicSwap = true;
            _mintPoolShare(INIT_POOL_SUPPLY);
            _pushPoolShare(msg.sender, INIT_POOL_SUPPLY);
        }
        function bind(address token, uint balance, uint denorm)
            public
            _logs_
            // _lock_  Bind does not lock because it jumps to `rebind`, which does
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(!_records[token].bound, 'ERR_IS_BOUND');
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(_tokens.length < MAX_BOUND_TOKENS, 'ERR_MAX_TOKENS');
            _records[token] = Record({
                bound: true,
                index: _tokens.length,
                denorm: 0,
                // balance and denorm will be validated
                balance: 0   // and set by `rebind`
            });
            _tokens.push(token);
            rebind(token, balance, denorm);
        }
        function rebind(address token, uint balance, uint denorm)
            public
            _logs_
            _lock_
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(_records[token].bound, 'ERR_NOT_BOUND');
            require(!_finalized, 'ERR_IS_FINALIZED');
            require(denorm >= MIN_WEIGHT, 'ERR_MIN_WEIGHT');
            require(denorm <= MAX_WEIGHT, 'ERR_MAX_WEIGHT');
            require(balance >= MIN_BALANCE, 'ERR_MIN_BALANCE');
            // Adjust the denorm and totalWeight
            uint oldWeight = _records[token].denorm;
            if (denorm > oldWeight) {
                _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));
                require(_totalWeight <= MAX_TOTAL_WEIGHT, 'ERR_MAX_TOTAL_WEIGHT');
            } else if (denorm < oldWeight) {
                _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));
            }        
            _records[token].denorm = denorm;
            // Adjust the balance record and actual token balance
            uint oldBalance = _records[token].balance;
            _records[token].balance = balance;
            if (balance > oldBalance) {
                _pullUnderlying(token, msg.sender, bsub(balance, oldBalance));
            } else if (balance < oldBalance) {
                // In this case liquidity is being withdrawn, so charge EXIT_FEE
                uint tokenBalanceWithdrawn = bsub(oldBalance, balance);
                uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE);
                _pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee));
                _pushUnderlying(token, _factory, tokenExitFee);
            }
        }
        function unbind(address token)
            external
            _logs_
            _lock_
        {
            require(msg.sender == _controller, 'ERR_NOT_CONTROLLER');
            require(_records[token].bound, 'ERR_NOT_BOUND');
            require(!_finalized, 'ERR_IS_FINALIZED');
            uint tokenBalance = _records[token].balance;
            uint tokenExitFee = bmul(tokenBalance, EXIT_FEE);
            _totalWeight = bsub(_totalWeight, _records[token].denorm);
            // Swap the token-to-unbind with the last token,
            // then delete the last token
            uint index = _records[token].index;
            uint last = _tokens.length - 1;
            _tokens[index] = _tokens[last];
            _records[_tokens[index]].index = index;
            _tokens.pop();
            _records[token] = Record({
                bound: false,
                index: 0,
                denorm: 0,
                balance: 0
            });
            _pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee));
            _pushUnderlying(token, _factory, tokenExitFee);
        }
        // Absorb any tokens that have been sent to this contract into the pool
        function gulp(address token)
            external
            _logs_
            _lock_
        {
            require(_records[token].bound, 'ERR_NOT_BOUND');
            _records[token].balance = IERC20(token).balanceOf(address(this));
        }
        function getSpotPrice(address tokenIn, address tokenOut)
            external view
            _viewlock_
            returns (uint spotPrice)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            Record storage inRecord = _records[tokenIn];
            Record storage outRecord = _records[tokenOut];
            return calcSpotPrice(
                inRecord.balance, 
                inRecord.denorm, 
                outRecord.balance, 
                outRecord.denorm, 
                _swapFee
            );
        }
        function getSpotPriceSansFee(address tokenIn, address tokenOut)
            external view
            _viewlock_
            returns (uint spotPrice)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            Record storage inRecord = _records[tokenIn];
            Record storage outRecord = _records[tokenOut];
            return calcSpotPrice(
                inRecord.balance, 
                inRecord.denorm, 
                outRecord.balance, 
                outRecord.denorm, 
                0
            );
        }
        function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn)
            external
            _logs_
            _lock_
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            uint poolTotal = totalSupply();
            uint ratio = bdiv(poolAmountOut, poolTotal);
            require(ratio != 0, 'ERR_MATH_APPROX');
            for (uint i = 0; i < _tokens.length; i++) {
                address t = _tokens[i];
                uint bal = _records[t].balance;
                uint tokenAmountIn = bmul(ratio, bal);
                require(tokenAmountIn != 0, 'ERR_MATH_APPROX');
                require(tokenAmountIn <= maxAmountsIn[i], 'ERR_LIMIT_IN');
                _records[t].balance = badd(_records[t].balance, tokenAmountIn);
                emit LOG_JOIN(msg.sender, t, tokenAmountIn);
                _pullUnderlying(t, msg.sender, tokenAmountIn);
            }
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(msg.sender, poolAmountOut);
        }
        function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut)
            external
            _logs_
            _lock_
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            uint poolTotal = totalSupply();
            uint exitFee = bmul(poolAmountIn, EXIT_FEE);
            uint pAiAfterExitFee = bsub(poolAmountIn, exitFee);
            uint ratio = bdiv(pAiAfterExitFee, poolTotal);
            require(ratio != 0, 'ERR_MATH_APPROX');
            _pullPoolShare(msg.sender, poolAmountIn);
            _pushPoolShare(_factory, exitFee);
            _burnPoolShare(pAiAfterExitFee);
            for (uint i = 0; i < _tokens.length; i++) {
                address t = _tokens[i];
                uint bal = _records[t].balance;
                uint tokenAmountOut = bmul(ratio, bal);
                require(tokenAmountOut != 0, 'ERR_MATH_APPROX');
                require(tokenAmountOut >= minAmountsOut[i], 'ERR_LIMIT_OUT');
                _records[t].balance = bsub(_records[t].balance, tokenAmountOut);
                emit LOG_EXIT(msg.sender, t, tokenAmountOut);
                _pushUnderlying(t, msg.sender, tokenAmountOut);
            }
        }
        function swapExactAmountIn(
            address tokenIn,
            uint tokenAmountIn,
            address tokenOut,
            uint minAmountOut,
            uint maxPrice
        )
            external
            _logs_
            _lock_
            returns (uint tokenAmountOut, uint spotPriceAfter)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            require(_publicSwap, 'ERR_SWAP_NOT_PUBLIC');
            Record storage inRecord = _records[address(tokenIn)];
            Record storage outRecord = _records[address(tokenOut)];
            require(
                tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), 
                'ERR_MAX_IN_RATIO'
            );
            uint spotPriceBefore = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            require(spotPriceBefore <= maxPrice, 'ERR_BAD_LIMIT_PRICE');
            tokenAmountOut = calcOutGivenIn(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                tokenAmountIn,
                _swapFee
            );
            require(tokenAmountOut >= minAmountOut, 'ERR_LIMIT_OUT');
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            spotPriceAfter = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            require(spotPriceAfter >= spotPriceBefore, 'ERR_MATH_APPROX');     
            require(spotPriceAfter <= maxPrice, 'ERR_LIMIT_PRICE');
            require(
                spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), 
                'ERR_MATH_APPROX'
            );
            emit LOG_SWAP(
                msg.sender, 
                tokenIn, 
                tokenOut, 
                tokenAmountIn, 
                tokenAmountOut
            );
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
            return (tokenAmountOut, spotPriceAfter);
        }
        function swapExactAmountOut(
            address tokenIn,
            uint maxAmountIn,
            address tokenOut,
            uint tokenAmountOut,
            uint maxPrice
        )
            external
            _logs_
            _lock_ 
            returns (uint tokenAmountIn, uint spotPriceAfter)
        {
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            require(_publicSwap, 'ERR_SWAP_NOT_PUBLIC');
            Record storage inRecord = _records[address(tokenIn)];
            Record storage outRecord = _records[address(tokenOut)];
            require(
                tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), 
                'ERR_MAX_OUT_RATIO'
            );
            uint spotPriceBefore = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            
            require(spotPriceBefore <= maxPrice, 'ERR_BAD_LIMIT_PRICE');
            tokenAmountIn = calcInGivenOut(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                tokenAmountOut,
                _swapFee
            );
            require(tokenAmountIn <= maxAmountIn, 'ERR_LIMIT_IN');
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            spotPriceAfter = calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _swapFee
            );
            require(spotPriceAfter >= spotPriceBefore, 'ERR_MATH_APPROX');
            require(spotPriceAfter <= maxPrice, 'ERR_LIMIT_PRICE');
            require(
                spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), 
                'ERR_MATH_APPROX'
            );
            emit LOG_SWAP(
                msg.sender, 
                tokenIn, 
                tokenOut, 
                tokenAmountIn, 
                tokenAmountOut
            );
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
            return (tokenAmountIn, spotPriceAfter);
        }
        function joinswapExternAmountIn(
            address tokenIn, 
            uint tokenAmountIn, 
            uint minPoolAmountOut
        )
            external
            _logs_
            _lock_
            returns (uint poolAmountOut)
        {        
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            require(
                tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), 
                'ERR_MAX_IN_RATIO'
            );
            Record storage inRecord = _records[tokenIn];
            poolAmountOut = calcPoolOutGivenSingleIn(
                inRecord.balance,
                inRecord.denorm,
                _totalSupply,
                _totalWeight,
                tokenAmountIn,
                _swapFee
            );
            require(poolAmountOut >= minPoolAmountOut, 'ERR_LIMIT_OUT');
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(msg.sender, poolAmountOut);
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            return poolAmountOut;
        }
        function joinswapPoolAmountOut(
            address tokenIn, 
            uint poolAmountOut, 
            uint maxAmountIn
        )
            external
            _logs_
            _lock_
            returns (uint tokenAmountIn)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenIn].bound, 'ERR_NOT_BOUND');
            Record storage inRecord = _records[tokenIn];
            tokenAmountIn = calcSingleInGivenPoolOut(
                inRecord.balance,
                inRecord.denorm,
                _totalSupply,
                _totalWeight,
                poolAmountOut,
                _swapFee
            );
            require(tokenAmountIn != 0, 'ERR_MATH_APPROX');
            require(tokenAmountIn <= maxAmountIn, 'ERR_LIMIT_IN');
            
            require(
                tokenAmountIn <= bmul(_records[tokenIn].balance, MAX_IN_RATIO), 
                'ERR_MAX_IN_RATIO'
            );
            inRecord.balance = badd(inRecord.balance, tokenAmountIn);
            emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(msg.sender, poolAmountOut);
            _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
            return tokenAmountIn;
        }
        function exitswapPoolAmountIn(
            address tokenOut, 
            uint poolAmountIn, 
            uint minAmountOut
        )
            external
            _logs_
            _lock_
            returns (uint tokenAmountOut)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            Record storage outRecord = _records[tokenOut];
            tokenAmountOut = calcSingleOutGivenPoolIn(
                outRecord.balance,
                outRecord.denorm,
                _totalSupply,
                _totalWeight,
                poolAmountIn,
                _swapFee
            );
            require(tokenAmountOut >= minAmountOut, 'ERR_LIMIT_OUT');
            
            require(
                tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), 
                'ERR_MAX_OUT_RATIO'
            );
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            uint exitFee = bmul(poolAmountIn, EXIT_FEE);
            emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
            _pullPoolShare(msg.sender, poolAmountIn);
            _burnPoolShare(bsub(poolAmountIn, exitFee));
            _pushPoolShare(_factory, exitFee);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
            return tokenAmountOut;
        }
        function exitswapExternAmountOut(
            address tokenOut, 
            uint tokenAmountOut, 
            uint maxPoolAmountIn
        )
            external
            _logs_
            _lock_
            returns (uint poolAmountIn)
        {
            require(_finalized, 'ERR_NOT_FINALIZED');
            require(_records[tokenOut].bound, 'ERR_NOT_BOUND');
            require(
                tokenAmountOut <= bmul(_records[tokenOut].balance, MAX_OUT_RATIO), 
                'ERR_MAX_OUT_RATIO'
            );
            Record storage outRecord = _records[tokenOut];
            poolAmountIn = calcPoolInGivenSingleOut(
                outRecord.balance,
                outRecord.denorm,
                _totalSupply,
                _totalWeight,
                tokenAmountOut,
                _swapFee
            );
            require(poolAmountIn != 0, 'ERR_MATH_APPROX');
            require(poolAmountIn <= maxPoolAmountIn, 'ERR_LIMIT_IN');
            outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
            uint exitFee = bmul(poolAmountIn, EXIT_FEE);
            emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
            _pullPoolShare(msg.sender, poolAmountIn);
            _burnPoolShare(bsub(poolAmountIn, exitFee));
            _pushPoolShare(_factory, exitFee);
            _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);        
            return poolAmountIn;
        }
        // ==
        // 'Underlying' token-manipulation functions make external calls but are NOT locked
        // You must `_lock_` or otherwise ensure reentry-safety
        function _pullUnderlying(address erc20, address from, uint amount)
            internal
        {
            bool xfer = IERC20(erc20).transferFrom(from, address(this), amount);
            require(xfer, 'ERR_ERC20_FALSE');
        }
        function _pushUnderlying(address erc20, address to, uint amount)
            internal
        {
            bool xfer = IERC20(erc20).transfer(to, amount);
            require(xfer, 'ERR_ERC20_FALSE');
        }
        function _pullPoolShare(address from, uint amount)
            internal
        {
            _pull(from, amount);
        }
        function _pushPoolShare(address to, uint amount)
            internal
        {
            _push(to, amount);
        }
        function _mintPoolShare(uint amount)
            internal
        {
            _mint(amount);
        }
        function _burnPoolShare(uint amount)
            internal
        {
            _burn(amount);
        }
    }
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    pragma solidity 0.5.7;
    import './BNum.sol';
    import 'openzeppelin-solidity/contracts/token/ERC20/IERC20.sol';
    // Highly opinionated token implementation
    // interface IERC20 {
    //     event Approval(address indexed src, address indexed dst, uint amt);
    //     event Transfer(address indexed src, address indexed dst, uint amt);
    //     function totalSupply() external view returns (uint);
    //     function balanceOf(address whom) external view returns (uint);
    //     function allowance(address src, address dst) external view returns (uint);
    //     function approve(address dst, uint amt) external returns (bool);
    //     function transfer(address dst, uint amt) external returns (bool);
    //     function transferFrom(
    //         address src, address dst, uint amt
    //     ) external returns (bool);
    // }
    contract BTokenBase is BNum {
        mapping(address => uint)                   internal _balance;
        mapping(address => mapping(address=>uint)) internal _allowance;
        uint internal _totalSupply;
        event Approval(address indexed src, address indexed dst, uint amt);
        event Transfer(address indexed src, address indexed dst, uint amt);
        function _mint(uint amt) internal {
            _balance[address(this)] = badd(_balance[address(this)], amt);
            _totalSupply = badd(_totalSupply, amt);
            emit Transfer(address(0), address(this), amt);
        }
        function _burn(uint amt) internal {
            require(
                _balance[address(this)] >= amt, 
                'ERR_INSUFFICIENT_BAL'
            );
            _balance[address(this)] = bsub(_balance[address(this)], amt);
            _totalSupply = bsub(_totalSupply, amt);
            emit Transfer(address(this), address(0), amt);
        }
        function _move(address src, address dst, uint amt) internal {
            require(_balance[src] >= amt, 'ERR_INSUFFICIENT_BAL');
            _balance[src] = bsub(_balance[src], amt);
            _balance[dst] = badd(_balance[dst], amt);
            emit Transfer(src, dst, amt);
        }
        function _push(address to, uint amt) internal {
            _move(address(this), to, amt);
        }
        function _pull(address from, uint amt) internal {
            _move(from, address(this), amt);
        }
    }
    contract BToken is BTokenBase, IERC20 {
        string  private _name     = 'Balancer Pool Token';
        string  private _symbol   = 'BPT';
        uint8   private _decimals = 18;
        function name() public view returns (string memory) {
            return _name;
        }
        function symbol() public view returns (string memory) {
            return _symbol;
        }
        function decimals() public view returns(uint8) {
            return _decimals;
        }
        function allowance(address src, address dst) external view returns (uint) {
            return _allowance[src][dst];
        }
        function balanceOf(address whom) external view returns (uint) {
            return _balance[whom];
        }
        function totalSupply() public view returns (uint) {
            return _totalSupply;
        }
        function approve(address dst, uint amt) external returns (bool) {
            _allowance[msg.sender][dst] = amt;
            emit Approval(msg.sender, dst, amt);
            return true;
        }
        function increaseApproval(address dst, uint amt) external returns (bool) {
            _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
            emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
            return true;
        }
        function decreaseApproval(address dst, uint amt) external returns (bool) {
            uint oldValue = _allowance[msg.sender][dst];
            if (amt > oldValue) {
                _allowance[msg.sender][dst] = 0;
            } else {
                _allowance[msg.sender][dst] = bsub(oldValue, amt);
            }
            emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
            return true;
        }
        function transfer(address dst, uint amt) external returns (bool) {
            _move(msg.sender, dst, amt);
            return true;
        }
        function transferFrom(
            address src, 
            address dst, 
            uint amt
        ) 
        external 
        returns (bool) 
        {
            require(
                msg.sender == src || amt <= _allowance[src][msg.sender], 
                'ERR_BTOKEN_BAD_CALLER'
            );
            _move(src, dst, amt);
            if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) {
                _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
                emit Approval(msg.sender, dst, _allowance[src][msg.sender]);
            }
            return true;
        }
    }
    pragma solidity ^0.5.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
     * the optional functions; to access them see {ERC20Detailed}.
     */
    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);
    }