contract ERC721Proxy is
    MixinAuthorizable
{
    // Id of this proxy.
    bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));

    // solhint-disable-next-line payable-fallback
    function ()
        external
    {
        assembly {
            // The first 4 bytes of calldata holds the function selector
            let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)

            // `transferFrom` will be called with the following parameters:
            // assetData Encoded byte array.
            // from Address to transfer asset from.
            // to Address to transfer asset to.
            // amount Amount of asset to transfer.
            // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
            if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {

                // To lookup a value in a mapping, we load from the storage location keccak256(k, p),
                // where k is the key left padded to 32 bytes and p is the storage slot
                let start := mload(64)
                mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
                mstore(add(start, 32), authorized_slot)

                // Revert if authorized[msg.sender] == false
                if iszero(sload(keccak256(start, 64))) {
                    // Revert with `Error("SENDER_NOT_AUTHORIZED")`
                    mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                    mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
                    mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
                    mstore(96, 0)
                    revert(0, 100)
                }

                // `transferFrom`.
                // The function is marked `external`, so no abi decodeding is done for
                // us. Instead, we expect the `calldata` memory to contain the
                // following:
                //
                // | Area     | Offset | Length  | Contents                            |
                // |----------|--------|---------|-------------------------------------|
                // | Header   | 0      | 4       | function selector                   |
                // | Params   |        | 4 * 32  | function parameters:                |
                // |          | 4      |         |   1. offset to assetData (*)        |
                // |          | 36     |         |   2. from                           |
                // |          | 68     |         |   3. to                             |
                // |          | 100    |         |   4. amount                         |
                // | Data     |        |         | assetData:                          |
                // |          | 132    | 32      | assetData Length                    |
                // |          | 164    | **      | assetData Contents                  |
                //
                // (*): offset is computed from start of function parameters, so offset
                //      by an additional 4 bytes in the calldata.
                //
                // (**): see table below to compute length of assetData Contents
                //
                // WARNING: The ABIv2 specification allows additional padding between
                //          the Params and Data section. This will result in a larger
                //          offset to assetData.

                // Asset data itself is encoded as follows:
                //
                // | Area     | Offset | Length  | Contents                            |
                // |----------|--------|---------|-------------------------------------|
                // | Header   | 0      | 4       | function selector                   |
                // | Params   |        | 2 * 32  | function parameters:                |
                // |          | 4      | 12 + 20 |   1. token address                  |
                // |          | 36     |         |   2. tokenId                        |

                // We construct calldata for the `token.transferFrom` ABI.
                // The layout of this calldata is in the table below.
                //
                // | Area     | Offset | Length  | Contents                            |
                // |----------|--------|---------|-------------------------------------|
                // | Header   | 0      | 4       | function selector                   |
                // | Params   |        | 3 * 32  | function parameters:                |
                // |          | 4      |         |   1. from                           |
                // |          | 36     |         |   2. to                             |
                // |          | 68     |         |   3. tokenId                        |

                // There exists only 1 of each token.
                // require(amount == 1, "INVALID_AMOUNT")
                if sub(calldataload(100), 1) {
                    // Revert with `Error("INVALID_AMOUNT")`
                    mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                    mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
                    mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)
                    mstore(96, 0)
                    revert(0, 100)
                }

                /////// Setup Header Area ///////
                // This area holds the 4-byte `transferFrom` selector.
                // Any trailing data in transferFromSelector will be
                // overwritten in the next `mstore` call.
                mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)

                /////// Setup Params Area ///////
                // We copy the fields `from` and `to` in bulk
                // from our own calldata to the new calldata.
                calldatacopy(4, 36, 64)

                // Copy `tokenId` field from our own calldata to the new calldata.
                let assetDataOffset := calldataload(4)
                calldatacopy(68, add(assetDataOffset, 72), 32)

                /////// Call `token.transferFrom` using the calldata ///////
                let token := calldataload(add(assetDataOffset, 40))
                let success := call(
                    gas,            // forward all gas
                    token,          // call address of token contract
                    0,              // don't send any ETH
                    0,              // pointer to start of input
                    100,            // length of input
                    0,              // write output to null
                    0               // output size is 0 bytes
                )
                if success {
                    return(0, 0)
                }

                // Revert with `Error("TRANSFER_FAILED")`
                mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
                mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
                mstore(96, 0)
                revert(0, 100)
            }

            // Revert if undefined function is called
            revert(0, 0)
        }
    }

    /// @dev Gets the proxy id associated with the proxy address.
    /// @return Proxy id.
    function getProxyId()
        external
        pure
        returns (bytes4)
    {
        return PROXY_ID;
    }
}
function ()
    external
{
    assembly {
        // The first 4 bytes of calldata holds the function selector
        let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)

        // `transferFrom` will be called with the following parameters:
        // assetData Encoded byte array.
        // from Address to transfer asset from.
        // to Address to transfer asset to.
        // amount Amount of asset to transfer.
        // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
        if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {

            // To lookup a value in a mapping, we load from the storage location keccak256(k, p),
            // where k is the key left padded to 32 bytes and p is the storage slot
            let start := mload(64)
            mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(start, 32), authorized_slot)

            // Revert if authorized[msg.sender] == false
            if iszero(sload(keccak256(start, 64))) {
                // Revert with `Error("SENDER_NOT_AUTHORIZED")`
                mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
                mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
                mstore(96, 0)
                revert(0, 100)
            }

            // `transferFrom`.
            // The function is marked `external`, so no abi decodeding is done for
            // us. Instead, we expect the `calldata` memory to contain the
            // following:
            //
            // | Area     | Offset | Length  | Contents                            |
            // |----------|--------|---------|-------------------------------------|
            // | Header   | 0      | 4       | function selector                   |
            // | Params   |        | 4 * 32  | function parameters:                |
            // |          | 4      |         |   1. offset to assetData (*)        |
            // |          | 36     |         |   2. from                           |
            // |          | 68     |         |   3. to                             |
            // |          | 100    |         |   4. amount                         |
            // | Data     |        |         | assetData:                          |
            // |          | 132    | 32      | assetData Length                    |
            // |          | 164    | **      | assetData Contents                  |
            //
            // (*): offset is computed from start of function parameters, so offset
            //      by an additional 4 bytes in the calldata.
            //
            // (**): see table below to compute length of assetData Contents
            //
            // WARNING: The ABIv2 specification allows additional padding between
            //          the Params and Data section. This will result in a larger
            //          offset to assetData.

            // Asset data itself is encoded as follows:
            //
            // | Area     | Offset | Length  | Contents                            |
            // |----------|--------|---------|-------------------------------------|
            // | Header   | 0      | 4       | function selector                   |
            // | Params   |        | 2 * 32  | function parameters:                |
            // |          | 4      | 12 + 20 |   1. token address                  |
            // |          | 36     |         |   2. tokenId                        |

            // We construct calldata for the `token.transferFrom` ABI.
            // The layout of this calldata is in the table below.
            //
            // | Area     | Offset | Length  | Contents                            |
            // |----------|--------|---------|-------------------------------------|
            // | Header   | 0      | 4       | function selector                   |
            // | Params   |        | 3 * 32  | function parameters:                |
            // |          | 4      |         |   1. from                           |
            // |          | 36     |         |   2. to                             |
            // |          | 68     |         |   3. tokenId                        |

            // There exists only 1 of each token.
            // require(amount == 1, "INVALID_AMOUNT")
            if sub(calldataload(100), 1) {
                // Revert with `Error("INVALID_AMOUNT")`
                mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
                mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)
                mstore(96, 0)
                revert(0, 100)
            }

            /////// Setup Header Area ///////
            // This area holds the 4-byte `transferFrom` selector.
            // Any trailing data in transferFromSelector will be
            // overwritten in the next `mstore` call.
            mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)

            /////// Setup Params Area ///////
            // We copy the fields `from` and `to` in bulk
            // from our own calldata to the new calldata.
            calldatacopy(4, 36, 64)

            // Copy `tokenId` field from our own calldata to the new calldata.
            let assetDataOffset := calldataload(4)
            calldatacopy(68, add(assetDataOffset, 72), 32)

            /////// Call `token.transferFrom` using the calldata ///////
            let token := calldataload(add(assetDataOffset, 40))
            let success := call(
                gas,            // forward all gas
                token,          // call address of token contract
                0,              // don't send any ETH
                0,              // pointer to start of input
                100,            // length of input
                0,              // write output to null
                0               // output size is 0 bytes
            )
            if success {
                return(0, 0)
            }

            // Revert with `Error("TRANSFER_FAILED")`
            mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
            mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
            mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
            mstore(96, 0)
            revert(0, 100)
        }

        // Revert if undefined function is called
        revert(0, 0)
    }
}

// solhint-disable-next-line payable-fallback