ETH Price: $2,043.65 (+0.46%)

Transaction Decoder

Block:
10526273 at Jul-25-2020 03:35:31 AM +UTC
Transaction Fee:
0.010709634 ETH $21.89
Gas Used:
137,303 Gas / 78 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x4187cC9f...790413bb0
0.273265114250050494 Eth
Nonce: 969
0.262555480250050494 Eth
Nonce: 970
0.010709634
0x762ed657...658C4CA35
(UUPool)
544.278608051918971398 Eth544.289317685918971398 Eth0.010709634

Execution Trace

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

            File 2 of 7: MassetProxy
            pragma solidity 0.5.16;
            
            
            /**
             * @title Proxy
             * @dev Implements delegation of calls to other contracts, with proper
             * forwarding of return values and bubbling of failures.
             * It defines a fallback function that delegates all calls to the address
             * returned by the abstract _implementation() internal function.
             */
            contract Proxy {
              /**
               * @dev Fallback function.
               * Implemented entirely in `_fallback`.
               */
              function () payable external {
                _fallback();
              }
            
              /**
               * @return The Address of the implementation.
               */
              function _implementation() internal view returns (address);
            
              /**
               * @dev Delegates execution to an implementation contract.
               * This is a low level function that doesn't return to its internal call site.
               * It will return to the external caller whatever the implementation returns.
               * @param implementation Address to delegate.
               */
              function _delegate(address implementation) internal {
                assembly {
                  // Copy msg.data. We take full control of memory in this inline assembly
                  // block because it will not return to Solidity code. We overwrite the
                  // Solidity scratch pad at memory position 0.
                  calldatacopy(0, 0, calldatasize)
            
                  // Call the implementation.
                  // out and outsize are 0 because we don't know the size yet.
                  let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
            
                  // Copy the returned data.
                  returndatacopy(0, 0, returndatasize)
            
                  switch result
                  // delegatecall returns 0 on error.
                  case 0 { revert(0, returndatasize) }
                  default { return(0, returndatasize) }
                }
              }
            
              /**
               * @dev Function that is run as the first thing in the fallback function.
               * Can be redefined in derived contracts to add functionality.
               * Redefinitions must call super._willFallback().
               */
              function _willFallback() internal {
              }
            
              /**
               * @dev fallback implementation.
               * Extracted to enable manual triggering.
               */
              function _fallback() internal {
                _willFallback();
                _delegate(_implementation());
              }
            }
            
            /**
             * Utility library of inline functions on addresses
             *
             * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
             * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
             * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
             * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
             */
            library OpenZeppelinUpgradesAddress {
                /**
                 * Returns whether the target address is a contract
                 * @dev This function will return false if invoked during the constructor of a contract,
                 * as the code is not actually created until after the constructor finishes.
                 * @param account address of the account to check
                 * @return whether the target address is a contract
                 */
                function isContract(address account) internal view returns (bool) {
                    uint256 size;
                    // XXX Currently there is no better way to check if there is a contract in an address
                    // than to check the size of the code at that address.
                    // See https://ethereum.stackexchange.com/a/14016/36603
                    // for more details about how this works.
                    // TODO Check this again before the Serenity release, because all addresses will be
                    // contracts then.
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
            }
            
            /**
             * @title BaseUpgradeabilityProxy
             * @dev This contract implements a proxy that allows to change the
             * implementation address to which it will delegate.
             * Such a change is called an implementation upgrade.
             */
            contract BaseUpgradeabilityProxy is Proxy {
              /**
               * @dev Emitted when the implementation is upgraded.
               * @param implementation Address of the new implementation.
               */
              event Upgraded(address indexed implementation);
            
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
            
              /**
               * @dev Returns the current implementation.
               * @return Address of the current implementation
               */
              function _implementation() internal view returns (address impl) {
                bytes32 slot = IMPLEMENTATION_SLOT;
                assembly {
                  impl := sload(slot)
                }
              }
            
              /**
               * @dev Upgrades the proxy to a new implementation.
               * @param newImplementation Address of the new implementation.
               */
              function _upgradeTo(address newImplementation) internal {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
              }
            
              /**
               * @dev Sets the implementation address of the proxy.
               * @param newImplementation Address of the new implementation.
               */
              function _setImplementation(address newImplementation) internal {
                require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
            
                bytes32 slot = IMPLEMENTATION_SLOT;
            
                assembly {
                  sstore(slot, newImplementation)
                }
              }
            }
            
            /**
             * @title UpgradeabilityProxy
             * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
             * implementation and init data.
             */
            contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Contract constructor.
               * @param _logic Address of the initial implementation.
               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
               */
              constructor(address _logic, bytes memory _data) public payable {
                assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                _setImplementation(_logic);
                if(_data.length > 0) {
                  (bool success,) = _logic.delegatecall(_data);
                  require(success);
                }
              }  
            }
            
            /**
             * @title BaseAdminUpgradeabilityProxy
             * @dev This contract combines an upgradeability proxy with an authorization
             * mechanism for administrative tasks.
             * All external functions in this contract must be guarded by the
             * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
             * feature proposal that would enable this to be done automatically.
             */
            contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Emitted when the administration has been transferred.
               * @param previousAdmin Address of the previous admin.
               * @param newAdmin Address of the new admin.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
            
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
               * validated in the constructor.
               */
            
              bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
            
              /**
               * @dev Modifier to check whether the `msg.sender` is the admin.
               * If it is, it will run the function. Otherwise, it will delegate the call
               * to the implementation.
               */
              modifier ifAdmin() {
                if (msg.sender == _admin()) {
                  _;
                } else {
                  _fallback();
                }
              }
            
              /**
               * @return The address of the proxy admin.
               */
              function admin() external ifAdmin returns (address) {
                return _admin();
              }
            
              /**
               * @return The address of the implementation.
               */
              function implementation() external ifAdmin returns (address) {
                return _implementation();
              }
            
              /**
               * @dev Changes the admin of the proxy.
               * Only the current admin can call this function.
               * @param newAdmin Address to transfer proxy administration to.
               */
              function changeAdmin(address newAdmin) external ifAdmin {
                require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                emit AdminChanged(_admin(), newAdmin);
                _setAdmin(newAdmin);
              }
            
              /**
               * @dev Upgrade the backing implementation of the proxy.
               * Only the admin can call this function.
               * @param newImplementation Address of the new implementation.
               */
              function upgradeTo(address newImplementation) external ifAdmin {
                _upgradeTo(newImplementation);
              }
            
              /**
               * @dev Upgrade the backing implementation of the proxy and call a function
               * on the new implementation.
               * This is useful to initialize the proxied contract.
               * @param newImplementation Address of the new implementation.
               * @param data Data to send as msg.data in the low level call.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               */
              function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
                _upgradeTo(newImplementation);
                (bool success,) = newImplementation.delegatecall(data);
                require(success);
              }
            
              /**
               * @return The admin slot.
               */
              function _admin() internal view returns (address adm) {
                bytes32 slot = ADMIN_SLOT;
                assembly {
                  adm := sload(slot)
                }
              }
            
              /**
               * @dev Sets the address of the proxy admin.
               * @param newAdmin Address of the new proxy admin.
               */
              function _setAdmin(address newAdmin) internal {
                bytes32 slot = ADMIN_SLOT;
            
                assembly {
                  sstore(slot, newAdmin)
                }
              }
            
              /**
               * @dev Only fall back when the sender is not the admin.
               */
              function _willFallback() internal {
                require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                super._willFallback();
              }
            }
            
            /**
             * @title InitializableUpgradeabilityProxy
             * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
             * implementation and init data.
             */
            contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Contract initializer.
               * @param _logic Address of the initial implementation.
               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
               */
              function initialize(address _logic, bytes memory _data) public payable {
                require(_implementation() == address(0));
                assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                _setImplementation(_logic);
                if(_data.length > 0) {
                  (bool success,) = _logic.delegatecall(_data);
                  require(success);
                }
              }  
            }
            
            contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {
              /**
               * Contract initializer.
               * @param _logic address of the initial implementation.
               * @param _admin Address of the proxy administrator.
               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
               */
              function initialize(address _logic, address _admin, bytes memory _data) public payable {
                require(_implementation() == address(0));
                InitializableUpgradeabilityProxy.initialize(_logic, _data);
                assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
                _setAdmin(_admin);
              }
            }
            
            /**
             * @notice MassetProxy delegates calls to a Masset implementation
             * @dev     Extending on OpenZeppelin's InitializableAdminUpgradabilityProxy
             * means that the proxy is upgradable through a ProxyAdmin. MassetProxy upgrades
             * are implemented by a DelayedProxyAdmin, which enforces a 1 week opt-out period.
             * All upgrades are governed through the current mStable governance.
             */
            contract MassetProxy is InitializableAdminUpgradeabilityProxy {
            }

            File 3 of 7: Masset
            pragma solidity 0.5.16;
            pragma experimental ABIEncoderV2;
            
            
            interface MassetStructs {
            
                /** @dev Stores high level basket info */
                struct Basket {
            
                    /** @dev Array of Bassets currently active */
                    Basset[] bassets;
            
                    /** @dev Max number of bAssets that can be present in any Basket */
                    uint8 maxBassets;
            
                    /** @dev Some bAsset is undergoing re-collateralisation */
                    bool undergoingRecol;
            
                    /**
                     * @dev In the event that we do not raise enough funds from the auctioning of a failed Basset,
                     * The Basket is deemed as failed, and is undercollateralised to a certain degree.
                     * The collateralisation ratio is used to calc Masset burn rate.
                     */
                    bool failed;
                    uint256 collateralisationRatio;
            
                }
            
                /** @dev Stores bAsset info. The struct takes 5 storage slots per Basset */
                struct Basset {
            
                    /** @dev Address of the bAsset */
                    address addr;
            
                    /** @dev Status of the basset,  */
                    BassetStatus status; // takes uint8 datatype (1 byte) in storage
            
                    /** @dev An ERC20 can charge transfer fee, for example USDT, DGX tokens. */
                    bool isTransferFeeCharged; // takes a byte in storage
            
                    /**
                     * @dev 1 Basset * ratio / ratioScale == x Masset (relative value)
                     *      If ratio == 10e8 then 1 bAsset = 10 mAssets
                     *      A ratio is divised as 10^(18-tokenDecimals) * measurementMultiple(relative value of 1 base unit)
                     */
                    uint256 ratio;
            
                    /** @dev Target weights of the Basset (100% == 1e18) */
                    uint256 maxWeight;
            
                    /** @dev Amount of the Basset that is held in Collateral */
                    uint256 vaultBalance;
            
                }
            
                /** @dev Status of the Basset - has it broken its peg? */
                enum BassetStatus {
                    Default,
                    Normal,
                    BrokenBelowPeg,
                    BrokenAbovePeg,
                    Blacklisted,
                    Liquidating,
                    Liquidated,
                    Failed
                }
            
                /** @dev Internal details on Basset */
                struct BassetDetails {
                    Basset bAsset;
                    address integrator;
                    uint8 index;
                }
            
                /** @dev All details needed to Forge with multiple bAssets */
                struct ForgePropsMulti {
                    bool isValid; // Flag to signify that forge bAssets have passed validity check
                    Basset[] bAssets;
                    address[] integrators;
                    uint8[] indexes;
                }
            
                /** @dev All details needed for proportionate Redemption */
                struct RedeemPropsMulti {
                    uint256 colRatio;
                    Basset[] bAssets;
                    address[] integrators;
                    uint8[] indexes;
                }
            }
            
            contract IForgeValidator is MassetStructs {
                function validateMint(uint256 _totalVault, Basset calldata _basset, uint256 _bAssetQuantity)
                    external pure returns (bool, string memory);
                function validateMintMulti(uint256 _totalVault, Basset[] calldata _bassets, uint256[] calldata _bAssetQuantities)
                    external pure returns (bool, string memory);
                function validateSwap(uint256 _totalVault, Basset calldata _inputBasset, Basset calldata _outputBasset, uint256 _quantity)
                    external pure returns (bool, string memory, uint256, bool);
                function validateRedemption(
                    bool basketIsFailed,
                    uint256 _totalVault,
                    Basset[] calldata _allBassets,
                    uint8[] calldata _indices,
                    uint256[] calldata _bassetQuantities) external pure returns (bool, string memory, bool);
                function calculateRedemptionMulti(
                    uint256 _mAssetQuantity,
                    Basset[] calldata _allBassets) external pure returns (bool, string memory, uint256[] memory);
            }
            
            interface IPlatformIntegration {
            
                /**
                 * @dev Deposit the given bAsset to Lending platform
                 * @param _bAsset bAsset address
                 * @param _amount Amount to deposit
                 */
                function deposit(address _bAsset, uint256 _amount, bool isTokenFeeCharged)
                    external returns (uint256 quantityDeposited);
            
                /**
                 * @dev Withdraw given bAsset from Lending platform
                 */
                function withdraw(address _receiver, address _bAsset, uint256 _amount, bool _isTokenFeeCharged) external;
            
                /**
                 * @dev Returns the current balance of the given bAsset
                 */
                function checkBalance(address _bAsset) external returns (uint256 balance);
            }
            
            contract IBasketManager is MassetStructs {
            
                /** @dev Setters for mAsset to update balances */
                function increaseVaultBalance(
                    uint8 _bAsset,
                    address _integrator,
                    uint256 _increaseAmount) external;
                function increaseVaultBalances(
                    uint8[] calldata _bAsset,
                    address[] calldata _integrator,
                    uint256[] calldata _increaseAmount) external;
                function decreaseVaultBalance(
                    uint8 _bAsset,
                    address _integrator,
                    uint256 _decreaseAmount) external;
                function decreaseVaultBalances(
                    uint8[] calldata _bAsset,
                    address[] calldata _integrator,
                    uint256[] calldata _decreaseAmount) external;
                function collectInterest() external
                    returns (uint256 interestCollected, uint256[] memory gains);
            
                /** @dev Setters for Gov to update Basket composition */
                function addBasset(
                    address _basset,
                    address _integration,
                    bool _isTransferFeeCharged) external returns (uint8 index);
                function setBasketWeights(address[] calldata _bassets, uint256[] calldata _weights) external;
                function setTransferFeesFlag(address _bAsset, bool _flag) external;
            
                /** @dev Getters to retrieve Basket information */
                function getBasket() external view returns (Basket memory b);
                function prepareForgeBasset(address _token, uint256 _amt, bool _mint) external
                    returns (bool isValid, BassetDetails memory bInfo);
                function prepareSwapBassets(address _input, address _output, bool _isMint) external view
                    returns (bool, string memory, BassetDetails memory, BassetDetails memory);
                function prepareForgeBassets(address[] calldata _bAssets, uint256[] calldata _amts, bool _mint) external
                    returns (ForgePropsMulti memory props);
                function prepareRedeemMulti() external view
                    returns (RedeemPropsMulti memory props);
                function getBasset(address _token) external view
                    returns (Basset memory bAsset);
                function getBassets() external view
                    returns (Basset[] memory bAssets, uint256 len);
            
                /** @dev Recollateralisation */
                function handlePegLoss(address _basset, bool _belowPeg) external returns (bool actioned);
                function negateIsolation(address _basset) external;
            }
            
            contract IMasset is MassetStructs {
            
                /** @dev Calc interest */
                function collectInterest() external returns (uint256 massetMinted, uint256 newTotalSupply);
            
                /** @dev Minting */
                function mint(address _basset, uint256 _bassetQuantity)
                    external returns (uint256 massetMinted);
                function mintTo(address _basset, uint256 _bassetQuantity, address _recipient)
                    external returns (uint256 massetMinted);
                function mintMulti(address[] calldata _bAssets, uint256[] calldata _bassetQuantity, address _recipient)
                    external returns (uint256 massetMinted);
            
                /** @dev Swapping */
                function swap( address _input, address _output, uint256 _quantity, address _recipient)
                    external returns (uint256 output);
                function getSwapOutput( address _input, address _output, uint256 _quantity)
                    external view returns (bool, string memory, uint256 output);
            
                /** @dev Redeeming */
                function redeem(address _basset, uint256 _bassetQuantity)
                    external returns (uint256 massetRedeemed);
                function redeemTo(address _basset, uint256 _bassetQuantity, address _recipient)
                    external returns (uint256 massetRedeemed);
                function redeemMulti(address[] calldata _bAssets, uint256[] calldata _bassetQuantities, address _recipient)
                    external returns (uint256 massetRedeemed);
                function redeemMasset(uint256 _mAssetQuantity, address _recipient) external;
            
                /** @dev Setters for the Manager or Gov to update module info */
                function upgradeForgeValidator(address _newForgeValidator) external;
            
                /** @dev Setters for Gov to set system params */
                function setSwapFee(uint256 _swapFee) external;
            
                /** @dev Getters */
                function getBasketManager() external view returns(address);
            }
            
            contract Initializable {
            
              /**
               * @dev Indicates that the contract has been initialized.
               */
              bool private initialized;
            
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private initializing;
            
              /**
               * @dev Modifier to use in the initializer function of a contract.
               */
              modifier initializer() {
                require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
            
                bool isTopLevelCall = !initializing;
                if (isTopLevelCall) {
                  initializing = true;
                  initialized = true;
                }
            
                _;
            
                if (isTopLevelCall) {
                  initializing = false;
                }
              }
            
              /// @dev Returns true if and only if the function is running in the constructor
              function isConstructor() private view returns (bool) {
                // extcodesize checks the size of the code stored in an address, and
                // address returns the current address. Since the code is still not
                // deployed when running a constructor, any checks on its code size will
                // yield zero, making it an effective way to detect if a contract is
                // under construction or not.
                address self = address(this);
                uint256 cs;
                assembly { cs := extcodesize(self) }
                return cs == 0;
              }
            
              // Reserved storage space to allow for layout changes in the future.
              uint256[50] private ______gap;
            }
            
            /*
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with GSN meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            contract Context {
                // Empty internal constructor, to prevent people from mistakenly deploying
                // an instance of this contract, which should be used via inheritance.
                constructor () internal { }
                // solhint-disable-previous-line no-empty-blocks
            
                function _msgSender() internal view returns (address payable) {
                    return msg.sender;
                }
            
                function _msgData() internal view returns (bytes memory) {
                    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                    return msg.data;
                }
            }
            
            /**
             * @dev 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);
            }
            
            /**
             * @dev Wrappers over Solidity's arithmetic operations with added overflow
             * checks.
             *
             * Arithmetic operations in Solidity wrap on overflow. This can easily result
             * in bugs, because programmers usually assume that an overflow raises an
             * error, which is the standard behavior in high level programming languages.
             * `SafeMath` restores this intuition by reverting the transaction when an
             * operation overflows.
             *
             * Using this library instead of the unchecked operations eliminates an entire
             * class of bugs, so it's recommended to use it always.
             */
            library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                    uint256 c = a + b;
                    require(c >= a, "SafeMath: addition overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 *
                 * _Available since v2.4.0._
                 */
                function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b <= a, errorMessage);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                    if (a == 0) {
                        return 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 *
                 * _Available since v2.4.0._
                 */
                function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    // Solidity only automatically asserts when dividing by 0
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 *
                 * _Available since v2.4.0._
                 */
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            contract ERC20 is Context, IERC20 {
                using SafeMath for uint256;
            
                mapping (address => uint256) private _balances;
            
                mapping (address => mapping (address => uint256)) private _allowances;
            
                uint256 private _totalSupply;
            
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public view returns (uint256) {
                    return _totalSupply;
                }
            
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public view returns (uint256) {
                    return _balances[account];
                }
            
                /**
                 * @dev See {IERC20-transfer}.
                 *
                 * Requirements:
                 *
                 * - `recipient` cannot be the zero address.
                 * - the caller must have a balance of at least `amount`.
                 */
                function transfer(address recipient, uint256 amount) public returns (bool) {
                    _transfer(_msgSender(), recipient, amount);
                    return true;
                }
            
                /**
                 * @dev See {IERC20-allowance}.
                 */
                function allowance(address owner, address spender) public view returns (uint256) {
                    return _allowances[owner][spender];
                }
            
                /**
                 * @dev See {IERC20-approve}.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function approve(address spender, uint256 amount) public returns (bool) {
                    _approve(_msgSender(), spender, amount);
                    return true;
                }
            
                /**
                 * @dev See {IERC20-transferFrom}.
                 *
                 * Emits an {Approval} event indicating the updated allowance. This is not
                 * required by the EIP. See the note at the beginning of {ERC20};
                 *
                 * Requirements:
                 * - `sender` and `recipient` cannot be the zero address.
                 * - `sender` must have a balance of at least `amount`.
                 * - the caller must have allowance for `sender`'s tokens of at least
                 * `amount`.
                 */
                function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
                    _transfer(sender, recipient, amount);
                    _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                    return true;
                }
            
                /**
                 * @dev Atomically increases the allowance granted to `spender` by the caller.
                 *
                 * This is an alternative to {approve} that can be used as a mitigation for
                 * problems described in {IERC20-approve}.
                 *
                 * Emits an {Approval} event indicating the updated allowance.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                    return true;
                }
            
                /**
                 * @dev Atomically decreases the allowance granted to `spender` by the caller.
                 *
                 * This is an alternative to {approve} that can be used as a mitigation for
                 * problems described in {IERC20-approve}.
                 *
                 * Emits an {Approval} event indicating the updated allowance.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 * - `spender` must have allowance for the caller of at least
                 * `subtractedValue`.
                 */
                function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                    return true;
                }
            
                /**
                 * @dev Moves tokens `amount` from `sender` to `recipient`.
                 *
                 * This is internal function is equivalent to {transfer}, and can be used to
                 * e.g. implement automatic token fees, slashing mechanisms, etc.
                 *
                 * Emits a {Transfer} event.
                 *
                 * Requirements:
                 *
                 * - `sender` cannot be the zero address.
                 * - `recipient` cannot be the zero address.
                 * - `sender` must have a balance of at least `amount`.
                 */
                function _transfer(address sender, address recipient, uint256 amount) internal {
                    require(sender != address(0), "ERC20: transfer from the zero address");
                    require(recipient != address(0), "ERC20: transfer to the zero address");
            
                    _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                    _balances[recipient] = _balances[recipient].add(amount);
                    emit Transfer(sender, recipient, amount);
                }
            
                /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                 * the total supply.
                 *
                 * Emits a {Transfer} event with `from` set to the zero address.
                 *
                 * Requirements
                 *
                 * - `to` cannot be the zero address.
                 */
                function _mint(address account, uint256 amount) internal {
                    require(account != address(0), "ERC20: mint to the zero address");
            
                    _totalSupply = _totalSupply.add(amount);
                    _balances[account] = _balances[account].add(amount);
                    emit Transfer(address(0), account, amount);
                }
            
                /**
                 * @dev Destroys `amount` tokens from `account`, reducing the
                 * total supply.
                 *
                 * Emits a {Transfer} event with `to` set to the zero address.
                 *
                 * Requirements
                 *
                 * - `account` cannot be the zero address.
                 * - `account` must have at least `amount` tokens.
                 */
                function _burn(address account, uint256 amount) internal {
                    require(account != address(0), "ERC20: burn from the zero address");
            
                    _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                    _totalSupply = _totalSupply.sub(amount);
                    emit Transfer(account, address(0), amount);
                }
            
                /**
                 * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                 *
                 * This is internal function is equivalent to `approve`, and can be used to
                 * e.g. set automatic allowances for certain subsystems, etc.
                 *
                 * Emits an {Approval} event.
                 *
                 * Requirements:
                 *
                 * - `owner` cannot be the zero address.
                 * - `spender` cannot be the zero address.
                 */
                function _approve(address owner, address spender, uint256 amount) internal {
                    require(owner != address(0), "ERC20: approve from the zero address");
                    require(spender != address(0), "ERC20: approve to the zero address");
            
                    _allowances[owner][spender] = amount;
                    emit Approval(owner, spender, amount);
                }
            
                /**
                 * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
                 * from the caller's allowance.
                 *
                 * See {_burn} and {_approve}.
                 */
                function _burnFrom(address account, uint256 amount) internal {
                    _burn(account, amount);
                    _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                }
            }
            
            contract InitializableERC20Detailed is IERC20 {
                string private _name;
                string private _symbol;
                uint8 private _decimals;
            
                /**
                 * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
                 * these values are immutable: they can only be set once during
                 * construction.
                 * @notice To avoid variable shadowing appended `Arg` after arguments name.
                 */
                function _initialize(string memory nameArg, string memory symbolArg, uint8 decimalsArg) internal {
                    _name = nameArg;
                    _symbol = symbolArg;
                    _decimals = decimalsArg;
                }
            
                /**
                 * @dev Returns the name of the token.
                 */
                function name() public view returns (string memory) {
                    return _name;
                }
            
                /**
                 * @dev Returns the symbol of the token, usually a shorter version of the
                 * name.
                 */
                function symbol() public view returns (string memory) {
                    return _symbol;
                }
            
                /**
                 * @dev Returns the number of decimals used to get its user representation.
                 * For example, if `decimals` equals `2`, a balance of `505` tokens should
                 * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                 *
                 * Tokens usually opt for a value of 18, imitating the relationship between
                 * Ether and Wei.
                 *
                 * NOTE: This information is only used for _display_ purposes: it in
                 * no way affects any of the arithmetic of the contract, including
                 * {IERC20-balanceOf} and {IERC20-transfer}.
                 */
                function decimals() public view returns (uint8) {
                    return _decimals;
                }
            }
            
            contract InitializableToken is ERC20, InitializableERC20Detailed {
            
                /**
                 * @dev Initialization function for implementing contract
                 * @notice To avoid variable shadowing appended `Arg` after arguments name.
                 */
                function _initialize(string memory _nameArg, string memory _symbolArg) internal {
                    InitializableERC20Detailed._initialize(_nameArg, _symbolArg, 18);
                }
            }
            
            contract InitializableModuleKeys {
            
                // Governance                             // Phases
                bytes32 internal KEY_GOVERNANCE;          // 2.x
                bytes32 internal KEY_STAKING;             // 1.2
                bytes32 internal KEY_PROXY_ADMIN;         // 1.0
            
                // mStable
                bytes32 internal KEY_ORACLE_HUB;          // 1.2
                bytes32 internal KEY_MANAGER;             // 1.2
                bytes32 internal KEY_RECOLLATERALISER;    // 2.x
                bytes32 internal KEY_META_TOKEN;          // 1.1
                bytes32 internal KEY_SAVINGS_MANAGER;     // 1.0
            
                /**
                 * @dev Initialize function for upgradable proxy contracts. This function should be called
                 *      via Proxy to initialize constants in the Proxy contract.
                 */
                function _initialize() internal {
                    // keccak256() values are evaluated only once at the time of this function call.
                    // Hence, no need to assign hard-coded values to these variables.
                    KEY_GOVERNANCE = keccak256("Governance");
                    KEY_STAKING = keccak256("Staking");
                    KEY_PROXY_ADMIN = keccak256("ProxyAdmin");
            
                    KEY_ORACLE_HUB = keccak256("OracleHub");
                    KEY_MANAGER = keccak256("Manager");
                    KEY_RECOLLATERALISER = keccak256("Recollateraliser");
                    KEY_META_TOKEN = keccak256("MetaToken");
                    KEY_SAVINGS_MANAGER = keccak256("SavingsManager");
                }
            }
            
            interface INexus {
                function governor() external view returns (address);
                function getModule(bytes32 key) external view returns (address);
            
                function proposeModule(bytes32 _key, address _addr) external;
                function cancelProposedModule(bytes32 _key) external;
                function acceptProposedModule(bytes32 _key) external;
                function acceptProposedModules(bytes32[] calldata _keys) external;
            
                function requestLockModule(bytes32 _key) external;
                function cancelLockModule(bytes32 _key) external;
                function lockModule(bytes32 _key) external;
            }
            
            contract InitializableModule is InitializableModuleKeys {
            
                INexus public nexus;
            
                /**
                 * @dev Modifier to allow function calls only from the Governor.
                 */
                modifier onlyGovernor() {
                    require(msg.sender == _governor(), "Only governor can execute");
                    _;
                }
            
                /**
                 * @dev Modifier to allow function calls only from the Governance.
                 *      Governance is either Governor address or Governance address.
                 */
                modifier onlyGovernance() {
                    require(
                        msg.sender == _governor() || msg.sender == _governance(),
                        "Only governance can execute"
                    );
                    _;
                }
            
                /**
                 * @dev Modifier to allow function calls only from the ProxyAdmin.
                 */
                modifier onlyProxyAdmin() {
                    require(
                        msg.sender == _proxyAdmin(), "Only ProxyAdmin can execute"
                    );
                    _;
                }
            
                /**
                 * @dev Modifier to allow function calls only from the Manager.
                 */
                modifier onlyManager() {
                    require(msg.sender == _manager(), "Only manager can execute");
                    _;
                }
            
                /**
                 * @dev Initialization function for upgradable proxy contracts
                 * @param _nexus Nexus contract address
                 */
                function _initialize(address _nexus) internal {
                    require(_nexus != address(0), "Nexus address is zero");
                    nexus = INexus(_nexus);
                    InitializableModuleKeys._initialize();
                }
            
                /**
                 * @dev Returns Governor address from the Nexus
                 * @return Address of Governor Contract
                 */
                function _governor() internal view returns (address) {
                    return nexus.governor();
                }
            
                /**
                 * @dev Returns Governance Module address from the Nexus
                 * @return Address of the Governance (Phase 2)
                 */
                function _governance() internal view returns (address) {
                    return nexus.getModule(KEY_GOVERNANCE);
                }
            
                /**
                 * @dev Return Staking Module address from the Nexus
                 * @return Address of the Staking Module contract
                 */
                function _staking() internal view returns (address) {
                    return nexus.getModule(KEY_STAKING);
                }
            
                /**
                 * @dev Return ProxyAdmin Module address from the Nexus
                 * @return Address of the ProxyAdmin Module contract
                 */
                function _proxyAdmin() internal view returns (address) {
                    return nexus.getModule(KEY_PROXY_ADMIN);
                }
            
                /**
                 * @dev Return MetaToken Module address from the Nexus
                 * @return Address of the MetaToken Module contract
                 */
                function _metaToken() internal view returns (address) {
                    return nexus.getModule(KEY_META_TOKEN);
                }
            
                /**
                 * @dev Return OracleHub Module address from the Nexus
                 * @return Address of the OracleHub Module contract
                 */
                function _oracleHub() internal view returns (address) {
                    return nexus.getModule(KEY_ORACLE_HUB);
                }
            
                /**
                 * @dev Return Manager Module address from the Nexus
                 * @return Address of the Manager Module contract
                 */
                function _manager() internal view returns (address) {
                    return nexus.getModule(KEY_MANAGER);
                }
            
                /**
                 * @dev Return SavingsManager Module address from the Nexus
                 * @return Address of the SavingsManager Module contract
                 */
                function _savingsManager() internal view returns (address) {
                    return nexus.getModule(KEY_SAVINGS_MANAGER);
                }
            
                /**
                 * @dev Return Recollateraliser Module address from the Nexus
                 * @return  Address of the Recollateraliser Module contract (Phase 2)
                 */
                function _recollateraliser() internal view returns (address) {
                    return nexus.getModule(KEY_RECOLLATERALISER);
                }
            }
            
            contract InitializableReentrancyGuard {
                bool private _notEntered;
            
                function _initialize() internal {
                    // Storing an initial non-zero value makes deployment a bit more
                    // expensive, but in exchange the refund on every call to nonReentrant
                    // will be lower in amount. Since refunds are capped to a percetange of
                    // the total transaction's gas, it is best to keep them low in cases
                    // like this one, to increase the likelihood of the full refund coming
                    // into effect.
                    _notEntered = true;
                }
            
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and make it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    // On the first call to nonReentrant, _notEntered will be true
                    require(_notEntered, "ReentrancyGuard: reentrant call");
            
                    // Any calls to nonReentrant after this point will fail
                    _notEntered = false;
            
                    _;
            
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _notEntered = true;
                }
            }
            
            library StableMath {
            
                using SafeMath for uint256;
            
                /**
                 * @dev Scaling unit for use in specific calculations,
                 * where 1 * 10**18, or 1e18 represents a unit '1'
                 */
                uint256 private constant FULL_SCALE = 1e18;
            
                /**
                 * @notice Token Ratios are used when converting between units of bAsset, mAsset and MTA
                 * Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold)
                 * @dev bAsset ratio unit for use in exact calculations,
                 * where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit
                 */
                uint256 private constant RATIO_SCALE = 1e8;
            
                /**
                 * @dev Provides an interface to the scaling unit
                 * @return Scaling unit (1e18 or 1 * 10**18)
                 */
                function getFullScale() internal pure returns (uint256) {
                    return FULL_SCALE;
                }
            
                /**
                 * @dev Provides an interface to the ratio unit
                 * @return Ratio scale unit (1e8 or 1 * 10**8)
                 */
                function getRatioScale() internal pure returns (uint256) {
                    return RATIO_SCALE;
                }
            
                /**
                 * @dev Scales a given integer to the power of the full scale.
                 * @param x   Simple uint256 to scale
                 * @return    Scaled value a to an exact number
                 */
                function scaleInteger(uint256 x)
                    internal
                    pure
                    returns (uint256)
                {
                    return x.mul(FULL_SCALE);
                }
            
                /***************************************
                          PRECISE ARITHMETIC
                ****************************************/
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the full scale
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit
                 */
                function mulTruncate(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return mulTruncateScale(x, y, FULL_SCALE);
                }
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the given scale. For example,
                 * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @param scale Scale unit
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit
                 */
                function mulTruncateScale(uint256 x, uint256 y, uint256 scale)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. assume scale = fullScale
                    // z = 10e18 * 9e17 = 9e36
                    uint256 z = x.mul(y);
                    // return 9e38 / 1e18 = 9e18
                    return z.div(scale);
                }
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit, rounded up to the closest base unit.
                 */
                function mulTruncateCeil(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. 8e17 * 17268172638 = 138145381104e17
                    uint256 scaled = x.mul(y);
                    // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17
                    uint256 ceil = scaled.add(FULL_SCALE.sub(1));
                    // e.g. 13814538111.399...e18 / 1e18 = 13814538111
                    return ceil.div(FULL_SCALE);
                }
            
                /**
                 * @dev Precisely divides two units, by first scaling the left hand operand. Useful
                 *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
                 * @param x     Left hand input to division
                 * @param y     Right hand input to division
                 * @return      Result after multiplying the left operand by the scale, and
                 *              executing the division on the right hand input.
                 */
                function divPrecisely(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. 8e18 * 1e18 = 8e36
                    uint256 z = x.mul(FULL_SCALE);
                    // e.g. 8e36 / 10e18 = 8e17
                    return z.div(y);
                }
            
            
                /***************************************
                              RATIO FUNCS
                ****************************************/
            
                /**
                 * @dev Multiplies and truncates a token ratio, essentially flooring the result
                 *      i.e. How much mAsset is this bAsset worth?
                 * @param x     Left hand operand to multiplication (i.e Exact quantity)
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the two inputs and then dividing by the ratio scale
                 */
                function mulRatioTruncate(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256 c)
                {
                    return mulTruncateScale(x, ratio, RATIO_SCALE);
                }
            
                /**
                 * @dev Multiplies and truncates a token ratio, rounding up the result
                 *      i.e. How much mAsset is this bAsset worth?
                 * @param x     Left hand input to multiplication (i.e Exact quantity)
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              ratio scale, rounded up to the closest base unit.
                 */
                function mulRatioTruncateCeil(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. How much mAsset should I burn for this bAsset (x)?
                    // 1e18 * 1e8 = 1e26
                    uint256 scaled = x.mul(ratio);
                    // 1e26 + 9.99e7 = 100..00.999e8
                    uint256 ceil = scaled.add(RATIO_SCALE.sub(1));
                    // return 100..00.999e8 / 1e8 = 1e18
                    return ceil.div(RATIO_SCALE);
                }
            
            
                /**
                 * @dev Precisely divides two ratioed units, by first scaling the left hand operand
                 *      i.e. How much bAsset is this mAsset worth?
                 * @param x     Left hand operand in division
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the left operand by the scale, and
                 *              executing the division on the right hand input.
                 */
                function divRatioPrecisely(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256 c)
                {
                    // e.g. 1e14 * 1e8 = 1e22
                    uint256 y = x.mul(RATIO_SCALE);
                    // return 1e22 / 1e12 = 1e10
                    return y.div(ratio);
                }
            
                /***************************************
                                HELPERS
                ****************************************/
            
                /**
                 * @dev Calculates minimum of two numbers
                 * @param x     Left hand input
                 * @param y     Right hand input
                 * @return      Minimum of the two inputs
                 */
                function min(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > y ? y : x;
                }
            
                /**
                 * @dev Calculated maximum of two numbers
                 * @param x     Left hand input
                 * @param y     Right hand input
                 * @return      Maximum of the two inputs
                 */
                function max(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > y ? x : y;
                }
            
                /**
                 * @dev Clamps a value to an upper bound
                 * @param x           Left hand input
                 * @param upperBound  Maximum possible value to return
                 * @return            Input x clamped to a maximum value, upperBound
                 */
                function clamp(uint256 x, uint256 upperBound)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > upperBound ? upperBound : x;
                }
            }
            
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following 
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                    // for accounts without code, i.e. `keccak256('')`
                    bytes32 codehash;
                    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { codehash := extcodehash(account) }
                    return (codehash != accountHash && codehash != 0x0);
                }
            
                /**
                 * @dev Converts an `address` into `address payable`. Note that this is
                 * simply a type cast: the actual underlying value is not changed.
                 *
                 * _Available since v2.4.0._
                 */
                function toPayable(address account) internal pure returns (address payable) {
                    return address(uint160(account));
                }
            
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 *
                 * _Available since v2.4.0._
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
            
                    // solhint-disable-next-line avoid-call-value
                    (bool success, ) = recipient.call.value(amount)("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
            }
            
            library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
            
                function safeTransfer(IERC20 token, address to, uint256 value) internal {
                    callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
            
                function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                    callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
            
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    // solhint-disable-next-line max-line-length
                    require((value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
            
                function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).add(value);
                    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves.
            
                    // A Solidity high level call has three parts:
                    //  1. The target address is checked to verify it contains contract code
                    //  2. The call itself is made, and success asserted
                    //  3. The return value is decoded, which in turn checks the size of the returned data.
                    // solhint-disable-next-line max-line-length
                    require(address(token).isContract(), "SafeERC20: call to non-contract");
            
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = address(token).call(data);
                    require(success, "SafeERC20: low-level call failed");
            
                    if (returndata.length > 0) { // Return data is optional
                        // solhint-disable-next-line max-line-length
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            
            library MassetHelpers {
            
                using StableMath for uint256;
                using SafeMath for uint256;
                using SafeERC20 for IERC20;
            
                function transferTokens(
                    address _sender,
                    address _recipient,
                    address _basset,
                    bool _erc20TransferFeeCharged,
                    uint256 _qty
                )
                    internal
                    returns (uint256 receivedQty)
                {
                    receivedQty = _qty;
                    if(_erc20TransferFeeCharged) {
                        uint256 balBefore = IERC20(_basset).balanceOf(_recipient);
                        IERC20(_basset).safeTransferFrom(_sender, _recipient, _qty);
                        uint256 balAfter = IERC20(_basset).balanceOf(_recipient);
                        receivedQty = StableMath.min(_qty, balAfter.sub(balBefore));
                    } else {
                        IERC20(_basset).safeTransferFrom(_sender, _recipient, _qty);
                    }
                }
            
                function safeInfiniteApprove(address _asset, address _spender)
                    internal
                {
                    IERC20(_asset).safeApprove(_spender, 0);
                    IERC20(_asset).safeApprove(_spender, uint256(-1));
                }
            }
            
            /**
             * @title   Masset
             * @author  Stability Labs Pty. Ltd.
             * @notice  The Masset is a token that allows minting and redemption at a 1:1 ratio
             *          for underlying basket assets (bAssets) of the same peg (i.e. USD,
             *          EUR, Gold). Composition and validation is enforced via the BasketManager.
             * @dev     VERSION: 1.1
             *          DATE:    2020-06-30
             */
            contract Masset is
                Initializable,
                IMasset,
                InitializableToken,
                InitializableModule,
                InitializableReentrancyGuard
            {
                using StableMath for uint256;
            
                // Forging Events
                event Minted(address indexed minter, address recipient, uint256 mAssetQuantity, address bAsset, uint256 bAssetQuantity);
                event MintedMulti(address indexed minter, address recipient, uint256 mAssetQuantity, address[] bAssets, uint256[] bAssetQuantities);
                event Swapped(address indexed swapper, address input, address output, uint256 outputAmount, address recipient);
                event Redeemed(address indexed redeemer, address recipient, uint256 mAssetQuantity, address[] bAssets, uint256[] bAssetQuantities);
                event RedeemedMasset(address indexed redeemer, address recipient, uint256 mAssetQuantity);
                event PaidFee(address indexed payer, address asset, uint256 feeQuantity);
            
                // State Events
                event SwapFeeChanged(uint256 fee);
                event RedemptionFeeChanged(uint256 fee);
                event ForgeValidatorChanged(address forgeValidator);
            
                // Modules and connectors
                IForgeValidator public forgeValidator;
                bool private forgeValidatorLocked;
                IBasketManager private basketManager;
            
                // Basic redemption fee information
                uint256 public swapFee;
                uint256 private MAX_FEE;
            
                /**
                 * @dev Constructor
                 * @notice To avoid variable shadowing appended `Arg` after arguments name.
                 */
                function initialize(
                    string calldata _nameArg,
                    string calldata _symbolArg,
                    address _nexus,
                    address _forgeValidator,
                    address _basketManager
                )
                    external
                    initializer
                {
                    InitializableToken._initialize(_nameArg, _symbolArg);
                    InitializableModule._initialize(_nexus);
                    InitializableReentrancyGuard._initialize();
            
                    forgeValidator = IForgeValidator(_forgeValidator);
            
                    basketManager = IBasketManager(_basketManager);
            
                    MAX_FEE = 2e16;
                    swapFee = 4e15;
                }
            
                /**
                  * @dev Verifies that the caller is the Savings Manager contract
                  */
                modifier onlySavingsManager() {
                    require(_savingsManager() == msg.sender, "Must be savings manager");
                    _;
                }
            
            
                /***************************************
                            MINTING (PUBLIC)
                ****************************************/
            
                /**
                 * @dev Mint a single bAsset, at a 1:1 ratio with the bAsset. This contract
                 *      must have approval to spend the senders bAsset
                 * @param _bAsset         Address of the bAsset to mint
                 * @param _bAssetQuantity Quantity in bAsset units
                 * @return massetMinted   Number of newly minted mAssets
                 */
                function mint(
                    address _bAsset,
                    uint256 _bAssetQuantity
                )
                    external
                    nonReentrant
                    returns (uint256 massetMinted)
                {
                    return _mintTo(_bAsset, _bAssetQuantity, msg.sender);
                }
            
                /**
                 * @dev Mint a single bAsset, at a 1:1 ratio with the bAsset. This contract
                 *      must have approval to spend the senders bAsset
                 * @param _bAsset         Address of the bAsset to mint
                 * @param _bAssetQuantity Quantity in bAsset units
                 * @param _recipient receipient of the newly minted mAsset tokens
                 * @return massetMinted   Number of newly minted mAssets
                 */
                function mintTo(
                    address _bAsset,
                    uint256 _bAssetQuantity,
                    address _recipient
                )
                    external
                    nonReentrant
                    returns (uint256 massetMinted)
                {
                    return _mintTo(_bAsset, _bAssetQuantity, _recipient);
                }
            
                /**
                 * @dev Mint with multiple bAssets, at a 1:1 ratio to mAsset. This contract
                 *      must have approval to spend the senders bAssets
                 * @param _bAssets          Non-duplicate address array of bAssets with which to mint
                 * @param _bAssetQuantity   Quantity of each bAsset to mint. Order of array
                 *                          should mirror the above
                 * @param _recipient        Address to receive the newly minted mAsset tokens
                 * @return massetMinted     Number of newly minted mAssets
                 */
                function mintMulti(
                    address[] calldata _bAssets,
                    uint256[] calldata _bAssetQuantity,
                    address _recipient
                )
                    external
                    nonReentrant
                    returns(uint256 massetMinted)
                {
                    return _mintTo(_bAssets, _bAssetQuantity, _recipient);
                }
            
                /***************************************
                          MINTING (INTERNAL)
                ****************************************/
            
                /** @dev Mint Single */
                function _mintTo(
                    address _bAsset,
                    uint256 _bAssetQuantity,
                    address _recipient
                )
                    internal
                    returns (uint256 massetMinted)
                {
                    require(_recipient != address(0), "Must be a valid recipient");
                    require(_bAssetQuantity > 0, "Quantity must not be 0");
            
                    (bool isValid, BassetDetails memory bInfo) = basketManager.prepareForgeBasset(_bAsset, _bAssetQuantity, true);
                    if(!isValid) return 0;
            
                    // Transfer collateral to the platform integration address and call deposit
                    address integrator = bInfo.integrator;
                    (uint256 quantityDeposited, uint256 ratioedDeposit) =
                        _depositTokens(_bAsset, bInfo.bAsset.ratio, integrator, bInfo.bAsset.isTransferFeeCharged, _bAssetQuantity);
            
                    // Validation should be after token transfer, as bAssetQty is unknown before
                    (bool mintValid, string memory reason) = forgeValidator.validateMint(totalSupply(), bInfo.bAsset, quantityDeposited);
                    require(mintValid, reason);
            
                    // Log the Vault increase - can only be done when basket is healthy
                    basketManager.increaseVaultBalance(bInfo.index, integrator, quantityDeposited);
            
                    // Mint the Masset
                    _mint(_recipient, ratioedDeposit);
                    emit Minted(msg.sender, _recipient, ratioedDeposit, _bAsset, quantityDeposited);
            
                    return ratioedDeposit;
                }
            
                /** @dev Mint Multi */
                function _mintTo(
                    address[] memory _bAssets,
                    uint256[] memory _bAssetQuantities,
                    address _recipient
                )
                    internal
                    returns (uint256 massetMinted)
                {
                    require(_recipient != address(0), "Must be a valid recipient");
                    uint256 len = _bAssetQuantities.length;
                    require(len > 0 && len == _bAssets.length, "Input array mismatch");
            
                    // Load only needed bAssets in array
                    ForgePropsMulti memory props
                        = basketManager.prepareForgeBassets(_bAssets, _bAssetQuantities, true);
                    if(!props.isValid) return 0;
            
                    uint256 mAssetQuantity = 0;
                    uint256[] memory receivedQty = new uint256[](len);
            
                    // Transfer the Bassets to the integrator, update storage and calc MassetQ
                    for(uint256 i = 0; i < len; i++){
                        uint256 bAssetQuantity = _bAssetQuantities[i];
                        if(bAssetQuantity > 0){
                            // bAsset == bAssets[i] == basket.bassets[indexes[i]]
                            Basset memory bAsset = props.bAssets[i];
            
                            (uint256 quantityDeposited, uint256 ratioedDeposit) =
                                _depositTokens(bAsset.addr, bAsset.ratio, props.integrators[i], bAsset.isTransferFeeCharged, bAssetQuantity);
            
                            receivedQty[i] = quantityDeposited;
                            mAssetQuantity = mAssetQuantity.add(ratioedDeposit);
                        }
                    }
                    require(mAssetQuantity > 0, "No masset quantity to mint");
            
                    basketManager.increaseVaultBalances(props.indexes, props.integrators, receivedQty);
            
                    // Validate the proposed mint, after token transfer
                    (bool mintValid, string memory reason) = forgeValidator.validateMintMulti(totalSupply(), props.bAssets, receivedQty);
                    require(mintValid, reason);
            
                    // Mint the Masset
                    _mint(_recipient, mAssetQuantity);
                    emit MintedMulti(msg.sender, _recipient, mAssetQuantity, _bAssets, _bAssetQuantities);
            
                    return mAssetQuantity;
                }
            
                /** @dev Deposits tokens into the platform integration and returns the ratioed amount */
                function _depositTokens(
                    address _bAsset,
                    uint256 _bAssetRatio,
                    address _integrator,
                    bool _erc20TransferFeeCharged,
                    uint256 _quantity
                )
                    internal
                    returns (uint256 quantityDeposited, uint256 ratioedDeposit)
                {
                    quantityDeposited = _depositTokens(_bAsset, _integrator, _erc20TransferFeeCharged, _quantity);
                    ratioedDeposit = quantityDeposited.mulRatioTruncate(_bAssetRatio);
                }
            
                /** @dev Deposits tokens into the platform integration and returns the deposited amount */
                function _depositTokens(
                    address _bAsset,
                    address _integrator,
                    bool _erc20TransferFeeCharged,
                    uint256 _quantity
                )
                    internal
                    returns (uint256 quantityDeposited)
                {
                    uint256 quantityTransferred = MassetHelpers.transferTokens(msg.sender, _integrator, _bAsset, _erc20TransferFeeCharged, _quantity);
                    uint256 deposited = IPlatformIntegration(_integrator).deposit(_bAsset, quantityTransferred, _erc20TransferFeeCharged);
                    quantityDeposited = StableMath.min(deposited, _quantity);
                }
            
            
                /***************************************
                            SWAP (PUBLIC)
                ****************************************/
            
                /**
                 * @dev Simply swaps one bAsset for another bAsset or this mAsset at a 1:1 ratio.
                 * bAsset <> bAsset swaps will incur a small fee (swapFee()). Swap
                 * is valid if it does not result in the input asset exceeding its maximum weight.
                 * @param _input        bAsset to deposit
                 * @param _output       Asset to receive - either a bAsset or mAsset(this)
                 * @param _quantity     Units of input bAsset to swap
                 * @param _recipient    Address to credit output asset
                 * @return output       Units of output asset returned
                 */
                function swap(
                    address _input,
                    address _output,
                    uint256 _quantity,
                    address _recipient
                )
                    external
                    nonReentrant
                    returns (uint256 output)
                {
                    require(_input != address(0) && _output != address(0), "Invalid swap asset addresses");
                    require(_input != _output, "Cannot swap the same asset");
                    require(_recipient != address(0), "Missing recipient address");
                    require(_quantity > 0, "Invalid quantity");
            
                    // 1. If the output is this mAsset, just mint
                    if(_output == address(this)){
                        return _mintTo(_input, _quantity, _recipient);
                    }
            
                    // 2. Grab all relevant info from the Manager
                    (bool isValid, string memory reason, BassetDetails memory inputDetails, BassetDetails memory outputDetails) =
                        basketManager.prepareSwapBassets(_input, _output, false);
                    require(isValid, reason);
            
                    // 3. Deposit the input tokens
                    uint256 quantitySwappedIn = _depositTokens(_input, inputDetails.integrator, inputDetails.bAsset.isTransferFeeCharged, _quantity);
                    // 3.1. Update the input balance
                    basketManager.increaseVaultBalance(inputDetails.index, inputDetails.integrator, quantitySwappedIn);
            
                    // 4. Validate the swap
                    (bool swapValid, string memory swapValidityReason, uint256 swapOutput, bool applySwapFee) =
                        forgeValidator.validateSwap(totalSupply(), inputDetails.bAsset, outputDetails.bAsset, quantitySwappedIn);
                    require(swapValid, swapValidityReason);
            
                    // 5. Settle the swap
                    // 5.1. Decrease output bal
                    basketManager.decreaseVaultBalance(outputDetails.index, outputDetails.integrator, swapOutput);
                    // 5.2. Calc fee, if any
                    if(applySwapFee){
                        swapOutput = _deductSwapFee(_output, swapOutput, swapFee);
                    }
                    // 5.3. Withdraw to recipient
                    IPlatformIntegration(outputDetails.integrator).withdraw(_recipient, _output, swapOutput, outputDetails.bAsset.isTransferFeeCharged);
            
                    output = swapOutput;
            
                    emit Swapped(msg.sender, _input, _output, swapOutput, _recipient);
                }
            
                /**
                 * @dev Determines both if a trade is valid, and the expected fee or output.
                 * Swap is valid if it does not result in the input asset exceeding its maximum weight.
                 * @param _input        bAsset to deposit
                 * @param _output       Asset to receive - bAsset or mAsset(this)
                 * @param _quantity     Units of input bAsset to swap
                 * @return valid        Bool to signify that swap is current valid
                 * @return reason       If swap is invalid, this is the reason
                 * @return output       Units of _output asset the trade would return
                 */
                function getSwapOutput(
                    address _input,
                    address _output,
                    uint256 _quantity
                )
                    external
                    view
                    returns (bool, string memory, uint256 output)
                {
                    require(_input != address(0) && _output != address(0), "Invalid swap asset addresses");
                    require(_input != _output, "Cannot swap the same asset");
            
                    bool isMint = _output == address(this);
                    uint256 quantity = _quantity;
            
                    // 1. Get relevant asset data
                    (bool isValid, string memory reason, BassetDetails memory inputDetails, BassetDetails memory outputDetails) =
                        basketManager.prepareSwapBassets(_input, _output, isMint);
                    if(!isValid){
                        return (false, reason, 0);
                    }
            
                    // 2. check if trade is valid
                    // 2.1. If output is mAsset(this), then calculate a simple mint
                    if(isMint){
                        // Validate mint
                        (isValid, reason) = forgeValidator.validateMint(totalSupply(), inputDetails.bAsset, quantity);
                        if(!isValid) return (false, reason, 0);
                        // Simply cast the quantity to mAsset
                        output = quantity.mulRatioTruncate(inputDetails.bAsset.ratio);
                        return(true, "", output);
                    }
                    // 2.2. If a bAsset swap, calculate the validity, output and fee
                    else {
                        (bool swapValid, string memory swapValidityReason, uint256 swapOutput, bool applySwapFee) =
                            forgeValidator.validateSwap(totalSupply(), inputDetails.bAsset, outputDetails.bAsset, quantity);
                        if(!swapValid){
                            return (false, swapValidityReason, 0);
                        }
            
                        // 3. Return output and fee, if any
                        if(applySwapFee){
                            (, swapOutput) = _calcSwapFee(swapOutput, swapFee);
                        }
                        return (true, "", swapOutput);
                    }
                }
            
            
                /***************************************
                          REDEMPTION (PUBLIC)
                ****************************************/
            
                /**
                 * @dev Credits the sender with a certain quantity of selected bAsset, in exchange for burning the
                 *      relative mAsset quantity from the sender. Sender also incurs a small mAsset fee, if any.
                 * @param _bAsset           Address of the bAsset to redeem
                 * @param _bAssetQuantity   Units of the bAsset to redeem
                 * @return massetMinted     Relative number of mAsset units burned to pay for the bAssets
                 */
                function redeem(
                    address _bAsset,
                    uint256 _bAssetQuantity
                )
                    external
                    nonReentrant
                    returns (uint256 massetRedeemed)
                {
                    return _redeemTo(_bAsset, _bAssetQuantity, msg.sender);
                }
            
                /**
                 * @dev Credits a recipient with a certain quantity of selected bAsset, in exchange for burning the
                 *      relative Masset quantity from the sender. Sender also incurs a small fee, if any.
                 * @param _bAsset           Address of the bAsset to redeem
                 * @param _bAssetQuantity   Units of the bAsset to redeem
                 * @param _recipient        Address to credit with withdrawn bAssets
                 * @return massetMinted     Relative number of mAsset units burned to pay for the bAssets
                 */
                function redeemTo(
                    address _bAsset,
                    uint256 _bAssetQuantity,
                    address _recipient
                )
                    external
                    nonReentrant
                    returns (uint256 massetRedeemed)
                {
                    return _redeemTo(_bAsset, _bAssetQuantity, _recipient);
                }
            
                /**
                 * @dev Credits a recipient with a certain quantity of selected bAssets, in exchange for burning the
                 *      relative Masset quantity from the sender. Sender also incurs a small fee, if any.
                 * @param _bAssets          Address of the bAssets to redeem
                 * @param _bAssetQuantities Units of the bAssets to redeem
                 * @param _recipient        Address to credit with withdrawn bAssets
                 * @return massetMinted     Relative number of mAsset units burned to pay for the bAssets
                 */
                function redeemMulti(
                    address[] calldata _bAssets,
                    uint256[] calldata _bAssetQuantities,
                    address _recipient
                )
                    external
                    nonReentrant
                    returns (uint256 massetRedeemed)
                {
                    return _redeemTo(_bAssets, _bAssetQuantities, _recipient);
                }
            
                /**
                 * @dev Credits a recipient with a proportionate amount of bAssets, relative to current vault
                 * balance levels and desired mAsset quantity. Burns the mAsset as payment.
                 * @param _mAssetQuantity   Quantity of mAsset to redeem
                 * @param _recipient        Address to credit the withdrawn bAssets
                 */
                function redeemMasset(
                    uint256 _mAssetQuantity,
                    address _recipient
                )
                    external
                    nonReentrant
                {
                    _redeemMasset(_mAssetQuantity, _recipient);
                }
            
                /***************************************
                          REDEMPTION (INTERNAL)
                ****************************************/
            
                /** @dev Casting to arrays for use in redeemMulti func */
                function _redeemTo(
                    address _bAsset,
                    uint256 _bAssetQuantity,
                    address _recipient
                )
                    internal
                    returns (uint256 massetRedeemed)
                {
                    address[] memory bAssets = new address[](1);
                    uint256[] memory quantities = new uint256[](1);
                    bAssets[0] = _bAsset;
                    quantities[0] = _bAssetQuantity;
                    return _redeemTo(bAssets, quantities, _recipient);
                }
            
                /** @dev Redeem mAsset for one or more bAssets */
                function _redeemTo(
                    address[] memory _bAssets,
                    uint256[] memory _bAssetQuantities,
                    address _recipient
                )
                    internal
                    returns (uint256 massetRedeemed)
                {
                    require(_recipient != address(0), "Must be a valid recipient");
                    uint256 bAssetCount = _bAssetQuantities.length;
                    require(bAssetCount > 0 && bAssetCount == _bAssets.length, "Input array mismatch");
            
                    // Get high level basket info
                    Basket memory basket = basketManager.getBasket();
            
                    // Prepare relevant data
                    ForgePropsMulti memory props = basketManager.prepareForgeBassets(_bAssets, _bAssetQuantities, false);
                    if(!props.isValid) return 0;
            
                    // Validate redemption
                    (bool redemptionValid, string memory reason, bool applyFee) =
                        forgeValidator.validateRedemption(basket.failed, totalSupply(), basket.bassets, props.indexes, _bAssetQuantities);
                    require(redemptionValid, reason);
            
                    uint256 mAssetQuantity = 0;
            
                    // Calc total redeemed mAsset quantity
                    for(uint256 i = 0; i < bAssetCount; i++){
                        uint256 bAssetQuantity = _bAssetQuantities[i];
                        if(bAssetQuantity > 0){
                            // Calc equivalent mAsset amount
                            uint256 ratioedBasset = bAssetQuantity.mulRatioTruncateCeil(props.bAssets[i].ratio);
                            mAssetQuantity = mAssetQuantity.add(ratioedBasset);
                        }
                    }
                    require(mAssetQuantity > 0, "Must redeem some bAssets");
            
                    // Redemption has fee? Fetch the rate
                    uint256 fee = applyFee ? swapFee : 0;
            
                    // Apply fees, burn mAsset and return bAsset to recipient
                    _settleRedemption(_recipient, mAssetQuantity, props.bAssets, _bAssetQuantities, props.indexes, props.integrators, fee);
            
                    emit Redeemed(msg.sender, _recipient, mAssetQuantity, _bAssets, _bAssetQuantities);
                    return mAssetQuantity;
                }
            
            
                /** @dev Redeem mAsset for a multiple bAssets */
                function _redeemMasset(
                    uint256 _mAssetQuantity,
                    address _recipient
                )
                    internal
                {
                    require(_recipient != address(0), "Must be a valid recipient");
                    require(_mAssetQuantity > 0, "Invalid redemption quantity");
            
                    // Fetch high level details
                    RedeemPropsMulti memory props = basketManager.prepareRedeemMulti();
                    uint256 colRatio = StableMath.min(props.colRatio, StableMath.getFullScale());
            
                    // Ensure payout is related to the collateralised mAsset quantity
                    uint256 collateralisedMassetQuantity = _mAssetQuantity.mulTruncate(colRatio);
            
                    // Calculate redemption quantities
                    (bool redemptionValid, string memory reason, uint256[] memory bAssetQuantities) =
                        forgeValidator.calculateRedemptionMulti(collateralisedMassetQuantity, props.bAssets);
                    require(redemptionValid, reason);
            
                    // Apply fees, burn mAsset and return bAsset to recipient
                    _settleRedemption(_recipient, _mAssetQuantity, props.bAssets, bAssetQuantities, props.indexes, props.integrators, redemptionFee);
            
                    emit RedeemedMasset(msg.sender, _recipient, _mAssetQuantity);
                }
            
                /**
                 * @dev Internal func to update contract state post-redemption
                 * @param _recipient        Recipient of the bAssets
                 * @param _mAssetQuantity   Total amount of mAsset to burn from sender
                 * @param _bAssets          Array of bAssets to redeem
                 * @param _bAssetQuantities Array of bAsset quantities
                 * @param _indices          Matching indices for the bAsset array
                 * @param _integrators      Matching integrators for the bAsset array
                 * @param _feeRate          Apply a fee to this redemption?
                 */
                function _settleRedemption(
                    address _recipient,
                    uint256 _mAssetQuantity,
                    Basset[] memory _bAssets,
                    uint256[] memory _bAssetQuantities,
                    uint8[] memory _indices,
                    address[] memory _integrators,
                    uint256 _feeRate
                ) internal {
                    // Burn the full amount of Masset
                    _burn(msg.sender, _mAssetQuantity);
            
                    // Reduce the amount of bAssets marked in the vault
                    basketManager.decreaseVaultBalances(_indices, _integrators, _bAssetQuantities);
            
                    // Transfer the Bassets to the recipient
                    uint256 bAssetCount = _bAssets.length;
                    for(uint256 i = 0; i < bAssetCount; i++){
                        address bAsset = _bAssets[i].addr;
                        uint256 q = _bAssetQuantities[i];
                        if(q > 0){
                            // Deduct the redemption fee, if any
                            q = _deductSwapFee(bAsset, q, _feeRate);
                            // Transfer the Bassets to the user
                            IPlatformIntegration(_integrators[i]).withdraw(_recipient, bAsset, q, _bAssets[i].isTransferFeeCharged);
                        }
                    }
                }
            
            
                /***************************************
                                INTERNAL
                ****************************************/
            
                /**
                 * @dev Pay the forging fee by burning relative amount of mAsset
                 * @param _bAssetQuantity     Exact amount of bAsset being swapped out
                 */
                function _deductSwapFee(address _asset, uint256 _bAssetQuantity, uint256 _feeRate)
                    private
                    returns (uint256 outputMinusFee)
                {
            
                    outputMinusFee = _bAssetQuantity;
            
                    if(_feeRate > 0){
                        (uint256 fee, uint256 output) = _calcSwapFee(_bAssetQuantity, _feeRate);
                        outputMinusFee = output;
                        emit PaidFee(msg.sender, _asset, fee);
                    }
                }
            
                /**
                 * @dev Pay the forging fee by burning relative amount of mAsset
                 * @param _bAssetQuantity     Exact amount of bAsset being swapped out
                 */
                function _calcSwapFee(uint256 _bAssetQuantity, uint256 _feeRate)
                    private
                    pure
                    returns (uint256 feeAmount, uint256 outputMinusFee)
                {
                    // e.g. for 500 massets.
                    // feeRate == 1% == 1e16. _quantity == 5e20.
                    // (5e20 * 1e16) / 1e18 = 5e18
                    feeAmount = _bAssetQuantity.mulTruncate(_feeRate);
                    outputMinusFee = _bAssetQuantity.sub(feeAmount);
                }
            
                /***************************************
                                STATE
                ****************************************/
            
                /**
                  * @dev Upgrades the version of ForgeValidator protocol. Governor can do this
                  *      only while ForgeValidator is unlocked.
                  * @param _newForgeValidator Address of the new ForgeValidator
                  */
                function upgradeForgeValidator(address _newForgeValidator)
                    external
                    onlyGovernor
                {
                    require(!forgeValidatorLocked, "Must be allowed to upgrade");
                    require(_newForgeValidator != address(0), "Must be non null address");
                    forgeValidator = IForgeValidator(_newForgeValidator);
                    emit ForgeValidatorChanged(_newForgeValidator);
                }
            
                /**
                  * @dev Locks the ForgeValidator into it's final form. Called by Governor
                  */
                function lockForgeValidator()
                    external
                    onlyGovernor
                {
                    forgeValidatorLocked = true;
                }
            
                /**
                  * @dev Set the ecosystem fee for redeeming a mAsset
                  * @param _swapFee Fee calculated in (%/100 * 1e18)
                  */
                function setSwapFee(uint256 _swapFee)
                    external
                    onlyGovernor
                {
                    require(_swapFee <= MAX_FEE, "Rate must be within bounds");
                    swapFee = _swapFee;
            
                    emit SwapFeeChanged(_swapFee);
                }
            
                /**
                  * @dev Set the ecosystem fee for redeeming a mAsset
                  * @param _redemptionFee Fee calculated in (%/100 * 1e18)
                  */
                function setRedemptionFee(uint256 _redemptionFee)
                    external
                    onlyGovernor
                {
                    require(_redemptionFee <= MAX_FEE, "Rate must be within bounds");
                    redemptionFee = _redemptionFee;
            
                    emit RedemptionFeeChanged(_redemptionFee);
                }
            
                /**
                  * @dev Gets the address of the BasketManager for this mAsset
                  * @return basketManager Address
                  */
                function getBasketManager()
                    external
                    view
                    returns (address)
                {
                    return address(basketManager);
                }
            
                /***************************************
                                INFLATION
                ****************************************/
            
                /**
                 * @dev Collects the interest generated from the Basket, minting a relative
                 *      amount of mAsset and sending it over to the SavingsManager.
                 * @return totalInterestGained   Equivalent amount of mAsset units that have been generated
                 * @return newSupply             New total mAsset supply
                 */
                function collectInterest()
                    external
                    onlySavingsManager
                    nonReentrant
                    returns (uint256 totalInterestGained, uint256 newSupply)
                {
                    (uint256 interestCollected, uint256[] memory gains) = basketManager.collectInterest();
            
                    // mint new mAsset to sender
                    _mint(msg.sender, interestCollected);
                    emit MintedMulti(address(this), address(this), interestCollected, new address[](0), gains);
            
                    return (interestCollected, totalSupply());
                }
            
                // RELEASE 1.1 VARS
                uint256 public redemptionFee;
            
            }

            File 4 of 7: BasketManagerProxy
            pragma solidity 0.5.16;
            
            
            /**
             * @title Proxy
             * @dev Implements delegation of calls to other contracts, with proper
             * forwarding of return values and bubbling of failures.
             * It defines a fallback function that delegates all calls to the address
             * returned by the abstract _implementation() internal function.
             */
            contract Proxy {
              /**
               * @dev Fallback function.
               * Implemented entirely in `_fallback`.
               */
              function () payable external {
                _fallback();
              }
            
              /**
               * @return The Address of the implementation.
               */
              function _implementation() internal view returns (address);
            
              /**
               * @dev Delegates execution to an implementation contract.
               * This is a low level function that doesn't return to its internal call site.
               * It will return to the external caller whatever the implementation returns.
               * @param implementation Address to delegate.
               */
              function _delegate(address implementation) internal {
                assembly {
                  // Copy msg.data. We take full control of memory in this inline assembly
                  // block because it will not return to Solidity code. We overwrite the
                  // Solidity scratch pad at memory position 0.
                  calldatacopy(0, 0, calldatasize)
            
                  // Call the implementation.
                  // out and outsize are 0 because we don't know the size yet.
                  let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
            
                  // Copy the returned data.
                  returndatacopy(0, 0, returndatasize)
            
                  switch result
                  // delegatecall returns 0 on error.
                  case 0 { revert(0, returndatasize) }
                  default { return(0, returndatasize) }
                }
              }
            
              /**
               * @dev Function that is run as the first thing in the fallback function.
               * Can be redefined in derived contracts to add functionality.
               * Redefinitions must call super._willFallback().
               */
              function _willFallback() internal {
              }
            
              /**
               * @dev fallback implementation.
               * Extracted to enable manual triggering.
               */
              function _fallback() internal {
                _willFallback();
                _delegate(_implementation());
              }
            }
            
            /**
             * Utility library of inline functions on addresses
             *
             * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
             * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
             * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
             * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
             */
            library OpenZeppelinUpgradesAddress {
                /**
                 * Returns whether the target address is a contract
                 * @dev This function will return false if invoked during the constructor of a contract,
                 * as the code is not actually created until after the constructor finishes.
                 * @param account address of the account to check
                 * @return whether the target address is a contract
                 */
                function isContract(address account) internal view returns (bool) {
                    uint256 size;
                    // XXX Currently there is no better way to check if there is a contract in an address
                    // than to check the size of the code at that address.
                    // See https://ethereum.stackexchange.com/a/14016/36603
                    // for more details about how this works.
                    // TODO Check this again before the Serenity release, because all addresses will be
                    // contracts then.
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
            }
            
            /**
             * @title BaseUpgradeabilityProxy
             * @dev This contract implements a proxy that allows to change the
             * implementation address to which it will delegate.
             * Such a change is called an implementation upgrade.
             */
            contract BaseUpgradeabilityProxy is Proxy {
              /**
               * @dev Emitted when the implementation is upgraded.
               * @param implementation Address of the new implementation.
               */
              event Upgraded(address indexed implementation);
            
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
            
              /**
               * @dev Returns the current implementation.
               * @return Address of the current implementation
               */
              function _implementation() internal view returns (address impl) {
                bytes32 slot = IMPLEMENTATION_SLOT;
                assembly {
                  impl := sload(slot)
                }
              }
            
              /**
               * @dev Upgrades the proxy to a new implementation.
               * @param newImplementation Address of the new implementation.
               */
              function _upgradeTo(address newImplementation) internal {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
              }
            
              /**
               * @dev Sets the implementation address of the proxy.
               * @param newImplementation Address of the new implementation.
               */
              function _setImplementation(address newImplementation) internal {
                require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
            
                bytes32 slot = IMPLEMENTATION_SLOT;
            
                assembly {
                  sstore(slot, newImplementation)
                }
              }
            }
            
            /**
             * @title UpgradeabilityProxy
             * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
             * implementation and init data.
             */
            contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Contract constructor.
               * @param _logic Address of the initial implementation.
               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
               */
              constructor(address _logic, bytes memory _data) public payable {
                assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                _setImplementation(_logic);
                if(_data.length > 0) {
                  (bool success,) = _logic.delegatecall(_data);
                  require(success);
                }
              }  
            }
            
            /**
             * @title BaseAdminUpgradeabilityProxy
             * @dev This contract combines an upgradeability proxy with an authorization
             * mechanism for administrative tasks.
             * All external functions in this contract must be guarded by the
             * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
             * feature proposal that would enable this to be done automatically.
             */
            contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Emitted when the administration has been transferred.
               * @param previousAdmin Address of the previous admin.
               * @param newAdmin Address of the new admin.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
            
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
               * validated in the constructor.
               */
            
              bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
            
              /**
               * @dev Modifier to check whether the `msg.sender` is the admin.
               * If it is, it will run the function. Otherwise, it will delegate the call
               * to the implementation.
               */
              modifier ifAdmin() {
                if (msg.sender == _admin()) {
                  _;
                } else {
                  _fallback();
                }
              }
            
              /**
               * @return The address of the proxy admin.
               */
              function admin() external ifAdmin returns (address) {
                return _admin();
              }
            
              /**
               * @return The address of the implementation.
               */
              function implementation() external ifAdmin returns (address) {
                return _implementation();
              }
            
              /**
               * @dev Changes the admin of the proxy.
               * Only the current admin can call this function.
               * @param newAdmin Address to transfer proxy administration to.
               */
              function changeAdmin(address newAdmin) external ifAdmin {
                require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                emit AdminChanged(_admin(), newAdmin);
                _setAdmin(newAdmin);
              }
            
              /**
               * @dev Upgrade the backing implementation of the proxy.
               * Only the admin can call this function.
               * @param newImplementation Address of the new implementation.
               */
              function upgradeTo(address newImplementation) external ifAdmin {
                _upgradeTo(newImplementation);
              }
            
              /**
               * @dev Upgrade the backing implementation of the proxy and call a function
               * on the new implementation.
               * This is useful to initialize the proxied contract.
               * @param newImplementation Address of the new implementation.
               * @param data Data to send as msg.data in the low level call.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               */
              function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
                _upgradeTo(newImplementation);
                (bool success,) = newImplementation.delegatecall(data);
                require(success);
              }
            
              /**
               * @return The admin slot.
               */
              function _admin() internal view returns (address adm) {
                bytes32 slot = ADMIN_SLOT;
                assembly {
                  adm := sload(slot)
                }
              }
            
              /**
               * @dev Sets the address of the proxy admin.
               * @param newAdmin Address of the new proxy admin.
               */
              function _setAdmin(address newAdmin) internal {
                bytes32 slot = ADMIN_SLOT;
            
                assembly {
                  sstore(slot, newAdmin)
                }
              }
            
              /**
               * @dev Only fall back when the sender is not the admin.
               */
              function _willFallback() internal {
                require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                super._willFallback();
              }
            }
            
            /**
             * @title InitializableUpgradeabilityProxy
             * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
             * implementation and init data.
             */
            contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
              /**
               * @dev Contract initializer.
               * @param _logic Address of the initial implementation.
               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
               */
              function initialize(address _logic, bytes memory _data) public payable {
                require(_implementation() == address(0));
                assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                _setImplementation(_logic);
                if(_data.length > 0) {
                  (bool success,) = _logic.delegatecall(_data);
                  require(success);
                }
              }  
            }
            
            /**
             * @title InitializableAdminUpgradeabilityProxy
             * @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for 
             * initializing the implementation, admin, and init data.
             */
            contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {
              /**
               * Contract initializer.
               * @param _logic address of the initial implementation.
               * @param _admin Address of the proxy administrator.
               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
               * It should include the signature and the parameters of the function to be called, as described in
               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
               */
              function initialize(address _logic, address _admin, bytes memory _data) public payable {
                require(_implementation() == address(0));
                InitializableUpgradeabilityProxy.initialize(_logic, _data);
                assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
                _setAdmin(_admin);
              }
            }
            
            /**
             * @notice MassetProxy delegates calls to a Masset implementation
             * @dev    Extending on OpenZeppelin's InitializableAdminUpgradabilityProxy
             * means that the proxy is upgradable through a ProxyAdmin. MassetProxy upgrades
             * are implemented by a DelayedProxyAdmin, which enforces a 1 week opt-out period.
             * All upgrades are governed through the current mStable governance.
             */
            contract MassetProxy is InitializableAdminUpgradeabilityProxy {
            }
            
            /**
             * @notice BasketManagerProxy delegates calls to a BasketManager implementation
             * @dev    Extending on OpenZeppelin's InitializableAdminUpgradabilityProxy
             * means that the proxy is upgradable through a ProxyAdmin. BasketManagerProxy upgrades
             * are implemented by a DelayedProxyAdmin, which enforces a 1 week opt-out period.
             * All upgrades are governed through the current mStable governance.
             */
            contract BasketManagerProxy is InitializableAdminUpgradeabilityProxy {
            }

            File 5 of 7: BasketManager
            pragma solidity 0.5.16;
            pragma experimental ABIEncoderV2;
            
            
            interface IPlatformIntegration {
            
                /**
                 * @dev Deposit the given bAsset to Lending platform
                 * @param _bAsset bAsset address
                 * @param _amount Amount to deposit
                 */
                function deposit(address _bAsset, uint256 _amount, bool isTokenFeeCharged)
                    external returns (uint256 quantityDeposited);
            
                /**
                 * @dev Withdraw given bAsset from Lending platform
                 */
                function withdraw(address _receiver, address _bAsset, uint256 _amount, bool _isTokenFeeCharged) external;
            
                /**
                 * @dev Returns the current balance of the given bAsset
                 */
                function checkBalance(address _bAsset) external returns (uint256 balance);
            }
            
            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);
            }
            
            contract InitializableModuleKeys {
            
                // Governance
                bytes32 internal KEY_GOVERNANCE;
                bytes32 internal KEY_STAKING;
                bytes32 internal KEY_PROXY_ADMIN;
            
                // mStable
                bytes32 internal KEY_ORACLE_HUB;
                bytes32 internal KEY_MANAGER;
                bytes32 internal KEY_RECOLLATERALISER;
                bytes32 internal KEY_META_TOKEN;
                bytes32 internal KEY_SAVINGS_MANAGER;
            
                /**
                 * @dev Initialize function for upgradable proxy contracts. This function should be called
                 *      via Proxy to initialize constants in the Proxy contract.
                 */
                function _initialize() internal {
                    // keccak256() values are evaluated only once at the time of this function call.
                    // Hence, no need to assign hard-coded values to these variables.
                    KEY_GOVERNANCE = keccak256("Governance");
                    KEY_STAKING = keccak256("Staking");
                    KEY_PROXY_ADMIN = keccak256("ProxyAdmin");
            
                    KEY_ORACLE_HUB = keccak256("OracleHub");
                    KEY_MANAGER = keccak256("Manager");
                    KEY_RECOLLATERALISER = keccak256("Recollateraliser");
                    KEY_META_TOKEN = keccak256("MetaToken");
                    KEY_SAVINGS_MANAGER = keccak256("SavingsManager");
                }
            }
            
            interface INexus {
                function governor() external view returns (address);
                function getModule(bytes32 key) external view returns (address);
            
                function proposeModule(bytes32 _key, address _addr) external;
                function cancelProposedModule(bytes32 _key) external;
                function acceptProposedModule(bytes32 _key) external;
                function acceptProposedModules(bytes32[] calldata _keys) external;
            
                function requestLockModule(bytes32 _key) external;
                function cancelLockModule(bytes32 _key) external;
                function lockModule(bytes32 _key) external;
            }
            
            contract InitializableModule is InitializableModuleKeys {
            
                INexus public nexus;
            
                /**
                 * @dev Modifier to allow function calls only from the Governor.
                 */
                modifier onlyGovernor() {
                    require(msg.sender == _governor(), "Only governor can execute");
                    _;
                }
            
                /**
                 * @dev Modifier to allow function calls only from the Governance.
                 *      Governance is either Governor address or Governance address.
                 */
                modifier onlyGovernance() {
                    require(
                        msg.sender == _governor() || msg.sender == _governance(),
                        "Only governance can execute"
                    );
                    _;
                }
            
                /**
                 * @dev Modifier to allow function calls only from the ProxyAdmin.
                 */
                modifier onlyProxyAdmin() {
                    require(
                        msg.sender == _proxyAdmin(), "Only ProxyAdmin can execute"
                    );
                    _;
                }
            
                /**
                 * @dev Modifier to allow function calls only from the Manager.
                 */
                modifier onlyManager() {
                    require(msg.sender == _manager(), "Only manager can execute");
                    _;
                }
            
                /**
                 * @dev Initialization function for upgradable proxy contracts
                 * @param _nexus Nexus contract address
                 */
                function _initialize(address _nexus) internal {
                    require(_nexus != address(0), "Nexus address is zero");
                    nexus = INexus(_nexus);
                    InitializableModuleKeys._initialize();
                }
            
                /**
                 * @dev Returns Governor address from the Nexus
                 * @return Address of Governor Contract
                 */
                function _governor() internal view returns (address) {
                    return nexus.governor();
                }
            
                /**
                 * @dev Returns Governance Module address from the Nexus
                 * @return Address of the Governance (Phase 2)
                 */
                function _governance() internal view returns (address) {
                    return nexus.getModule(KEY_GOVERNANCE);
                }
            
                /**
                 * @dev Return Staking Module address from the Nexus
                 * @return Address of the Staking Module contract
                 */
                function _staking() internal view returns (address) {
                    return nexus.getModule(KEY_STAKING);
                }
            
                /**
                 * @dev Return ProxyAdmin Module address from the Nexus
                 * @return Address of the ProxyAdmin Module contract
                 */
                function _proxyAdmin() internal view returns (address) {
                    return nexus.getModule(KEY_PROXY_ADMIN);
                }
            
                /**
                 * @dev Return MetaToken Module address from the Nexus
                 * @return Address of the MetaToken Module contract
                 */
                function _metaToken() internal view returns (address) {
                    return nexus.getModule(KEY_META_TOKEN);
                }
            
                /**
                 * @dev Return OracleHub Module address from the Nexus
                 * @return Address of the OracleHub Module contract
                 */
                function _oracleHub() internal view returns (address) {
                    return nexus.getModule(KEY_ORACLE_HUB);
                }
            
                /**
                 * @dev Return Manager Module address from the Nexus
                 * @return Address of the Manager Module contract
                 */
                function _manager() internal view returns (address) {
                    return nexus.getModule(KEY_MANAGER);
                }
            
                /**
                 * @dev Return SavingsManager Module address from the Nexus
                 * @return Address of the SavingsManager Module contract
                 */
                function _savingsManager() internal view returns (address) {
                    return nexus.getModule(KEY_SAVINGS_MANAGER);
                }
            
                /**
                 * @dev Return Recollateraliser Module address from the Nexus
                 * @return  Address of the Recollateraliser Module contract (Phase 2)
                 */
                function _recollateraliser() internal view returns (address) {
                    return nexus.getModule(KEY_RECOLLATERALISER);
                }
            }
            
            contract InitializablePausableModule is InitializableModule {
            
                /**
                 * @dev Emitted when the pause is triggered by Governor
                 */
                event Paused(address account);
            
                /**
                 * @dev Emitted when the pause is lifted by Governor
                 */
                event Unpaused(address account);
            
                bool private _paused = false;
            
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 */
                modifier whenNotPaused() {
                    require(!_paused, "Pausable: paused");
                    _;
                }
            
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 */
                modifier whenPaused() {
                    require(_paused, "Pausable: not paused");
                    _;
                }
            
                /**
                 * @dev Initializes the contract in unpaused state.
                 * Hooks into the Module to give the Governor ability to pause
                 * @param _nexus Nexus contract address
                 */
                function _initialize(address _nexus) internal {
                    InitializableModule._initialize(_nexus);
                    _paused = false;
                }
            
                /**
                 * @dev Returns true if the contract is paused, and false otherwise.
                 * @return Returns `true` when paused, otherwise `false`
                 */
                function paused() external view returns (bool) {
                    return _paused;
                }
            
                /**
                 * @dev Called by the Governor to pause, triggers stopped state.
                 */
                function pause() external onlyGovernor whenNotPaused {
                    _paused = true;
                    emit Paused(msg.sender);
                }
            
                /**
                 * @dev Called by Governor to unpause, returns to normal state.
                 */
                function unpause() external onlyGovernor whenPaused {
                    _paused = false;
                    emit Unpaused(msg.sender);
                }
            }
            
            interface MassetStructs {
            
                /** @dev Stores high level basket info */
                struct Basket {
            
                    /** @dev Array of Bassets currently active */
                    Basset[] bassets;
            
                    /** @dev Max number of bAssets that can be present in any Basket */
                    uint8 maxBassets;
            
                    /** @dev Some bAsset is undergoing re-collateralisation */
                    bool undergoingRecol;
            
                    /**
                     * @dev In the event that we do not raise enough funds from the auctioning of a failed Basset,
                     * The Basket is deemed as failed, and is undercollateralised to a certain degree.
                     * The collateralisation ratio is used to calc Masset burn rate.
                     */
                    bool failed;
                    uint256 collateralisationRatio;
            
                }
            
                /** @dev Stores bAsset info. The struct takes 5 storage slots per Basset */
                struct Basset {
            
                    /** @dev Address of the bAsset */
                    address addr;
            
                    /** @dev Status of the basset,  */
                    BassetStatus status; // takes uint8 datatype (1 byte) in storage
            
                    /** @dev An ERC20 can charge transfer fee, for example USDT, DGX tokens. */
                    bool isTransferFeeCharged; // takes a byte in storage
            
                    /**
                     * @dev 1 Basset * ratio / ratioScale == x Masset (relative value)
                     *      If ratio == 10e8 then 1 bAsset = 10 mAssets
                     *      A ratio is divised as 10^(18-tokenDecimals) * measurementMultiple(relative value of 1 base unit)
                     */
                    uint256 ratio;
            
                    /** @dev Target weights of the Basset (100% == 1e18) */
                    uint256 maxWeight;
            
                    /** @dev Amount of the Basset that is held in Collateral */
                    uint256 vaultBalance;
            
                }
            
                /** @dev Status of the Basset - has it broken its peg? */
                enum BassetStatus {
                    Default,
                    Normal,
                    BrokenBelowPeg,
                    BrokenAbovePeg,
                    Blacklisted,
                    Liquidating,
                    Liquidated,
                    Failed
                }
            
                /** @dev Internal details on Basset */
                struct BassetDetails {
                    Basset bAsset;
                    address integrator;
                    uint8 index;
                }
            
                /** @dev All details needed to Forge with multiple bAssets */
                struct ForgePropsMulti {
                    bool isValid; // Flag to signify that forge bAssets have passed validity check
                    Basset[] bAssets;
                    address[] integrators;
                    uint8[] indexes;
                }
            
                /** @dev All details needed for proportionate Redemption */
                struct RedeemPropsMulti {
                    uint256 colRatio;
                    Basset[] bAssets;
                    address[] integrators;
                    uint8[] indexes;
                }
            }
            
            contract IBasketManager is MassetStructs {
            
                /** @dev Setters for mAsset to update balances */
                function increaseVaultBalance(
                    uint8 _bAsset,
                    address _integrator,
                    uint256 _increaseAmount) external;
                function increaseVaultBalances(
                    uint8[] calldata _bAsset,
                    address[] calldata _integrator,
                    uint256[] calldata _increaseAmount) external;
                function decreaseVaultBalance(
                    uint8 _bAsset,
                    address _integrator,
                    uint256 _decreaseAmount) external;
                function decreaseVaultBalances(
                    uint8[] calldata _bAsset,
                    address[] calldata _integrator,
                    uint256[] calldata _decreaseAmount) external;
                function collectInterest() external
                    returns (uint256 interestCollected, uint256[] memory gains);
            
                /** @dev Setters for Gov to update Basket composition */
                function addBasset(
                    address _basset,
                    address _integration,
                    bool _isTransferFeeCharged) external returns (uint8 index);
                function setBasketWeights(address[] calldata _bassets, uint256[] calldata _weights) external;
                function setTransferFeesFlag(address _bAsset, bool _flag) external;
            
                /** @dev Getters to retrieve Basket information */
                function getBasket() external view returns (Basket memory b);
                function prepareForgeBasset(address _token, uint256 _amt, bool _mint) external
                    returns (bool isValid, BassetDetails memory bInfo);
                function prepareSwapBassets(address _input, address _output, bool _isMint) external view
                    returns (bool, string memory, BassetDetails memory, BassetDetails memory);
                function prepareForgeBassets(address[] calldata _bAssets, uint256[] calldata _amts, bool _mint) external
                    returns (ForgePropsMulti memory props);
                function prepareRedeemMulti() external view
                    returns (RedeemPropsMulti memory props);
                function getBasset(address _token) external view
                    returns (Basset memory bAsset);
                function getBassets() external view
                    returns (Basset[] memory bAssets, uint256 len);
            
                /** @dev Recollateralisation */
                function handlePegLoss(address _basset, bool _belowPeg) external returns (bool actioned);
                function negateIsolation(address _basset) external;
            }
            
            contract Initializable {
            
              /**
               * @dev Indicates that the contract has been initialized.
               */
              bool private initialized;
            
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private initializing;
            
              /**
               * @dev Modifier to use in the initializer function of a contract.
               */
              modifier initializer() {
                require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
            
                bool isTopLevelCall = !initializing;
                if (isTopLevelCall) {
                  initializing = true;
                  initialized = true;
                }
            
                _;
            
                if (isTopLevelCall) {
                  initializing = false;
                }
              }
            
              /// @dev Returns true if and only if the function is running in the constructor
              function isConstructor() private view returns (bool) {
                // extcodesize checks the size of the code stored in an address, and
                // address returns the current address. Since the code is still not
                // deployed when running a constructor, any checks on its code size will
                // yield zero, making it an effective way to detect if a contract is
                // under construction or not.
                address self = address(this);
                uint256 cs;
                assembly { cs := extcodesize(self) }
                return cs == 0;
              }
            
              // Reserved storage space to allow for layout changes in the future.
              uint256[50] private ______gap;
            }
            
            contract ModuleKeys {
            
                // Governance
                // ===========
                                                            // Phases
                // keccak256("Governance");                 // 2.x
                bytes32 internal constant KEY_GOVERNANCE = 0x9409903de1e6fd852dfc61c9dacb48196c48535b60e25abf92acc92dd689078d;
                //keccak256("Staking");                     // 1.2
                bytes32 internal constant KEY_STAKING = 0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034;
                //keccak256("ProxyAdmin");                  // 1.0
                bytes32 internal constant KEY_PROXY_ADMIN = 0x96ed0203eb7e975a4cbcaa23951943fa35c5d8288117d50c12b3d48b0fab48d1;
            
                // mStable
                // =======
                // keccak256("OracleHub");                  // 1.2
                bytes32 internal constant KEY_ORACLE_HUB = 0x8ae3a082c61a7379e2280f3356a5131507d9829d222d853bfa7c9fe1200dd040;
                // keccak256("Manager");                    // 1.2
                bytes32 internal constant KEY_MANAGER = 0x6d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6f;
                //keccak256("Recollateraliser");            // 2.x
                bytes32 internal constant KEY_RECOLLATERALISER = 0x39e3ed1fc335ce346a8cbe3e64dd525cf22b37f1e2104a755e761c3c1eb4734f;
                //keccak256("MetaToken");                   // 1.1
                bytes32 internal constant KEY_META_TOKEN = 0xea7469b14936af748ee93c53b2fe510b9928edbdccac3963321efca7eb1a57a2;
                // keccak256("SavingsManager");             // 1.0
                bytes32 internal constant KEY_SAVINGS_MANAGER = 0x12fe936c77a1e196473c4314f3bed8eeac1d757b319abb85bdda70df35511bf1;
            }
            
            interface IBasicToken {
                function decimals() external view returns (uint8);
            }
            
            library CommonHelpers {
            
                /**
                 * @notice Fetch the `decimals()` from an ERC20 token
                 * @dev Grabs the `decimals()` from a contract and fails if
                 *      the decimal value does not live within a certain range
                 * @param _token Address of the ERC20 token
                 * @return uint256 Decimals of the ERC20 token
                 */
                function getDecimals(address _token)
                internal
                view
                returns (uint256) {
                    uint256 decimals = IBasicToken(_token).decimals();
                    require(decimals >= 4 && decimals <= 18, "Token must have sufficient decimal places");
            
                    return decimals;
                }
            
            }
            
            
            /**
             * @dev Wrappers over Solidity's arithmetic operations with added overflow
             * checks.
             *
             * Arithmetic operations in Solidity wrap on overflow. This can easily result
             * in bugs, because programmers usually assume that an overflow raises an
             * error, which is the standard behavior in high level programming languages.
             * `SafeMath` restores this intuition by reverting the transaction when an
             * operation overflows.
             *
             * Using this library instead of the unchecked operations eliminates an entire
             * class of bugs, so it's recommended to use it always.
             */
            library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                    uint256 c = a + b;
                    require(c >= a, "SafeMath: addition overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 *
                 * _Available since v2.4.0._
                 */
                function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b <= a, errorMessage);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                    if (a == 0) {
                        return 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 *
                 * _Available since v2.4.0._
                 */
                function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    // Solidity only automatically asserts when dividing by 0
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 *
                 * _Available since v2.4.0._
                 */
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following 
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                    // for accounts without code, i.e. `keccak256('')`
                    bytes32 codehash;
                    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { codehash := extcodehash(account) }
                    return (codehash != accountHash && codehash != 0x0);
                }
            
                /**
                 * @dev Converts an `address` into `address payable`. Note that this is
                 * simply a type cast: the actual underlying value is not changed.
                 *
                 * _Available since v2.4.0._
                 */
                function toPayable(address account) internal pure returns (address payable) {
                    return address(uint160(account));
                }
            
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 *
                 * _Available since v2.4.0._
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
            
                    // solhint-disable-next-line avoid-call-value
                    (bool success, ) = recipient.call.value(amount)("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
            }
            
            library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
            
                function safeTransfer(IERC20 token, address to, uint256 value) internal {
                    callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
            
                function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                    callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
            
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    // solhint-disable-next-line max-line-length
                    require((value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
            
                function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).add(value);
                    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves.
            
                    // A Solidity high level call has three parts:
                    //  1. The target address is checked to verify it contains contract code
                    //  2. The call itself is made, and success asserted
                    //  3. The return value is decoded, which in turn checks the size of the returned data.
                    // solhint-disable-next-line max-line-length
                    require(address(token).isContract(), "SafeERC20: call to non-contract");
            
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = address(token).call(data);
                    require(success, "SafeERC20: low-level call failed");
            
                    if (returndata.length > 0) { // Return data is optional
                        // solhint-disable-next-line max-line-length
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            
            library StableMath {
            
                using SafeMath for uint256;
            
                /**
                 * @dev Scaling unit for use in specific calculations,
                 * where 1 * 10**18, or 1e18 represents a unit '1'
                 */
                uint256 private constant FULL_SCALE = 1e18;
            
                /**
                 * @notice Token Ratios are used when converting between units of bAsset, mAsset and MTA
                 * Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold)
                 * @dev bAsset ratio unit for use in exact calculations,
                 * where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit
                 */
                uint256 private constant RATIO_SCALE = 1e8;
            
                /**
                 * @dev Provides an interface to the scaling unit
                 * @return Scaling unit (1e18 or 1 * 10**18)
                 */
                function getFullScale() internal pure returns (uint256) {
                    return FULL_SCALE;
                }
            
                /**
                 * @dev Provides an interface to the ratio unit
                 * @return Ratio scale unit (1e8 or 1 * 10**8)
                 */
                function getRatioScale() internal pure returns (uint256) {
                    return RATIO_SCALE;
                }
            
                /**
                 * @dev Scales a given integer to the power of the full scale.
                 * @param x   Simple uint256 to scale
                 * @return    Scaled value a to an exact number
                 */
                function scaleInteger(uint256 x)
                    internal
                    pure
                    returns (uint256)
                {
                    return x.mul(FULL_SCALE);
                }
            
                /***************************************
                          PRECISE ARITHMETIC
                ****************************************/
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the full scale
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit
                 */
                function mulTruncate(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return mulTruncateScale(x, y, FULL_SCALE);
                }
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the given scale. For example,
                 * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @param scale Scale unit
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit
                 */
                function mulTruncateScale(uint256 x, uint256 y, uint256 scale)
                    internal
                    pure
                    returns (uint256)
                {
                    uint256 z = x.mul(y);
                    return z.div(scale);
                }
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit, rounded up to the closest base unit.
                 */
                function mulTruncateCeil(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    uint256 scaled = x.mul(y);
                    uint256 ceil = scaled.add(FULL_SCALE.sub(1));
                    return ceil.div(FULL_SCALE);
                }
            
                /**
                 * @dev Precisely divides two units, by first scaling the left hand operand. Useful
                 *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
                 * @param x     Left hand input to division
                 * @param y     Right hand input to division
                 * @return      Result after multiplying the left operand by the scale, and
                 *              executing the division on the right hand input.
                 */
                function divPrecisely(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    uint256 z = x.mul(FULL_SCALE);
                    return z.div(y);
                }
            
            
                /***************************************
                              RATIO FUNCS
                ****************************************/
            
                /**
                 * @dev Multiplies and truncates a token ratio, essentially flooring the result
                 *      i.e. How much mAsset is this bAsset worth?
                 * @param x     Left hand operand to multiplication (i.e Exact quantity)
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the two inputs and then dividing by the ratio scale
                 */
                function mulRatioTruncate(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256 c)
                {
                    return mulTruncateScale(x, ratio, RATIO_SCALE);
                }
            
                /**
                 * @dev Multiplies and truncates a token ratio, rounding up the result
                 *      i.e. How much mAsset is this bAsset worth?
                 * @param x     Left hand input to multiplication (i.e Exact quantity)
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              ratio scale, rounded up to the closest base unit.
                 */
                function mulRatioTruncateCeil(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. How much mAsset should I burn for this bAsset (x)?
                    // 1e18 * 1e8 = 1e26
                    uint256 scaled = x.mul(ratio);
                    // 1e26 + 9.99e7 = 100..00.999e8
                    uint256 ceil = scaled.add(RATIO_SCALE.sub(1));
                    // return 100..00.999e8 / 1e8 = 1e18
                    return ceil.div(RATIO_SCALE);
                }
            
            
                /**
                 * @dev Precisely divides two ratioed units, by first scaling the left hand operand
                 *      i.e. How much bAsset is this mAsset worth?
                 * @param x     Left hand operand in division
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the left operand by the scale, and
                 *              executing the division on the right hand input.
                 */
                function divRatioPrecisely(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256 c)
                {
                    // e.g. 1e14 * 1e8 = 1e22
                    uint256 y = x.mul(RATIO_SCALE);
                    // return 1e22 / 1e12 = 1e10
                    return y.div(ratio);
                }
            
                /***************************************
                                HELPERS
                ****************************************/
            
                /**
                 * @dev Calculates minimum of two numbers
                 * @param x     Left hand input
                 * @param y     Right hand input
                 * @return      Minimum of the two inputs
                 */
                function min(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > y ? y : x;
                }
            
                /**
                 * @dev Calculated maximum of two numbers
                 * @param x     Left hand input
                 * @param y     Right hand input
                 * @return      Maximum of the two inputs
                 */
                function max(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > y ? x : y;
                }
            
                /**
                 * @dev Clamps a value to an upper bound
                 * @param x           Left hand input
                 * @param upperBound  Maximum possible value to return
                 * @return            Input x clamped to a maximum value, upperBound
                 */
                function clamp(uint256 x, uint256 upperBound)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > upperBound ? upperBound : x;
                }
            }
            
            contract InitializableReentrancyGuard {
                bool private _notEntered;
            
                function _initialize() internal {
                    // Storing an initial non-zero value makes deployment a bit more
                    // expensive, but in exchange the refund on every call to nonReentrant
                    // will be lower in amount. Since refunds are capped to a percetange of
                    // the total transaction's gas, it is best to keep them low in cases
                    // like this one, to increase the likelihood of the full refund coming
                    // into effect.
                    _notEntered = true;
                }
            
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and make it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    // On the first call to nonReentrant, _notEntered will be true
                    require(_notEntered, "ReentrancyGuard: reentrant call");
            
                    // Any calls to nonReentrant after this point will fail
                    _notEntered = false;
            
                    _;
            
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _notEntered = true;
                }
            }
            
            /**
             * @title   BasketManager
             * @notice  Manages the Basket composition for a particular mAsset. Feeds all required
             *          basket data to the mAsset and is responsible for keeping accurate records.
             *          BasketManager can also optimise lending pool integrations and perform
             *          re-collateralisation on failed bAssets.
             * @dev     VERSION: 1.0
             *          DATE:    2020-05-05
             */
            contract BasketManager is
                Initializable,
                IBasketManager,
                InitializablePausableModule,
                InitializableReentrancyGuard
            {
                using SafeMath for uint256;
                using StableMath for uint256;
                using SafeERC20 for IERC20;
            
                // Events for Basket composition changes
                event BassetAdded(address indexed bAsset, address integrator);
                event BassetRemoved(address indexed bAsset);
                event BasketWeightsUpdated(address[] bAssets, uint256[] maxWeights);
                event BassetStatusChanged(address indexed bAsset, BassetStatus status);
                event BasketStatusChanged();
                event TransferFeeEnabled(address indexed bAsset, bool enabled);
            
                // mAsset linked to the manager (const)
                address public mAsset;
            
                // Struct holding Basket details
                Basket public basket;
                // Mapping holds bAsset token address => array index
                mapping(address => uint8) private bAssetsMap;
                // Holds relative addresses of the integration platforms
                address[] public integrations;
            
                /**
                 * @dev Initialization function for upgradable proxy contract.
                 *      This function should be called via Proxy just after contract deployment.
                 * @param _nexus            Address of system Nexus
                 * @param _mAsset           Address of the mAsset whose Basket to manage
                 * @param _bAssets          Array of erc20 bAsset addresses
                 * @param _integrators      Matching array of the platform intergations for bAssets
                 * @param _weights          Weightings of each bAsset, summing to 1e18
                 * @param _hasTransferFees  Bool signifying if this bAsset has xfer fees
                 */
                function initialize(
                    address _nexus,
                    address _mAsset,
                    address[] calldata _bAssets,
                    address[] calldata _integrators,
                    uint256[] calldata _weights,
                    bool[] calldata _hasTransferFees
                )
                    external
                    initializer
                {
                    InitializableReentrancyGuard._initialize();
                    InitializablePausableModule._initialize(_nexus);
            
                    require(_mAsset != address(0), "mAsset address is zero");
                    require(_bAssets.length > 0, "Must initialise with some bAssets");
                    mAsset = _mAsset;
            
                    // Defaults
                    basket.maxBassets = 10;               // 10
                    basket.collateralisationRatio = 1e18; // 100%
            
                    for (uint256 i = 0; i < _bAssets.length; i++) {
                        _addBasset(
                            _bAssets[i],
                            _integrators[i],
                            StableMath.getRatioScale(),
                            _hasTransferFees[i]
                        );
                    }
                    _setBasketWeights(_bAssets, _weights, true);
                }
            
                /**
                 * @dev Requires the overall basket composition to be healthy
                 */
                modifier whenBasketIsHealthy() {
                    require(!basket.failed, "Basket must be alive");
                    _;
                }
            
                /**
                 * @dev Requires the overall basket composition to be healthy
                 */
                modifier whenNotRecolling() {
                    require(!basket.undergoingRecol, "No bAssets can be undergoing recol");
                    _;
                }
            
                /**
                 * @dev Verifies that the caller either Manager or Gov
                 */
                modifier managerOrGovernor() {
                    require(
                        _manager() == msg.sender || _governor() == msg.sender,
                        "Must be manager or governor");
                    _;
                }
            
                /**
                 * @dev Verifies that the caller is governed mAsset
                 */
                modifier onlyMasset() {
                    require(mAsset == msg.sender, "Must be called by mAsset");
                    _;
                }
            
                /***************************************
                            VAULT BALANCE
                ****************************************/
            
                /**
                 * @dev Called by only mAsset, and only when the basket is healthy, to add units to
                 *      storage after they have been deposited into the vault
                 * @param _bAssetIndex      Index of the bAsset
                 * @param _increaseAmount   Units deposited
                 */
                function increaseVaultBalance(
                    uint8 _bAssetIndex,
                    address /* _integrator */,
                    uint256 _increaseAmount
                )
                    external
                    onlyMasset
                    whenBasketIsHealthy
                    nonReentrant
                {
                    basket.bassets[_bAssetIndex].vaultBalance =
                        basket.bassets[_bAssetIndex].vaultBalance.add(_increaseAmount);
                }
            
                /**
                 * @dev Called by only mAsset, and only when the basket is healthy, to add units to
                 *      storage after they have been deposited into the vault
                 * @param _bAssetIndices    Array of bAsset indexes
                 * @param _increaseAmount   Units deposited
                 */
                function increaseVaultBalances(
                    uint8[] calldata _bAssetIndices,
                    address[] calldata /* _integrator */,
                    uint256[] calldata _increaseAmount
                )
                    external
                    onlyMasset
                    whenBasketIsHealthy
                    nonReentrant
                {
                    uint256 len = _bAssetIndices.length;
                    for(uint i = 0; i < len; i++) {
                        basket.bassets[_bAssetIndices[i]].vaultBalance =
                            basket.bassets[_bAssetIndices[i]].vaultBalance.add(_increaseAmount[i]);
                    }
                }
            
                /**
                 * @dev Called by mAsset after redeeming tokens. Simply reduce the balance in the vault
                 * @param _bAssetIndex      Index of the bAsset
                 * @param _decreaseAmount   Units withdrawn
                 */
                function decreaseVaultBalance(
                    uint8 _bAssetIndex,
                    address /* _integrator */,
                    uint256 _decreaseAmount
                )
                    external
                    onlyMasset
                    nonReentrant
                {
                    basket.bassets[_bAssetIndex].vaultBalance =
                        basket.bassets[_bAssetIndex].vaultBalance.sub(_decreaseAmount);
                }
            
                /**
                 * @dev Called by mAsset after redeeming tokens. Simply reduce the balance in the vault
                 * @param _bAssetIndices    Array of bAsset indexes
                 * @param _decreaseAmount   Units withdrawn
                 */
                function decreaseVaultBalances(
                    uint8[] calldata _bAssetIndices,
                    address[] calldata /* _integrator */,
                    uint256[] calldata _decreaseAmount
                )
                    external
                    onlyMasset
                    nonReentrant
                {
                    uint256 len = _bAssetIndices.length;
                    for(uint i = 0; i < len; i++) {
                        basket.bassets[_bAssetIndices[i]].vaultBalance =
                            basket.bassets[_bAssetIndices[i]].vaultBalance.sub(_decreaseAmount[i]);
                    }
                }
            
                /**
                 * @dev Called by mAsset to calculate how much interest has been generated in the basket
                 *      and withdraw it. Cycles through the connected platforms to check the balances.
                 * @return interestCollected   Total amount of interest collected, in mAsset terms
                 * @return gains               Array of bAsset units gained
                 */
                function collectInterest()
                    external
                    onlyMasset
                    whenNotPaused
                    whenBasketIsHealthy
                    nonReentrant
                    returns (uint256 interestCollected, uint256[] memory gains)
                {
                    // Get basket details
                    (Basset[] memory allBassets, uint256 count) = _getBassets();
                    gains = new uint256[](count);
                    interestCollected = 0;
            
                    // foreach bAsset
                    for(uint8 i = 0; i < count; i++) {
                        Basset memory b = allBassets[i];
                        // call each integration to `checkBalance`
                        uint256 balance = IPlatformIntegration(integrations[i]).checkBalance(b.addr);
                        uint256 oldVaultBalance = b.vaultBalance;
            
                        // accumulate interest (ratioed bAsset)
                        if(balance > oldVaultBalance && b.status == BassetStatus.Normal) {
                            // Update balance
                            basket.bassets[i].vaultBalance = balance;
            
                            uint256 interestDelta = balance.sub(oldVaultBalance);
                            gains[i] = interestDelta;
            
                            // Calc MassetQ
                            uint256 ratioedDelta = interestDelta.mulRatioTruncate(b.ratio);
                            interestCollected = interestCollected.add(ratioedDelta);
                        } else {
                            gains[i] = 0;
                        }
                    }
                }
            
            
                /***************************************
                            BASKET MANAGEMENT
                ****************************************/
            
                /**
                 * @dev External func to allow the Governor to conduct add operations on the Basket
                 * @param _bAsset               Address of the ERC20 token to add to the Basket
                 * @param _integration          Address of the vault integration to deposit and withdraw
                 * @param _isTransferFeeCharged Bool - are transfer fees charged on this bAsset
                 * @return index                Position of the bAsset in the Basket
                 */
                function addBasset(address _bAsset, address _integration, bool _isTransferFeeCharged)
                    external
                    onlyGovernor
                    whenBasketIsHealthy
                    whenNotRecolling
                    returns (uint8 index)
                {
                    index = _addBasset(
                        _bAsset,
                        _integration,
                        StableMath.getRatioScale(),
                        _isTransferFeeCharged
                    );
                }
            
                /**
                 * @dev Adds a bAsset to the Basket, fetching its decimals and calculating the Ratios
                 * @param _bAsset               Address of the ERC20 token to add to the Basket
                 * @param _integration          Address of the Platform Integration
                 * @param _measurementMultiple  Base 1e8 var to determine measurement ratio
                 *                              between bAsset:mAsset
                 * @param _isTransferFeeCharged Bool - are transfer fees charged on this bAsset
                 * @return index                Position of the bAsset in the Basket
                 */
                function _addBasset(
                    address _bAsset,
                    address _integration,
                    uint256 _measurementMultiple,
                    bool _isTransferFeeCharged
                )
                    internal
                    returns (uint8 index)
                {
                    require(_bAsset != address(0), "bAsset address must be valid");
                    require(_integration != address(0), "Integration address must be valid");
                    require(_measurementMultiple >= 1e6 && _measurementMultiple <= 1e10, "MM out of range");
            
                    (bool alreadyInBasket, ) = _isAssetInBasket(_bAsset);
                    require(!alreadyInBasket, "bAsset already exists in Basket");
            
                    // Should fail if bAsset is not added to integration
                    // Programmatic enforcement of bAsset validity should service through decentralised feed
                    IPlatformIntegration(_integration).checkBalance(_bAsset);
            
                    uint256 bAsset_decimals = CommonHelpers.getDecimals(_bAsset);
                    uint256 delta = uint256(18).sub(bAsset_decimals);
            
                    uint256 ratio = _measurementMultiple.mul(10 ** delta);
            
                    uint8 numberOfBassetsInBasket = uint8(basket.bassets.length);
                    require(numberOfBassetsInBasket < basket.maxBassets, "Max bAssets in Basket");
            
                    bAssetsMap[_bAsset] = numberOfBassetsInBasket;
            
                    integrations.push(_integration);
                    basket.bassets.push(Basset({
                        addr: _bAsset,
                        ratio: ratio,
                        maxWeight: 0,
                        vaultBalance: 0,
                        status: BassetStatus.Normal,
                        isTransferFeeCharged: _isTransferFeeCharged
                    }));
            
                    emit BassetAdded(_bAsset, _integration);
            
                    index = numberOfBassetsInBasket;
                }
            
            
                /**
                 * @dev External call for the governor to set weightings of all bAssets
                 * @param _bAssets Array of bAsset addresses
                 * @param _weights Array of bAsset weights - summing 100% where 100% == 1e18
                 */
                function setBasketWeights(
                    address[] calldata _bAssets,
                    uint256[] calldata _weights
                )
                    external
                    onlyGovernor
                    whenBasketIsHealthy
                {
                    _setBasketWeights(_bAssets, _weights, false);
                }
            
                /**
                 * @notice Sets new Basket weightings
                 * @dev Requires the modified bAssets to be in a Normal state
                 * @param _bAssets Array of bAsset addresses
                 * @param _weights Array of bAsset weights - summing 100% where 100% == 1e18
                 * @param _isBootstrap True only on the first occurence of weight setting
                 */
                function _setBasketWeights(
                    address[] memory _bAssets,
                    uint256[] memory _weights,
                    bool _isBootstrap
                )
                    internal
                {
                    uint256 bAssetCount = _bAssets.length;
                    require(bAssetCount > 0, "Empty bAssets array passed");
                    require(bAssetCount == _weights.length, "Must be matching bAsset arrays");
            
                    for (uint256 i = 0; i < bAssetCount; i++) {
                        (bool exists, uint8 index) = _isAssetInBasket(_bAssets[i]);
                        require(exists, "bAsset must exist");
            
                        Basset memory bAsset = _getBasset(index);
            
                        uint256 bAssetWeight = _weights[i];
            
                        if(bAsset.status == BassetStatus.Normal) {
                            require(
                                bAssetWeight <= 1e18,
                                "Asset weight must be <= 100%"
                            );
                            basket.bassets[index].maxWeight = bAssetWeight;
                        } else {
                            require(
                                bAssetWeight == basket.bassets[index].maxWeight,
                                "Affected bAssets must be static"
                            );
                        }
                    }
            
                    if(!_isBootstrap){
                        _validateBasketWeight();
                    }
            
                    emit BasketWeightsUpdated(_bAssets, _weights);
                }
            
                /**
                 * @dev Throws if the total Basket weight does not sum to 100
                 */
                function _validateBasketWeight() internal view {
                    uint256 len = basket.bassets.length;
                    uint256 weightSum = 0;
                    for(uint256 i = 0; i < len; i++) {
                        weightSum = weightSum.add(basket.bassets[i].maxWeight);
                    }
                    require(weightSum >= 1e18 && weightSum <= 4e18, "Basket weight must be >= 100 && <= 400%");
                }
            
                /**
                 * @dev Update transfer fee flag for a given bAsset, should it change its fee practice
                 * @param _bAsset   bAsset address
                 * @param _flag         Charge transfer fee when its set to 'true', otherwise 'false'
                 */
                function setTransferFeesFlag(address _bAsset, bool _flag)
                    external
                    managerOrGovernor
                {
                    (bool exist, uint8 index) = _isAssetInBasket(_bAsset);
                    require(exist, "bAsset does not exist");
                    basket.bassets[index].isTransferFeeCharged = _flag;
            
                    emit TransferFeeEnabled(_bAsset, _flag);
                }
            
            
                /**
                 * @dev Removes a specific Asset from the Basket, given that its target/collateral
                 *      level is already 0, throws if invalid.
                 * @param _assetToRemove The asset to remove from the basket
                 */
                function removeBasset(address _assetToRemove)
                    external
                    whenBasketIsHealthy
                    whenNotRecolling
                    managerOrGovernor
                {
                    _removeBasset(_assetToRemove);
                }
            
                /**
                 * @dev Removes a specific Asset from the Basket, given that its target/collateral
                 *      level is already 0, throws if invalid.
                 * @param _assetToRemove The asset to remove from the basket
                 */
                function _removeBasset(address _assetToRemove) internal {
                    (bool exists, uint8 index) = _isAssetInBasket(_assetToRemove);
                    require(exists, "bAsset does not exist");
            
                    uint256 len = basket.bassets.length;
                    Basset memory bAsset = basket.bassets[index];
            
                    require(bAsset.maxWeight == 0, "bAsset must have a target weight of 0");
                    require(bAsset.vaultBalance == 0, "bAsset vault must be empty");
                    require(bAsset.status != BassetStatus.Liquidating, "bAsset must be active");
            
                    uint8 lastIndex = uint8(len.sub(1));
                    if(index == lastIndex) {
                        basket.bassets.pop();
                        bAssetsMap[_assetToRemove] = 0;
                        integrations.pop();
                    } else {
                        // Swap the bassets
                        basket.bassets[index] = basket.bassets[lastIndex];
                        basket.bassets.pop();
                        Basset memory swappedBasset = basket.bassets[index];
                        // Update bassetsMap
                        bAssetsMap[_assetToRemove] = 0;
                        bAssetsMap[swappedBasset.addr] = index;
                        // Update integrations
                        integrations[index] = integrations[lastIndex];
                        integrations.pop();
                    }
            
                    emit BassetRemoved(bAsset.addr);
                }
            
            
                /***************************************
                                GETTERS
                ****************************************/
            
                /**
                 * @dev Get basket details for `MassetStructs.Basket`
                 * @return b   Basket struct
                 */
                function getBasket()
                    external
                    view
                    returns (Basket memory b)
                {
                    b = basket;
                }
            
                /**
                 * @dev Prepare given bAsset for Forging. Currently returns integrator
                 *      and essential minting info.
                 * @param _bAsset    Address of the bAsset
                 * @return props     Struct of all relevant Forge information
                 */
                function prepareForgeBasset(address _bAsset, uint256 /*_amt*/, bool /*_mint*/)
                    external
                    whenNotPaused
                    whenNotRecolling
                    returns (bool isValid, BassetDetails memory bInfo)
                {
                    (bool exists, uint8 idx) = _isAssetInBasket(_bAsset);
                    require(exists, "bAsset does not exist");
                    isValid = true;
                    bInfo = BassetDetails({
                        bAsset: basket.bassets[idx],
                        integrator: integrations[idx],
                        index: idx
                    });
                }
            
                /**
                 * @dev Prepare given bAssets for swapping
                 * @param _input     Address of the input bAsset
                 * @param _output    Address of the output bAsset
                 * @param _isMint    Is this swap actually a mint? i.e. output == address(mAsset)
                 * @return props     Struct of all relevant Forge information
                 */
                function prepareSwapBassets(address _input, address _output, bool _isMint)
                    external
                    view
                    whenNotPaused
                    returns (bool, string memory, BassetDetails memory, BassetDetails memory)
                {
                    BassetDetails memory input = BassetDetails({
                        bAsset: basket.bassets[0],
                        integrator: address(0),
                        index: 0
                    });
                    BassetDetails memory output = input;
                    // Check that basket state is healthy
                    if(basket.failed || basket.undergoingRecol){
                        return (false, "Basket is undergoing change", input, output);
                    }
            
                    // Fetch input bAsset
                    (bool inputExists, uint8 inputIdx) = _isAssetInBasket(_input);
                    if(!inputExists) {
                        return (false, "Input asset does not exist", input, output);
                    }
                    input = BassetDetails({
                        bAsset: basket.bassets[inputIdx],
                        integrator: integrations[inputIdx],
                        index: inputIdx
                    });
            
                    // If this is a mint, we don't need output bAsset
                    if(_isMint) {
                        return (true, "", input, output);
                    }
            
                    // Fetch output bAsset
                    (bool outputExists, uint8 outputIdx) = _isAssetInBasket(_output);
                    if(!outputExists) {
                        return (false, "Output asset does not exist", input, output);
                    }
                    output = BassetDetails({
                        bAsset: basket.bassets[outputIdx],
                        integrator: integrations[outputIdx],
                        index: outputIdx
                    });
                    return (true, "", input, output);
                }
            
                /**
                 * @dev Prepare given bAsset addresses for Forging. Currently returns integrator
                 *      and essential minting info for each bAsset
                 * @param _bAssets   Array of bAsset addresses with which to forge
                 * @return props     Struct of all relevant Forge information
                 */
                function prepareForgeBassets(
                    address[] calldata _bAssets,
                    uint256[] calldata /*_amts*/,
                    bool /* _isMint */
                )
                    external
                    whenNotPaused
                    whenNotRecolling
                    returns (ForgePropsMulti memory props)
                {
                    // Pass the fetching logic to the internal view func to reduce SLOAD cost
                    (Basset[] memory bAssets, uint8[] memory indexes, address[] memory integrators) = _fetchForgeBassets(_bAssets);
                    props = ForgePropsMulti({
                        isValid: true,
                        bAssets: bAssets,
                        integrators: integrators,
                        indexes: indexes
                    });
                }
            
                /**
                 * @dev Prepare given bAsset addresses for RedeemMulti. Currently returns integrator
                 *      and essential minting info for each bAsset
                 * @return props     Struct of all relevant Forge information
                 */
                function prepareRedeemMulti()
                    external
                    view
                    whenNotPaused
                    whenNotRecolling
                    returns (RedeemPropsMulti memory props)
                {
                    (Basset[] memory bAssets, uint256 len) = _getBassets();
                    address[] memory orderedIntegrators = new address[](len);
                    uint8[] memory indexes = new uint8[](len);
                    for(uint8 i = 0; i < len; i++){
                        orderedIntegrators[i] = integrations[i];
                        indexes[i] = i;
                    }
                    props = RedeemPropsMulti({
                        colRatio: basket.collateralisationRatio,
                        bAssets: bAssets,
                        integrators: orderedIntegrators,
                        indexes: indexes
                    });
                }
            
                /**
                 * @dev Internal func to fetch an array of bAssets and their integrators from storage
                 * @param _bAssets       Array of non-duplicate bAsset addresses with which to forge
                 * @return bAssets       Array of bAsset structs for the given addresses
                 * @return indexes       Array of indexes for the given addresses
                 * @return integrators   Array of integrators for the given addresses
                 */
                function _fetchForgeBassets(address[] memory _bAssets)
                    internal
                    view
                    returns (
                        Basset[] memory bAssets,
                        uint8[] memory indexes,
                        address[] memory integrators
                    )
                {
                    uint8 len = uint8(_bAssets.length);
            
                    bAssets = new Basset[](len);
                    integrators = new address[](len);
                    indexes = new uint8[](len);
            
                    // Iterate through the input
                    for(uint8 i = 0; i < len; i++) {
                        address current = _bAssets[i];
            
                        // If there is a duplicate here, throw
                        // Gas costs do not incur SLOAD
                        for(uint8 j = i+1; j < len; j++){
                            require(current != _bAssets[j], "Must have no duplicates");
                        }
            
                        // Fetch and log all the relevant data
                        (bool exists, uint8 index) = _isAssetInBasket(current);
                        require(exists, "bAsset must exist");
                        indexes[i] = index;
                        bAssets[i] = basket.bassets[index];
                        integrators[i] = integrations[index];
                    }
                }
            
                /**
                 * @dev Get data for a all bAssets in basket
                 * @return bAssets  Struct[] with full bAsset data
                 * @return len      Number of bAssets in the Basket
                 */
                function getBassets()
                    external
                    view
                    returns (
                        Basset[] memory bAssets,
                        uint256 len
                    )
                {
                    return _getBassets();
                }
            
                /**
                 * @dev Get data for a specific bAsset, if it exists
                 * @param _bAsset   Address of bAsset
                 * @return bAsset  Struct with full bAsset data
                 */
                function getBasset(address _bAsset)
                    external
                    view
                    returns (Basset memory bAsset)
                {
                    (bool exists, uint8 index) = _isAssetInBasket(_bAsset);
                    require(exists, "bAsset must exist");
                    bAsset = _getBasset(index);
                }
            
                /**
                 * @dev Get current integrator for a specific bAsset, if it exists
                 * @param _bAsset      Address of bAsset
                 * @return integrator  Address of current integrator
                 */
                function getBassetIntegrator(address _bAsset)
                    external
                    view
                    returns (address integrator)
                {
                    (bool exists, uint8 index) = _isAssetInBasket(_bAsset);
                    require(exists, "bAsset must exist");
                    integrator = integrations[index];
                }
            
                function _getBassets()
                    internal
                    view
                    returns (
                        Basset[] memory bAssets,
                        uint256 len
                    )
                {
                    bAssets = basket.bassets;
                    len = basket.bassets.length;
                }
            
                function _getBasset(uint8 _bAssetIndex)
                    internal
                    view
                    returns (Basset memory bAsset)
                {
                    bAsset = basket.bassets[_bAssetIndex];
                }
            
            
                /***************************************
                                HELPERS
                ****************************************/
            
                /**
                 * @dev Checks if a particular asset is in the basket
                 * @param _asset   Address of bAsset to look for
                 * @return exists  bool to signal that the asset is in basket
                 * @return index   uint256 Index of the bAsset
                 */
                function _isAssetInBasket(address _asset)
                    internal
                    view
                    returns (bool exists, uint8 index)
                {
                    index = bAssetsMap[_asset];
                    if(index == 0) {
                        if(basket.bassets.length == 0) {
                            return (false, 0);
                        }
                        return (basket.bassets[0].addr == _asset, 0);
                    }
                    return (true, index);
                }
            
                /**
                 * @notice Determine whether or not a bAsset has already undergone re-collateralisation
                 * @param _status   Status of the bAsset
                 * @return          Bool to determine if undergone re-collateralisation
                 */
                function _bAssetHasRecolled(BassetStatus _status)
                    internal
                    pure
                    returns (bool)
                {
                    if(_status == BassetStatus.Liquidating ||
                        _status == BassetStatus.Liquidated ||
                        _status == BassetStatus.Failed) {
                        return true;
                    }
                    return false;
                }
            
            
                /***************************************
                            RE-COLLATERALISATION
                ****************************************/
            
                /**
                 * @dev Executes the Auto Redistribution event by isolating the bAsset from the Basket
                 * @param _bAsset          Address of the ERC20 token to isolate
                 * @param _belowPeg        Bool to describe whether the bAsset deviated below peg (t)
                 *                         or above (f)
                 * @return alreadyActioned Bool to show whether a bAsset had already been actioned
                 */
                function handlePegLoss(address _bAsset, bool _belowPeg)
                    external
                    managerOrGovernor
                    whenBasketIsHealthy
                    returns (bool alreadyActioned)
                {
                    (bool exists, uint256 i) = _isAssetInBasket(_bAsset);
                    require(exists, "bAsset must exist in Basket");
            
                    BassetStatus oldStatus = basket.bassets[i].status;
                    BassetStatus newStatus =
                        _belowPeg ? BassetStatus.BrokenBelowPeg : BassetStatus.BrokenAbovePeg;
            
                    if(oldStatus == newStatus || _bAssetHasRecolled(oldStatus)) {
                        return true;
                    }
            
                    // If we need to update the status.. then do it
                    basket.bassets[i].status = newStatus;
                    emit BassetStatusChanged(_bAsset, newStatus);
                    return false;
                }
            
                /**
                 * @dev Negates the isolation of a given bAsset
                 * @param _bAsset Address of the bAsset
                 */
                function negateIsolation(address _bAsset)
                    external
                    managerOrGovernor
                {
                    (bool exists, uint256 i) = _isAssetInBasket(_bAsset);
                    require(exists, "bAsset must exist");
            
                    BassetStatus currentStatus = basket.bassets[i].status;
                    if(currentStatus == BassetStatus.BrokenBelowPeg ||
                        currentStatus == BassetStatus.BrokenAbovePeg ||
                        currentStatus == BassetStatus.Blacklisted) {
                        basket.bassets[i].status = BassetStatus.Normal;
                        emit BassetStatusChanged(_bAsset, BassetStatus.Normal);
                    }
                }
            
                /**
                 * @dev Marks a bAsset as permanently deviated from peg
                 * @param _bAsset Address of the bAsset
                 */
                function failBasset(address _bAsset)
                    external
                    onlyGovernor
                {
                    (bool exists, uint256 i) = _isAssetInBasket(_bAsset);
                    require(exists, "bAsset must exist");
            
                    BassetStatus currentStatus = basket.bassets[i].status;
                    require(
                        currentStatus == BassetStatus.BrokenBelowPeg ||
                        currentStatus == BassetStatus.BrokenAbovePeg,
                        "bAsset must be affected"
                    );
                    basket.failed = true;
                }
            }

            File 6 of 7: ForgeValidator
            pragma solidity 0.5.16;
            pragma experimental ABIEncoderV2;
            
            
            interface MassetStructs {
            
                /** @dev Stores high level basket info */
                struct Basket {
            
                    /** @dev Array of Bassets currently active */
                    Basset[] bassets;
            
                    /** @dev Max number of bAssets that can be present in any Basket */
                    uint8 maxBassets;
            
                    /** @dev Some bAsset is undergoing re-collateralisation */
                    bool undergoingRecol;
            
                    /**
                     * @dev In the event that we do not raise enough funds from the auctioning of a failed Basset,
                     * The Basket is deemed as failed, and is undercollateralised to a certain degree.
                     * The collateralisation ratio is used to calc Masset burn rate.
                     */
                    bool failed;
                    uint256 collateralisationRatio;
            
                }
            
                /** @dev Stores bAsset info. The struct takes 5 storage slots per Basset */
                struct Basset {
            
                    /** @dev Address of the bAsset */
                    address addr;
            
                    /** @dev Status of the basset,  */
                    BassetStatus status; // takes uint8 datatype (1 byte) in storage
            
                    /** @dev An ERC20 can charge transfer fee, for example USDT, DGX tokens. */
                    bool isTransferFeeCharged; // takes a byte in storage
            
                    /**
                     * @dev 1 Basset * ratio / ratioScale == x Masset (relative value)
                     *      If ratio == 10e8 then 1 bAsset = 10 mAssets
                     *      A ratio is divised as 10^(18-tokenDecimals) * measurementMultiple(relative value of 1 base unit)
                     */
                    uint256 ratio;
            
                    /** @dev Target weights of the Basset (100% == 1e18) */
                    uint256 maxWeight;
            
                    /** @dev Amount of the Basset that is held in Collateral */
                    uint256 vaultBalance;
            
                }
            
                /** @dev Status of the Basset - has it broken its peg? */
                enum BassetStatus {
                    Default,
                    Normal,
                    BrokenBelowPeg,
                    BrokenAbovePeg,
                    Blacklisted,
                    Liquidating,
                    Liquidated,
                    Failed
                }
            
                /** @dev Internal details on Basset */
                struct BassetDetails {
                    Basset bAsset;
                    address integrator;
                    uint8 index;
                }
            
                /** @dev All details needed to Forge with multiple bAssets */
                struct ForgePropsMulti {
                    bool isValid; // Flag to signify that forge bAssets have passed validity check
                    Basset[] bAssets;
                    address[] integrators;
                    uint8[] indexes;
                }
            
                /** @dev All details needed for proportionate Redemption */
                struct RedeemPropsMulti {
                    uint256 colRatio;
                    Basset[] bAssets;
                    address[] integrators;
                    uint8[] indexes;
                }
            }
            
            contract IForgeValidator is MassetStructs {
                function validateMint(uint256 _totalVault, Basset calldata _basset, uint256 _bAssetQuantity)
                    external pure returns (bool, string memory);
                function validateMintMulti(uint256 _totalVault, Basset[] calldata _bassets, uint256[] calldata _bAssetQuantities)
                    external pure returns (bool, string memory);
                function validateSwap(uint256 _totalVault, Basset calldata _inputBasset, Basset calldata _outputBasset, uint256 _quantity)
                    external pure returns (bool, string memory, uint256, bool);
                function validateRedemption(
                    bool basketIsFailed,
                    uint256 _totalVault,
                    Basset[] calldata _allBassets,
                    uint8[] calldata _indices,
                    uint256[] calldata _bassetQuantities) external pure returns (bool, string memory, bool);
                function calculateRedemptionMulti(
                    uint256 _mAssetQuantity,
                    Basset[] calldata _allBassets) external pure returns (bool, string memory, uint256[] memory);
            }
            
            library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                    uint256 c = a + b;
                    require(c >= a, "SafeMath: addition overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 *
                 * _Available since v2.4.0._
                 */
                function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b <= a, errorMessage);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                    if (a == 0) {
                        return 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 *
                 * _Available since v2.4.0._
                 */
                function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    // Solidity only automatically asserts when dividing by 0
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 *
                 * _Available since v2.4.0._
                 */
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            library StableMath {
            
                using SafeMath for uint256;
            
                /**
                 * @dev Scaling unit for use in specific calculations,
                 * where 1 * 10**18, or 1e18 represents a unit '1'
                 */
                uint256 private constant FULL_SCALE = 1e18;
            
                /**
                 * @notice Token Ratios are used when converting between units of bAsset, mAsset and MTA
                 * Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold)
                 * @dev bAsset ratio unit for use in exact calculations,
                 * where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit
                 */
                uint256 private constant RATIO_SCALE = 1e8;
            
                /**
                 * @dev Provides an interface to the scaling unit
                 * @return Scaling unit (1e18 or 1 * 10**18)
                 */
                function getFullScale() internal pure returns (uint256) {
                    return FULL_SCALE;
                }
            
                /**
                 * @dev Provides an interface to the ratio unit
                 * @return Ratio scale unit (1e8 or 1 * 10**8)
                 */
                function getRatioScale() internal pure returns (uint256) {
                    return RATIO_SCALE;
                }
            
                /**
                 * @dev Scales a given integer to the power of the full scale.
                 * @param x   Simple uint256 to scale
                 * @return    Scaled value a to an exact number
                 */
                function scaleInteger(uint256 x)
                    internal
                    pure
                    returns (uint256)
                {
                    return x.mul(FULL_SCALE);
                }
            
                /***************************************
                          PRECISE ARITHMETIC
                ****************************************/
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the full scale
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit
                 */
                function mulTruncate(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return mulTruncateScale(x, y, FULL_SCALE);
                }
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the given scale. For example,
                 * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @param scale Scale unit
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit
                 */
                function mulTruncateScale(uint256 x, uint256 y, uint256 scale)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. assume scale = fullScale
                    // z = 10e18 * 9e17 = 9e36
                    uint256 z = x.mul(y);
                    // return 9e38 / 1e18 = 9e18
                    return z.div(scale);
                }
            
                /**
                 * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
                 * @param x     Left hand input to multiplication
                 * @param y     Right hand input to multiplication
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              scale unit, rounded up to the closest base unit.
                 */
                function mulTruncateCeil(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. 8e17 * 17268172638 = 138145381104e17
                    uint256 scaled = x.mul(y);
                    // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17
                    uint256 ceil = scaled.add(FULL_SCALE.sub(1));
                    // e.g. 13814538111.399...e18 / 1e18 = 13814538111
                    return ceil.div(FULL_SCALE);
                }
            
                /**
                 * @dev Precisely divides two units, by first scaling the left hand operand. Useful
                 *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
                 * @param x     Left hand input to division
                 * @param y     Right hand input to division
                 * @return      Result after multiplying the left operand by the scale, and
                 *              executing the division on the right hand input.
                 */
                function divPrecisely(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. 8e18 * 1e18 = 8e36
                    uint256 z = x.mul(FULL_SCALE);
                    // e.g. 8e36 / 10e18 = 8e17
                    return z.div(y);
                }
            
            
                /***************************************
                              RATIO FUNCS
                ****************************************/
            
                /**
                 * @dev Multiplies and truncates a token ratio, essentially flooring the result
                 *      i.e. How much mAsset is this bAsset worth?
                 * @param x     Left hand operand to multiplication (i.e Exact quantity)
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the two inputs and then dividing by the ratio scale
                 */
                function mulRatioTruncate(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256 c)
                {
                    return mulTruncateScale(x, ratio, RATIO_SCALE);
                }
            
                /**
                 * @dev Multiplies and truncates a token ratio, rounding up the result
                 *      i.e. How much mAsset is this bAsset worth?
                 * @param x     Left hand input to multiplication (i.e Exact quantity)
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the two inputs and then dividing by the shared
                 *              ratio scale, rounded up to the closest base unit.
                 */
                function mulRatioTruncateCeil(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256)
                {
                    // e.g. How much mAsset should I burn for this bAsset (x)?
                    // 1e18 * 1e8 = 1e26
                    uint256 scaled = x.mul(ratio);
                    // 1e26 + 9.99e7 = 100..00.999e8
                    uint256 ceil = scaled.add(RATIO_SCALE.sub(1));
                    // return 100..00.999e8 / 1e8 = 1e18
                    return ceil.div(RATIO_SCALE);
                }
            
            
                /**
                 * @dev Precisely divides two ratioed units, by first scaling the left hand operand
                 *      i.e. How much bAsset is this mAsset worth?
                 * @param x     Left hand operand in division
                 * @param ratio bAsset ratio
                 * @return      Result after multiplying the left operand by the scale, and
                 *              executing the division on the right hand input.
                 */
                function divRatioPrecisely(uint256 x, uint256 ratio)
                    internal
                    pure
                    returns (uint256 c)
                {
                    // e.g. 1e14 * 1e8 = 1e22
                    uint256 y = x.mul(RATIO_SCALE);
                    // return 1e22 / 1e12 = 1e10
                    return y.div(ratio);
                }
            
                /***************************************
                                HELPERS
                ****************************************/
            
                /**
                 * @dev Calculates minimum of two numbers
                 * @param x     Left hand input
                 * @param y     Right hand input
                 * @return      Minimum of the two inputs
                 */
                function min(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > y ? y : x;
                }
            
                /**
                 * @dev Calculated maximum of two numbers
                 * @param x     Left hand input
                 * @param y     Right hand input
                 * @return      Maximum of the two inputs
                 */
                function max(uint256 x, uint256 y)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > y ? x : y;
                }
            
                /**
                 * @dev Clamps a value to an upper bound
                 * @param x           Left hand input
                 * @param upperBound  Maximum possible value to return
                 * @return            Input x clamped to a maximum value, upperBound
                 */
                function clamp(uint256 x, uint256 upperBound)
                    internal
                    pure
                    returns (uint256)
                {
                    return x > upperBound ? upperBound : x;
                }
            }
            
            /**
             * @title   ForgeValidator
             * @author  Stability Labs Pty. Ltd.
             * @notice  Calculates whether or not minting or redemption is valid, based
             *          on how it affects the underlying basket collateral weightings
             * @dev     VERSION: 1.1
             *          DATE:    2020-06-22
             */
            contract ForgeValidator is IForgeValidator {
            
                using SafeMath for uint256;
                using StableMath for uint256;
            
                /***************************************
                                MINT
                ****************************************/
            
                /**
                 * @notice Checks whether a given mint is valid and returns the result
                 * @dev Is the resulting weighting of the max bAsset beyond it's implicit max weight?
                 * Max weight is determined as max weight (in units)
                 * @param _totalVault       Current sum of basket collateral
                 * @param _bAsset           Struct containing relevant data on the bAsset
                 * @param _bAssetQuantity   Number of bAsset units that will be used to mint
                 * @return isValid          Bool to signify that the mint does not move our weightings the wrong way
                 * @return reason           If the mint is invalid, this is the reason
                 */
                function validateMint(
                    uint256 _totalVault,
                    Basset calldata _bAsset,
                    uint256 _bAssetQuantity
                )
                    external
                    pure
                    returns (bool isValid, string memory reason)
                {
                    if(
                        _bAsset.status == BassetStatus.BrokenBelowPeg ||
                        _bAsset.status == BassetStatus.Liquidating ||
                        _bAsset.status == BassetStatus.Blacklisted
                    ) {
                        return (false, "bAsset not allowed in mint");
                    }
            
                    // How much mAsset is this _bAssetQuantity worth?
                    uint256 mintAmountInMasset = _bAssetQuantity.mulRatioTruncate(_bAsset.ratio);
                    // How much of this bAsset do we have in the vault, in terms of mAsset?
                    uint256 newBalanceInMasset = _bAsset.vaultBalance.mulRatioTruncate(_bAsset.ratio).add(mintAmountInMasset);
                    // What is the max weight of this bAsset in the basket?
                    uint256 maxWeightInUnits = (_totalVault.add(mintAmountInMasset)).mulTruncate(_bAsset.maxWeight);
            
                    if(newBalanceInMasset > maxWeightInUnits) {
                        return (false, "bAssets used in mint cannot exceed their max weight");
                    }
            
                    return (true, "");
                }
            
                /**
                 * @notice Checks whether a given mint using more than one asset is valid and returns the result
                 * @dev Is the resulting weighting of the max bAssets beyond their implicit max weight?
                 * Max weight is determined as max weight (in units)
                 * @param _totalVault       Current sum of basket collateral
                 * @param _bAssets          Array of Struct containing relevant data on the bAssets
                 * @param _bAssetQuantities Number of bAsset units that will be used to mint (aligned with above)
                 * @return isValid          Bool to signify that the mint does not move our weightings the wrong way
                 * @return reason           If the mint is invalid, this is the reason
                 */
                function validateMintMulti(
                    uint256 _totalVault,
                    Basset[] calldata _bAssets,
                    uint256[] calldata _bAssetQuantities
                )
                    external
                    pure
                    returns (bool isValid, string memory reason)
                {
                    uint256 bAssetCount = _bAssets.length;
                    if(bAssetCount != _bAssetQuantities.length) return (false, "Input length should be equal");
            
                    uint256[] memory newBalances = new uint256[](bAssetCount);
                    uint256 newTotalVault = _totalVault;
            
                    // Theoretically add the mint quantities to the vault
                    for(uint256 j = 0; j < bAssetCount; j++){
                        Basset memory b = _bAssets[j];
                        BassetStatus bAssetStatus = b.status;
            
                        if(
                            bAssetStatus == BassetStatus.BrokenBelowPeg ||
                            bAssetStatus == BassetStatus.Liquidating ||
                            bAssetStatus == BassetStatus.Blacklisted
                        ) {
                            return (false, "bAsset not allowed in mint");
                        }
            
                        // How much mAsset is this bassetquantity worth?
                        uint256 mintAmountInMasset = _bAssetQuantities[j].mulRatioTruncate(b.ratio);
                        // How much of this bAsset do we have in the vault, in terms of mAsset?
                        newBalances[j] = b.vaultBalance.mulRatioTruncate(b.ratio).add(mintAmountInMasset);
            
                        newTotalVault = newTotalVault.add(mintAmountInMasset);
                    }
            
                    for(uint256 k = 0; k < bAssetCount; k++){
                        // What is the max weight of this bAsset in the basket?
                        uint256 maxWeightInUnits = newTotalVault.mulTruncate(_bAssets[k].maxWeight);
            
                        if(newBalances[k] > maxWeightInUnits) {
                            return (false, "bAssets used in mint cannot exceed their max weight");
                        }
                    }
            
                    return (true, "");
                }
            
                /***************************************
                                SWAP
                ****************************************/
            
                /**
                 * @notice Checks whether a given swap is valid and calculates the output
                 * @dev Input bAsset must not exceed max weight, output bAsset must have sufficient liquidity
                 * @param _totalVault       Current sum of basket collateral
                 * @param _inputBasset      Input bAsset details
                 * @param _outputBasset     Output bAsset details
                 * @param _quantity         Number of bAsset units on the input side
                 * @return isValid          Bool to signify that the mint does not move our weightings the wrong way
                 * @return reason           If the swap is invalid, this is the reason
                 * @return output           Units of output bAsset, before fee is applied
                 * @return applySwapFee     Bool to signify that the swap fee is applied
                 */
                function validateSwap(
                    uint256 _totalVault,
                    Basset calldata _inputBasset,
                    Basset calldata _outputBasset,
                    uint256 _quantity
                )
                    external
                    pure
                    returns (bool isValid, string memory reason, uint256 output, bool applySwapFee)
                {
                    if(_inputBasset.status != BassetStatus.Normal || _outputBasset.status != BassetStatus.Normal) {
                        return (false, "bAsset not allowed in swap", 0, false);
                    }
            
                    // How much mAsset is this _bAssetQuantity worth?
                    uint256 inputAmountInMasset = _quantity.mulRatioTruncate(_inputBasset.ratio);
            
                    // 1. Determine output bAsset valid
                    //  - Enough units in the bank
                    uint256 outputAmount = inputAmountInMasset.divRatioPrecisely(_outputBasset.ratio);
                    if(outputAmount > _outputBasset.vaultBalance) {
                        return (false, "Not enough liquidity", 0, false);
                    }
            
                    // 1.1. If it is currently overweight, then no fee
                    applySwapFee = true;
                    uint256 outputBalanceMasset = _outputBasset.vaultBalance.mulRatioTruncate(_outputBasset.ratio);
                    uint256 outputMaxWeightUnits = _totalVault.mulTruncate(_outputBasset.maxWeight);
                    if(outputBalanceMasset > outputMaxWeightUnits) {
                        applySwapFee = false;
                    }
            
                    // 2. Calculate input bAsset valid - If incoming basket goes above weight, then fail
                    // How much of this bAsset do we have in the vault, in terms of mAsset?
                    uint256 newInputBalanceInMasset = _inputBasset.vaultBalance.mulRatioTruncate(_inputBasset.ratio).add(inputAmountInMasset);
                    // What is the max weight of this bAsset in the basket?
                    uint256 inputMaxWeightInUnits = _totalVault.mulTruncate(_inputBasset.maxWeight);
                    if(newInputBalanceInMasset > inputMaxWeightInUnits) {
                        return (false, "Input must remain below max weighting", 0, false);
                    }
            
                    // 3. Return swap output
                    return (true, "", outputAmount, applySwapFee);
                }
            
            
                /***************************************
                                REDEEM
                ****************************************/
            
                /**
                 * @notice Checks whether a given redemption is valid and returns the result
                 * @dev A redemption is valid if it does not push any untouched bAssets above their
                 * max weightings. In addition, if bAssets are currently above their max weight
                 * (i.e. during basket composition changes) they must be redeemed
                 * @param _basketIsFailed   Bool to suggest that the basket has failed a recollateralisation attempt
                 * @param _totalVault       Sum of collateral units in the basket
                 * @param _allBassets       Array of all bAsset information
                 * @param _indices          Indexes of the bAssets to redeem
                 * @param _bAssetQuantities Quantity of bAsset to redeem
                 * @return isValid          Bool to signify that the redemption is allowed
                 * @return reason           If the redemption is invalid, this is the reason
                 * @return feeRequired      Does this redemption require the swap fee to be applied
                 */
                function validateRedemption(
                    bool _basketIsFailed,
                    uint256 _totalVault,
                    Basset[] calldata _allBassets,
                    uint8[] calldata _indices,
                    uint256[] calldata _bAssetQuantities
                )
                    external
                    pure
                    returns (bool, string memory, bool)
                {
                    uint256 idxCount = _indices.length;
                    if(idxCount != _bAssetQuantities.length) return (false, "Input arrays must have equal length", false);
            
                    // Get current weightings, and cache some outputs from the loop to avoid unecessary recursion
                    BasketStateResponse memory data = _getBasketState(_totalVault, _allBassets);
                    if(!data.isValid) return (false, data.reason, false);
            
                    // If the basket is in an affected state, enforce proportional redemption
                    if(
                        _basketIsFailed ||
                        data.atLeastOneBroken
                    ) {
                        return (false, "Must redeem proportionately", false);
                    } else if (data.overWeightCount > idxCount) {
                        return (false, "Redemption must contain all overweight bAssets", false);
                    }
            
                    uint256 newTotalVault = _totalVault;
            
                    // Simulate the redemption on the ratioedBassetVaults and totalSupply
                    for(uint256 i = 0; i < idxCount; i++){
                        uint8 idx = _indices[i];
                        if(idx >= _allBassets.length) return (false, "Basset does not exist", false);
            
                        Basset memory bAsset = _allBassets[idx];
                        uint256 quantity = _bAssetQuantities[i];
                        if(quantity > bAsset.vaultBalance) return (false, "Cannot redeem more bAssets than are in the vault", false);
            
                        // Calculate ratioed redemption amount in mAsset terms
                        uint256 ratioedRedemptionAmount = quantity.mulRatioTruncate(bAsset.ratio);
                        // Subtract ratioed redemption amount from both vault and total supply
                        data.ratioedBassetVaults[idx] = data.ratioedBassetVaults[idx].sub(ratioedRedemptionAmount);
            
                        newTotalVault = newTotalVault.sub(ratioedRedemptionAmount);
                    }
            
                    // Get overweight after
                    bool atLeastOneBecameOverweight =
                        _getOverweightBassetsAfter(newTotalVault, _allBassets, data.ratioedBassetVaults, data.isOverWeight);
            
                    bool applySwapFee = true;
                    // If there are any bAssets overweight, we must redeem them all
                    if(data.overWeightCount > 0) {
                        for(uint256 j = 0; j < idxCount; j++) {
                            if(!data.isOverWeight[_indices[j]]) return (false, "Must redeem overweight bAssets", false);
                        }
                        applySwapFee = false;
                    }
                    // Since no bAssets were overweight before, if one becomes overweight, throw
                    if(atLeastOneBecameOverweight) return (false, "bAssets must remain below max weight", false);
            
                    return (true, "", applySwapFee);
                }
            
                /**
                 * @notice Calculates the relative quantities of bAssets to redeem, with current basket state
                 * @dev Sum the value of the bAssets, and then find the proportions relative to the desired
                 * mAsset quantity.
                 * @param _mAssetQuantity   Quantity of mAsset to redeem
                 * @param _allBassets       Array of all bAsset information
                 * @return isValid          Bool to signify that the redemption is allowed
                 * @return reason           If the redemption is invalid, this is the reason
                 * @return quantities       Array of bAsset quantities to redeem
                 */
                function calculateRedemptionMulti(
                    uint256 _mAssetQuantity,
                    Basset[] calldata _allBassets
                )
                    external
                    pure
                    returns (bool, string memory, uint256[] memory)
                {
                    // e.g. mAsset = 1e20 (100)
                    // e.g. bAsset: [   A,   B,    C,    D]
                    // e.g. vaults: [  80,  60,   60,    0]
                    // e.g. ratio:  [1e12, 1e8, 1e20, 1e18]
                    // expectedM:    4e19 3e19  3e19     0
                    // expectedB:    4e15 3e19   3e7     0
                    uint256 len = _allBassets.length;
                    uint256[] memory redeemQuantities = new uint256[](len);
                    uint256[] memory ratioedBassetVaults = new uint256[](len);
                    uint256 totalBassetVault = 0;
                    // 1. Add up total vault & ratioedBassets, fail if blacklisted
                    for(uint256 i = 0; i < len; i++) {
                        if(_allBassets[i].status == BassetStatus.Blacklisted) {
                            return (false, "Basket contains blacklisted bAsset", redeemQuantities);
                        } else if(_allBassets[i].status == BassetStatus.Liquidating) {
                            return (false, "Basket contains liquidating bAsset", redeemQuantities);
                        }
                        // e.g. (80e14 * 1e12) / 1e8 = 80e18
                        // e.g. (60e18 * 1e8) / 1e8 = 60e18
                        uint256 ratioedBasset = _allBassets[i].vaultBalance.mulRatioTruncate(_allBassets[i].ratio);
                        ratioedBassetVaults[i] = ratioedBasset;
                        totalBassetVault = totalBassetVault.add(ratioedBasset);
                    }
                    if(totalBassetVault == 0) return (false, "Nothing in the basket to redeem", redeemQuantities);
                    if(_mAssetQuantity > totalBassetVault) return (false, "Not enough liquidity", redeemQuantities);
                    // 2. Calculate proportional weighting & non-ratioed amount
                    for(uint256 i = 0; i < len; i++) {
                        // proportional weighting
                        // e.g. (8e19 * 1e18) / 2e20 = 8e37 / 2e20 = 4e17 (40%)
                        uint256 percentageOfVault = ratioedBassetVaults[i].divPrecisely(totalBassetVault);
                        // e.g. (1e20 * 4e17) / 1e18 = 4e37 / 1e18 = 4e19 (40)
                        uint256 ratioedProportionalBasset = _mAssetQuantity.mulTruncate(percentageOfVault);
                        // convert back to bAsset amount
                         // e.g. (4e19 * 1e8) / 1e12 = 4e27 / 1e12 = 4e15
                        redeemQuantities[i] = ratioedProportionalBasset.divRatioPrecisely(_allBassets[i].ratio);
                    }
                    // 3. Return
                    return (true, "", redeemQuantities);
                }
            
                /***************************************
                                HELPERS
                ****************************************/
            
                struct BasketStateResponse {
                    bool isValid;
                    string reason;
                    bool atLeastOneBroken;
                    uint256 overWeightCount;
                    bool[] isOverWeight;
                    uint256[] ratioedBassetVaults;
                }
            
                /**
                 * @dev Gets the currently overweight bAssets, and capitalises on the for loop to
                 * produce some other useful data. Loops through, validating the bAsset, and determining
                 * if it is overweight, returning the ratioed bAsset.
                 * @param _total         Sum of collateral units in the basket
                 * @param _bAssets       Array of all bAsset information
                 * @return response      Struct containing calculated data
                 */
                function _getBasketState(uint256 _total, Basset[] memory _bAssets)
                    private
                    pure
                    returns (BasketStateResponse memory response)
                {
                    uint256 len = _bAssets.length;
                    response = BasketStateResponse({
                        isValid: true,
                        reason: "",
                        atLeastOneBroken: false,
                        overWeightCount: 0,
                        isOverWeight: new bool[](len),
                        ratioedBassetVaults: new uint256[](len)
                    });
            
                    for(uint256 i = 0; i < len; i++) {
                        BassetStatus status = _bAssets[i].status;
                        if(status == BassetStatus.Blacklisted) {
                            response.isValid = false;
                            response.reason = "Basket contains blacklisted bAsset";
                            return response;
                        } else if(
                            status == BassetStatus.Liquidating ||
                            status == BassetStatus.BrokenBelowPeg ||
                            status == BassetStatus.BrokenAbovePeg
                        ) {
                            response.atLeastOneBroken = true;
                        }
            
                        uint256 ratioedBasset = _bAssets[i].vaultBalance.mulRatioTruncate(_bAssets[i].ratio);
                        response.ratioedBassetVaults[i] = ratioedBasset;
                        uint256 maxWeightInUnits = _total.mulTruncate(_bAssets[i].maxWeight);
            
                        bool bAssetOverWeight = ratioedBasset > maxWeightInUnits;
                        if(bAssetOverWeight){
                            response.isOverWeight[i] = true;
                            response.overWeightCount += 1;
                        }
                    }
                }
            
                /**
                 * @dev After the redeemed bAssets have been removed from the basket, determine
                 * if there are any resulting overweight, or underweight
                 * @param _newTotal                 Sum of collateral units in the basket
                 * @param _bAssets                  Array of all bAsset information
                 * @param _ratioedBassetVaultsAfter Array of all new bAsset vaults
                 * @param _previouslyOverWeight     Array of bools - was this bAsset already overweight
                 * @return underWeight              Array of bools - is this bAsset now under min weight
                 */
                function _getOverweightBassetsAfter(
                    uint256 _newTotal,
                    Basset[] memory _bAssets,
                    uint256[] memory _ratioedBassetVaultsAfter,
                    bool[] memory _previouslyOverWeight
                )
                    private
                    pure
                    returns (bool atLeastOneBecameOverweight)
                {
                    uint256 len = _ratioedBassetVaultsAfter.length;
            
                    for(uint256 i = 0; i < len; i++) {
                        uint256 maxWeightInUnits = _newTotal.mulTruncate(_bAssets[i].maxWeight);
            
                        bool isOverweight = _ratioedBassetVaultsAfter[i] > maxWeightInUnits;
                        // If it was not previously overweight, and now it, then it became overweight
                        bool becameOverweight = !_previouslyOverWeight[i] && isOverweight;
                        atLeastOneBecameOverweight = atLeastOneBecameOverweight || becameOverweight;
                    }
                }
            }

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