ETH Price: $2,628.21 (-3.27%)

Transaction Decoder

Block:
14031670 at Jan-18-2022 08:23:56 PM +UTC
Transaction Fee:
0.00393668439742788 ETH $10.35
Gas Used:
35,070 Gas / 112.252192684 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x3B385794...59c08bFfc
0.010794902211755034 Eth
Nonce: 26
0.006858217814327154 Eth
Nonce: 27
0.00393668439742788
(Ethermine)
1,171.771161653138866819 Eth1,171.771281241838866819 Eth0.0001195887

Execution Trace

AggregationRouter.swap( caller=0xd12bcdFB9A39BE79DA3bDF02557EFdcD5CA59e77, desc=[{name:srcToken, type:address, order:1, indexed:false, value:0xdd974D5C2e2928deA5F71b9825b8b646686BD200, valueString:0xdd974D5C2e2928deA5F71b9825b8b646686BD200}, {name:dstToken, type:address, order:2, indexed:false, value:0xdeFA4e8a7bcBA345F687a2f1456F5Edd9CE97202, valueString:0xdeFA4e8a7bcBA345F687a2f1456F5Edd9CE97202}, {name:srcReceivers, type:address[], order:3, indexed:false, value:[], valueString:[]}, {name:srcAmounts, type:uint256[], order:4, indexed:false, value:[], valueString:[]}, {name:dstReceiver, type:address, order:5, indexed:false, value:0x3B385794F894612D2287354e803993C59c08bFfc, valueString:0x3B385794F894612D2287354e803993C59c08bFfc}, {name:amount, type:uint256, order:6, indexed:false, value:32822229857267213892, valueString:32822229857267213892}, {name:minReturnAmount, type:uint256, order:7, indexed:false, value:31905213297284983116, valueString:31905213297284983116}, {name:flags, type:uint256, order:8, indexed:false, value:32, valueString:32}, {name:permit, type:bytes, order:9, indexed:false, value:0x, valueString:0x}], data=0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000061E7275F00000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000E294B87474C9ED8AECECC014F6032888FD0F49800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000001C77FFC27D2A1E6440000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000E294B87474C9ED8AECECC014F6032888FD0F4980000000000000000000000000DD974D5C2E2928DEA5F71B9825B8B646686BD200000000000000000000000000DEFA4E8A7BCBA345F687A2F1456F5EDD9CE972020000000000000000000000003B385794F894612D2287354E803993C59C08BFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 )
  • AdminUpgradeabilityProxy.70a08231( )
    swap[AggregationRouter (ln:393)]
    File 1 of 2: AggregationRouter
    // SPDX-License-Identifier: MIT
    
    pragma solidity >=0.7.6;
    pragma abicoder v2;
    
    interface IERC20 {
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
        event Transfer(address indexed from, address indexed to, uint256 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 (uint256);
    
        function balanceOf(address owner) external view returns (uint256);
    
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
    
        function approve(address spender, uint256 value) external returns (bool);
    
        function transfer(address to, uint256 value) external returns (bool);
    
        function transferFrom(
            address from,
            address to,
            uint256 value
        ) external returns (bool);
    }
    
    pragma solidity >=0.6.12;
    
    interface IAggregationExecutor {
        function callBytes(bytes calldata data) external payable;  // 0xd9c45357
        // callbytes per swap sequence
        function swapSingleSequence(
            bytes calldata data
        ) external;
        function finalTransactionProcessing(
            address tokenIn,
            address tokenOut,
            address to,
            bytes calldata destTokenFeeData
        ) external;
    }
    
    // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
    library TransferHelper {
        function safeApprove(
            address token,
            address to,
            uint256 value
        ) internal {
            // bytes4(keccak256(bytes('approve(address,uint256)')));
            (bool success, bytes memory data) =
                token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
            require(
                success && (data.length == 0 || abi.decode(data, (bool))),
                "TransferHelper: APPROVE_FAILED"
            );
        }
    
        function safeTransfer(
            address token,
            address to,
            uint256 value
        ) internal {
            // bytes4(keccak256(bytes('transfer(address,uint256)')));
            (bool success, bytes memory data) =
                token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
            require(
                success && (data.length == 0 || abi.decode(data, (bool))),
                "TransferHelper: TRANSFER_FAILED"
            );
        }
    
        function safeTransferFrom(
            address token,
            address from,
            address to,
            uint256 value
        ) internal {
            // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
            (bool success, bytes memory data) =
                token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
            require(
                success && (data.length == 0 || abi.decode(data, (bool))),
                "TransferHelper: TRANSFER_FROM_FAILED"
            );
        }
    
        function safeTransferETH(address to, uint256 value) internal {
            (bool success, ) = to.call{value: value}(new bytes(0));
            require(success, "TransferHelper: ETH_TRANSFER_FAILED");
        }
    }
    
    // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
    library SafeMath {
        function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
            require((z = x + y) >= x, "ds-math-add-overflow");
        }
    
        function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
            require((z = x - y) <= x, "ds-math-sub-underflow");
        }
    
        function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
            require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
        }
    
        function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
            require(b > 0, "ds-math-division-by-zero");
            c = a / b;
        }
    }
    
    interface IERC20Permit {
        function permit(
            address owner,
            address spender,
            uint256 amount,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    }
    
    library RevertReasonParser {
        function parse(bytes memory data, string memory prefix)
            internal
            pure
            returns (string memory)
        {
            // https://solidity.readthedocs.io/en/latest/control-structures.html#revert
            // We assume that revert reason is abi-encoded as Error(string)
    
            // 68 = 4-byte selector 0x08c379a0 + 32 bytes offset + 32 bytes length
            if (
                data.length >= 68 &&
                data[0] == "\x08" &&
                data[1] == "\xc3" &&
                data[2] == "\x79" &&
                data[3] == "\xa0"
            ) {
                string memory reason;
                // solhint-disable no-inline-assembly
                assembly {
                    // 68 = 32 bytes data length + 4-byte selector + 32 bytes offset
                    reason := add(data, 68)
                }
                /*
                    revert reason is padded up to 32 bytes with ABI encoder: Error(string)
                    also sometimes there is extra 32 bytes of zeros padded in the end:
                    https://github.com/ethereum/solidity/issues/10170
                    because of that we can't check for equality and instead check
                    that string length + extra 68 bytes is less than overall data length
                */
                require(
                    data.length >= 68 + bytes(reason).length,
                    "Invalid revert reason"
                );
                return string(abi.encodePacked(prefix, "Error(", reason, ")"));
            }
            // 36 = 4-byte selector 0x4e487b71 + 32 bytes integer
            else if (
                data.length == 36 &&
                data[0] == "\x4e" &&
                data[1] == "\x48" &&
                data[2] == "\x7b" &&
                data[3] == "\x71"
            ) {
                uint256 code;
                // solhint-disable no-inline-assembly
                assembly {
                    // 36 = 32 bytes data length + 4-byte selector
                    code := mload(add(data, 36))
                }
                return
                    string(abi.encodePacked(prefix, "Panic(", _toHex(code), ")"));
            }
    
            return string(abi.encodePacked(prefix, "Unknown(", _toHex(data), ")"));
        }
    
        function _toHex(uint256 value) private pure returns (string memory) {
            return _toHex(abi.encodePacked(value));
        }
    
        function _toHex(bytes memory data) private pure returns (string memory) {
            bytes16 alphabet = 0x30313233343536373839616263646566;
            bytes memory str = new bytes(2 + data.length * 2);
            str[0] = "0";
            str[1] = "x";
            for (uint256 i = 0; i < data.length; i++) {
                str[2 * i + 2] = alphabet[uint8(data[i] >> 4)];
                str[2 * i + 3] = alphabet[uint8(data[i] & 0x0f)];
            }
            return string(str);
        }
    }
    
    contract Permitable {
        event Error(string reason);
    
        function _permit(
            IERC20 token,
            uint256 amount,
            bytes calldata permit
        ) internal {
            if (permit.length == 32 * 7) {
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory result) =
                    address(token).call(
                        abi.encodePacked(IERC20Permit.permit.selector, permit)
                    );
                if (!success) {
                    string memory reason =
                        RevertReasonParser.parse(result, "Permit call failed: ");
                    if (token.allowance(msg.sender, address(this)) < amount) {
                        revert(reason);
                    } else {
                        emit Error(reason);
                    }
                }
            }
        }
    }
    
    /*
     * @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.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address payable) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(
            address indexed previousOwner,
            address indexed newOwner
        );
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() internal {
            address msgSender = _msgSender();
            _owner = msgSender;
            emit OwnershipTransferred(address(0), msgSender);
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(
                newOwner != address(0),
                "Ownable: new owner is the zero address"
            );
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    
    contract AggregationRouter is Permitable, Ownable {
        using SafeMath for uint256;
        address public immutable WETH;
        address private constant ETH_ADDRESS =
            address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    
        uint256 private constant _PARTIAL_FILL = 0x01;
        uint256 private constant _REQUIRES_EXTRA_ETH = 0x02;
        uint256 private constant _SHOULD_CLAIM = 0x04;
        uint256 private constant _BURN_FROM_MSG_SENDER = 0x08;
        uint256 private constant _BURN_FROM_TX_ORIGIN = 0x10;
        uint256 private constant _SIMPLE_SWAP = 0x20;
    
        struct SwapDescription {
            IERC20 srcToken;
            IERC20 dstToken;
            address[] srcReceivers;
            uint[] srcAmounts;
            address dstReceiver;
            uint256 amount;
            uint256 minReturnAmount;
            uint256 flags;
            bytes permit;
        }
    
        struct SimpleSwapData {
            address[] firstPools;
            uint256[] firstSwapAmounts;
            bytes[] swapDatas;
            uint256 deadline;
            bytes destTokenFeeData;
        }
    
        event Swapped(
            address sender,
            IERC20 srcToken,
            IERC20 dstToken,
            address dstReceiver,
            uint256 spentAmount,
            uint256 returnAmount
        );
    
        event Exchange(address pair, uint256 amountOut, address output);
    
        constructor(address _WETH) public {
            WETH = _WETH;
        }
    
        receive() external payable {
            assert(msg.sender == WETH);
            // only accept ETH via fallback from the WETH contract
        }
    
        function rescueFunds(address token, uint256 amount) external onlyOwner {
            if (_isETH(IERC20(token))) {
                TransferHelper.safeTransferETH(msg.sender, amount);
            } else {
                TransferHelper.safeTransfer(token, msg.sender, amount);
            }
        }
    
        function swap(
            IAggregationExecutor caller,
            SwapDescription calldata desc,
            bytes calldata data
        ) external payable returns (uint256 returnAmount) {
            require(desc.minReturnAmount > 0, "Min return should not be 0");
            require(data.length > 0, "data should be not zero");
    
            uint256 flags = desc.flags;
    
            // simple mode swap
            if (flags & _SIMPLE_SWAP != 0) return swapSimpleMode(caller, desc, data);
    
            uint256 amount = desc.amount;
            IERC20 srcToken = desc.srcToken;
            IERC20 dstToken = desc.dstToken;
    
            if (flags & _REQUIRES_EXTRA_ETH != 0) {
                require(
                    msg.value > (_isETH(srcToken) ? amount : 0),
                    "Invalid msg.value"
                );
            } else {
                require(
                    msg.value == (_isETH(srcToken) ? amount : 0),
                    "Invalid msg.value"
                );
            }
    
            require(
                desc.srcReceivers.length == desc.srcAmounts.length,
                "Invalid lengths for receiving src tokens"
            );
    
            if (flags & _SHOULD_CLAIM != 0) {
                require(!_isETH(srcToken), "Claim token is ETH");
                _permit(srcToken, amount, desc.permit);
                for (uint i = 0; i < desc.srcReceivers.length; i++) {
                    TransferHelper.safeTransferFrom(
                        address(srcToken),
                        msg.sender,
                        desc.srcReceivers[i],
                        desc.srcAmounts[i]
                    );
                }
            }
    
            if (_isETH(srcToken)) {
                // normally in case taking fee in srcToken and srcToken is the native token
                for (uint i = 0; i < desc.srcReceivers.length; i++) {
                    TransferHelper.safeTransferETH(
                        desc.srcReceivers[i],
                        desc.srcAmounts[i]
                    );
                }
            }
    
            address dstReceiver =
                (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver;
            uint256 initialSrcBalance =
                (flags & _PARTIAL_FILL != 0) ? _getBalance(srcToken, msg.sender) : 0;
            uint256 initialDstBalance = _getBalance(dstToken, dstReceiver);
    
            {
                // solhint-disable-next-line avoid-low-level-calls
                // may take some native tokens for commission fee
                uint256 ethAmount = _getBalance(IERC20(ETH_ADDRESS), address(this));
                if (ethAmount > msg.value) ethAmount = msg.value;
                (bool success, bytes memory result) =
                    address(caller).call{value: ethAmount}(
                        abi.encodeWithSelector(caller.callBytes.selector, data)
                    );
                if (!success) {
                    revert(RevertReasonParser.parse(result, "callBytes failed: "));
                }
            }
    
            uint256 spentAmount = amount;
            returnAmount = _getBalance(dstToken, dstReceiver).sub(initialDstBalance);
    
            if (flags & _PARTIAL_FILL != 0) {
                spentAmount = initialSrcBalance.add(amount).sub(
                    _getBalance(srcToken, msg.sender)
                );
                require(
                    returnAmount.mul(amount) >=
                        desc.minReturnAmount.mul(spentAmount),
                    "Return amount is not enough"
                );
            } else {
                require(
                    returnAmount >= desc.minReturnAmount,
                    "Return amount is not enough"
                );
            }
    
            emit Swapped(
                msg.sender,
                srcToken,
                dstToken,
                dstReceiver,
                spentAmount,
                returnAmount
            );
            emit Exchange(
                address(caller),
                returnAmount,
                _isETH(dstToken) ? WETH : address(dstToken)
            );
        }
    
        function swapSimpleMode(
            IAggregationExecutor caller,
            SwapDescription calldata desc,
            bytes calldata data
        ) public returns (uint256 returnAmount) {
            uint256 amount = desc.amount;
            IERC20 srcToken = desc.srcToken;
            IERC20 dstToken = desc.dstToken;
            require(!_isETH(srcToken), "src is eth, should use normal swap");
    
            _permit(srcToken, amount, desc.permit);
    
            uint256 totalSwapAmount = amount;
            if (desc.srcReceivers.length > 0) {
                // take fee in tokenIn
                require(
                    desc.srcReceivers.length == 1 &&
                    desc.srcReceivers.length == desc.srcAmounts.length,
                    "Wrong number of src receivers"
                );
                TransferHelper.safeTransferFrom(
                    address(srcToken),
                    msg.sender,
                    desc.srcReceivers[0],
                    desc.srcAmounts[0]
                );
                require(desc.srcAmounts[0] <= totalSwapAmount, "invalid fee amount in src token");
                totalSwapAmount -= desc.srcAmounts[0];
            }
    
            address dstReceiver =
                (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver;
            uint256 initialDstBalance = _getBalance(dstToken, dstReceiver);
    
            _swapMultiSequencesWithSimpleMode(
                caller,
                address(srcToken),
                totalSwapAmount,
                address(dstToken),
                dstReceiver,
                data
            );
    
            returnAmount = _getBalance(dstToken, dstReceiver).sub(initialDstBalance);
    
            require(
                returnAmount >= desc.minReturnAmount,
                "Return amount is not enough"
            );
    
            emit Swapped(
                msg.sender,
                srcToken,
                dstToken,
                dstReceiver,
                amount,
                returnAmount
            );
            emit Exchange(
                address(caller),
                returnAmount,
                _isETH(dstToken) ? WETH : address(dstToken)
            );
        }
    
        // Only use this mode if the first pool of each sequence can receive tokenIn directly into the pool
        function _swapMultiSequencesWithSimpleMode(
            IAggregationExecutor caller,
            address tokenIn,
            uint256 totalSwapAmount,
            address tokenOut,
            address dstReceiver,
            bytes calldata data
        ) internal {
            SimpleSwapData memory swapData = abi.decode(data, (SimpleSwapData));
            require(swapData.deadline >= block.timestamp, "ROUTER: Expired");
            require(
                swapData.firstPools.length == swapData.firstSwapAmounts.length
                && swapData.firstPools.length == swapData.swapDatas.length,
                "invalid swap data length"
            );
            uint256 numberSeq = swapData.firstPools.length;
            for (uint256 i = 0; i < numberSeq; i++) {
                // collect amount to the first pool
                TransferHelper.safeTransferFrom(
                    tokenIn,
                    msg.sender,
                    swapData.firstPools[i],
                    swapData.firstSwapAmounts[i]
                );
                require(swapData.firstSwapAmounts[i] <= totalSwapAmount, "invalid swap amount");
                totalSwapAmount -= swapData.firstSwapAmounts[i];
                {
                    // solhint-disable-next-line avoid-low-level-calls
                    // may take some native tokens for commission fee
                    (bool success, bytes memory result) =
                        address(caller).call(
                            abi.encodeWithSelector(
                                caller.swapSingleSequence.selector,
                                swapData.swapDatas[i]
                            )
                        );
                    if (!success) {
                        revert(RevertReasonParser.parse(result, "swapSingleSequence failed: "));
                    }
                }
            }
            {
                // solhint-disable-next-line avoid-low-level-calls
                // may take some native tokens for commission fee
                (bool success, bytes memory result) =
                    address(caller).call(
                        abi.encodeWithSelector(
                            caller.finalTransactionProcessing.selector,
                            tokenIn,
                            tokenOut,
                            dstReceiver,
                            swapData.destTokenFeeData
                        )
                    );
                if (!success) {
                    revert(RevertReasonParser.parse(result, "finalTransactionProcessing failed: "));
                }
            }
        }
    
        function _getBalance(IERC20 token, address account)
            internal
            view
            returns (uint256)
        {
            if (_isETH(token)) {
                return account.balance;
            } else {
                return token.balanceOf(account);
            }
        }
    
        function _isETH(IERC20 token) internal pure returns (bool) {
            return (address(token) == ETH_ADDRESS);
        }
    }

    File 2 of 2: AdminUpgradeabilityProxy
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import './UpgradeabilityProxy.sol';
    /**
     * @title AdminUpgradeabilityProxy
     * @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 AdminUpgradeabilityProxy is UpgradeabilityProxy {
      /**
       * Contract constructor.
       * @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.
       */
      constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
        assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
        _setAdmin(_admin);
      }
      /**
       * @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 adm 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 override virtual {
        require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
        super._willFallback();
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import './Proxy.sol';
    import '@openzeppelin/contracts/utils/Address.sol';
    /**
     * @title UpgradeabilityProxy
     * @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 UpgradeabilityProxy is Proxy {
      /**
       * @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);
        }
      }  
      /**
       * @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 impl Address of the current implementation
       */
      function _implementation() internal override 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(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
        bytes32 slot = IMPLEMENTATION_SLOT;
        assembly {
          sstore(slot, newImplementation)
        }
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /**
     * @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.
     */
    abstract contract Proxy {
      /**
       * @dev Fallback function.
       * Implemented entirely in `_fallback`.
       */
      fallback () payable external {
        _fallback();
      }
      /**
       * @dev Receive function.
       * Implemented entirely in `_fallback`.
       */
      receive () payable external {
        _fallback();
      }
      /**
       * @return The Address of the implementation.
       */
      function _implementation() internal virtual 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 virtual {
      }
      /**
       * @dev fallback implementation.
       * Extracted to enable manual triggering.
       */
      function _fallback() internal {
        _willFallback();
        _delegate(_implementation());
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.2 <0.8.0;
    /**
     * @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) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 0;
        }
        /**
         * @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].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
            (bool success, ) = recipient.call{ value: amount }("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain`call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.call{ value: value }(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.staticcall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }