ETH Price: $2,938.43 (-0.51%)
 

Overview

Max Total Supply

10,000,000,000 ELIZ

Holders

30,796 (0.00%)

Market

Price

$0.00 @ 0.000000 ETH

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Filtered by Token Holder
melmel.base.eth
Balance
1 ELIZ

Value
$0.00
0x67a2d1566d6d6f6cb1867a2de7f4f05b33c07c03
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

ELiZ by Unchained Elephants is a cryptocurrency project with elephant welfare charitable fundamentals at its core. It aims to rescue elephants in dire conditions and relocate them to sanctuaries and be a digital advocate for elephants in Asia.

Contract Source Code Verified (Exact Match)

Contract Name:
MEME404

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 1 runs

Other Settings:
shanghai EvmVersion, BSL 1.1 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at basescan.org on 2024-08-12
*/

/// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0 ^0.8.23 ^0.8.4;

// lib/solady/src/utils/FixedPointMathLib.sol

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant WAD = 1e18;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if mul(y, gt(x, div(not(0), y))) {
                mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if mul(y, gt(x, div(not(0), y))) {
                mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
            if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(and(iszero(iszero(y)), eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
            if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
                // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
                if iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5 ** 18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // `k` is in the range `[-61, 195]`.

            // Evaluate using a (6, 7)-term rational approximation.
            // `p` is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            /// @solidity memory-safe-assembly
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already `2**96` too large.
                r := sdiv(p, q)
            }

            // r should be in the range `(0.09, 0.25) * 2**96`.

            // We now need to multiply r by:
            // - The scale factor `s ≈ 6.031367120`.
            // - The `2**k` factor from the range reduction.
            // - The `1e18 / 2**96` factor for base conversion.
            // We do this all at once, with an intermediate result in `2**213`
            // basis, so the final right shift is always by a positive amount.
            r = int256(
                (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            int256 wad = int256(WAD);
            int256 p = x;
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (w >> 63 == 0) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == 0) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != 0);
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c != 0) {
                int256 t = w | 1;
                /// @solidity memory-safe-assembly
                assembly {
                    x := sdiv(mul(x, wad), t)
                }
                x = (t * (wad + lnWad(x)));
                /// @solidity memory-safe-assembly
                assembly {
                    w := sdiv(x, add(wad, t))
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                // 512-bit multiply `[p1 p0] = x * y`.
                // Compute the product mod `2**256` and mod `2**256 - 1`
                // then use the Chinese Remainder Theorem to reconstruct
                // the 512 bit result. The result is stored in two 256
                // variables such that `product = p1 * 2**256 + p0`.

                // Least significant 256 bits of the product.
                result := mul(x, y) // Temporarily use `result` as `p0` to save gas.
                let mm := mulmod(x, y, not(0))
                // Most significant 256 bits of the product.
                let p1 := sub(mm, add(result, lt(mm, result)))

                // Handle non-overflow cases, 256 by 256 division.
                if iszero(p1) {
                    if iszero(d) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    result := div(result, d)
                    break
                }

                // Make sure the result is less than `2**256`. Also prevents `d == 0`.
                if iszero(gt(d, p1)) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }

                /*------------------- 512 by 256 division --------------------*/

                // Make division exact by subtracting the remainder from `[p1 p0]`.
                // Compute remainder using mulmod.
                let r := mulmod(x, y, d)
                // `t` is the least significant bit of `d`.
                // Always greater or equal to 1.
                let t := and(d, sub(0, d))
                // Divide `d` by `t`, which is a power of two.
                d := div(d, t)
                // Invert `d mod 2**256`
                // Now that `d` is an odd number, it has an inverse
                // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                // Compute the inverse by starting with a seed that is correct
                // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                let inv := xor(2, mul(3, d))
                // Now use Newton-Raphson iteration to improve the precision.
                // Thanks to Hensel's lifting lemma, this also works in modular
                // arithmetic, doubling the correct bits in each step.
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                result :=
                    mul(
                        // Divide [p1 p0] by the factors of two.
                        // Shift in bits from `p1` into `p0`. For this we need
                        // to flip `t` such that it is `2**256 / t`.
                        or(
                            mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)),
                            div(sub(result, r), t)
                        ),
                        // inverse mod 2**256
                        mul(inv, sub(2, mul(d, inv)))
                    )
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
        result = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                result := add(result, 1)
                if iszero(result) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, y), d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if iszero(iszero(x)) {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), z)

            // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
            // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
            // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
            // That's not possible if `x < 256` but we can just verify those cases exhaustively.

            // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
            // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
            // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

            // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
            // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
            // with largest error when `s = 1` and when `s = 256` or `1/256`.

            // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
            // Then we can estimate `sqrt(y)` using
            // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

            // There is no overflow risk here since `y < 2**136` after the first branch above.
            z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))

            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))

            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)

            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            z = 10 ** 9;
            if (x <= type(uint256).max / 10 ** 36 - 1) {
                x *= 10 ** 18;
                z = 1;
            }
            z *= sqrt(x);
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`.
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            z = 10 ** 12;
            if (x <= (type(uint256).max / 10 ** 36) * 10 ** 18 - 1) {
                if (x >= type(uint256).max / 10 ** 36) {
                    x *= 10 ** 18;
                    z = 10 ** 6;
                } else {
                    x *= 10 ** 36;
                    z = 1;
                }
            }
            z *= cbrt(x);
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for { result := 1 } x { x := sub(x, 1) } { result := mul(result, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(sub(0, shr(255, x)), add(sub(0, shr(255, x)), x))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(mul(xor(sub(y, x), sub(x, y)), sgt(x, y)), sub(y, x))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

// lib/solmate/src/tokens/ERC1155.sol

/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );

    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] amounts
    );

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    event URI(string value, uint256 indexed id);

    /*//////////////////////////////////////////////////////////////
                             ERC1155 STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(address => mapping(uint256 => uint256)) public balanceOf;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                             METADATA LOGIC
    //////////////////////////////////////////////////////////////*/

    function uri(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                              ERC1155 LOGIC
    //////////////////////////////////////////////////////////////*/

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) public virtual {
        require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");

        balanceOf[from][id] -= amount;
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, from, to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
                    ERC1155TokenReceiver.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) public virtual {
        require(ids.length == amounts.length, "LENGTH_MISMATCH");

        require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");

        // Storing these outside the loop saves ~15 gas per iteration.
        uint256 id;
        uint256 amount;

        for (uint256 i = 0; i < ids.length; ) {
            id = ids[i];
            amount = amounts[i];

            balanceOf[from][id] -= amount;
            balanceOf[to][id] += amount;

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, from, to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
                    ERC1155TokenReceiver.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
        public
        view
        virtual
        returns (uint256[] memory balances)
    {
        require(owners.length == ids.length, "LENGTH_MISMATCH");

        balances = new uint256[](owners.length);

        // Unchecked because the only math done is incrementing
        // the array index counter which cannot possibly overflow.
        unchecked {
            for (uint256 i = 0; i < owners.length; ++i) {
                balances[i] = balanceOf[owners[i]][ids[i]];
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
            interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, address(0), to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
                    ERC1155TokenReceiver.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i = 0; i < idsLength; ) {
            balanceOf[to][ids[i]] += amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, address(0), to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
                    ERC1155TokenReceiver.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchBurn(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i = 0; i < idsLength; ) {
            balanceOf[from][ids[i]] -= amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, from, address(0), ids, amounts);
    }

    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        balanceOf[from][id] -= amount;

        emit TransferSingle(msg.sender, from, address(0), id, amount);
    }
}

/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155TokenReceiver {
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC1155TokenReceiver.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
    }
}

// lib/solmate/src/tokens/ERC20.sol

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

// lib/solmate/src/tokens/ERC721.sol

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

// lib/solmate/src/utils/ReentrancyGuard.sol

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() virtual {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}

// src/interfaces/IMEME1155.sol

interface IMEME1155 {
    function mint(address account, uint256 id, uint256 amount, bytes memory data) external;

    function burn(address account, uint256 id, uint256 amount) external;

    function balanceOf(address account, uint256 id) external view returns (uint256);
}

// src/interfaces/IMEME404.sol

/// @title Trugly's IMEME404
/// @notice Contract automatically generated by https://www.trugly.meme
interface IMEME404 {
    struct TierCreateParam {
        string baseURL;
        string nftName;
        string nftSymbol;
        uint256 amountThreshold;
        uint256 nftId;
        uint256 lowerId;
        uint256 upperId;
        bool isFungible;
    }

    /// @notice Can only be called by NFT collection
    /// @dev Raw transfer of memecoins
    /// @dev This function bypasses the NFT mint/burn, approval and any fees
    function transferFromNFT(address from, address to, uint256 nftTokenId) external returns (bool);

    /// @dev Initialize the _tiers
    /// @dev Is called automatically by the Memeception contract
    function initializeTiers(TierCreateParam[] memory _tierParams, address[] memory _exempt) external;
}

// src/interfaces/IMEME721.sol

interface IMEME721 {
    function mint(address account, uint256 id) external;
    function burn(uint256 id) external;

    function nextOwnedTokenId(address account) external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);
}

// src/interfaces/ITruglyFactoryNFT.sol

/// @title Trugly's Manager to create memecoins
interface ITruglyFactoryNFT {
    function createMeme1155(
        string memory name,
        string memory symbol,
        address meme404,
        address creator,
        string memory baseURI
    ) external returns (address);

    function createMeme721(
        string memory name,
        string memory symbol,
        address meme404,
        address creator,
        string memory baseURI
    ) external returns (address);
}

// src/libraries/MEME20Constant.sol

library MEME20Constant {
    /// @dev Total supply of the Meme token
    uint256 internal constant TOKEN_TOTAL_SUPPLY = 10_000_000_000 ether;

    /// @dev Token decimals
    uint8 internal constant TOKEN_DECIMALS = 18;

    uint256 internal constant MAX_CREATOR_FEE_BPS = 80;

    uint256 internal constant MAX_PROTOCOL_FEE_BPS = 50;
}

// src/types/MEME20.sol

/// @title ERC20 memecoin created by Trugly
/// @notice Contract automatically generated by https://www.trugly.meme
contract MEME20 is ERC20 {
    using FixedPointMathLib for uint256;

    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       EVENTS                      */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    event CreatorFeesUpdated(uint256 oldFeesBps, uint256 newFeesBps);
    event ProtocolFeesUpdated(uint256 oldFeesBps, uint256 newFeesBps);
    event CreatorAddressUpdated(address oldCreator, address newCreator);
    event ProtocolAddressUpdated(address oldProtocol, address newProtocol);
    event TreasuryUpdated(address oldTreasury, address newTreasury);
    event PoolOrRouterAdded(address indexed account);
    event ExemptAdded(address indexed account);

    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       ERRORS                      */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    error OnlyCreator();
    error OnlyProtocol();
    error CreatorFeeTooHigh();
    error ProtocolFeeTooHigh();
    error AddressZero();
    error AlreadyInitialized();
    error PoolNotInitialized();

    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       STORAGE                     */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    bool private _initialized;
    address public creator;
    address private _protocol;

    address private _pTreasury;

    uint256 public feeBps;
    uint256 private _pFeesBps;

    mapping(address => bool) private _exemptFees;
    mapping(address => bool) private _routersAndPools;

    modifier onlyCreator() {
        if (msg.sender != creator) revert OnlyCreator();
        _;
    }

    modifier onlyProtocol() {
        if (msg.sender != _protocol) revert OnlyProtocol();
        _;
    }

    constructor(string memory _name, string memory _symbol, address _memeception, address _creator)
        ERC20(_name, _symbol, MEME20Constant.TOKEN_DECIMALS)
    {
        // Set Creator
        creator = _creator;
        // Set Temporarily to TruglyMemeception (will be transfer to protocol after deployment and setting routers & pools)
        _protocol = _memeception;

        // Exempt
        _addExempt(_memeception);
        _addExempt(_creator);
        _addExempt(address(this));
        _addExempt(address(0));

        // Mint to Launchpad
        _mint(_memeception, MEME20Constant.TOKEN_TOTAL_SUPPLY);
    }

    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        if (amount == 0) return super.transferFrom(from, to, 0);

        amount = _transferFees(from, to, amount, true);

        return super.transferFrom(from, to, amount);
    }

    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        if (amount == 0) return super.transfer(to, 0);

        amount = _transferFees(msg.sender, to, amount, false);

        return super.transfer(to, amount);
    }

    function _transferFees(address from, address to, uint256 amount, bool isTransferFrom) internal returns (uint256) {
        /// @dev Forbid trading until UniV3 LP is initialized
        if (!_initialized && msg.sender != _protocol) revert PoolNotInitialized();

        // @dev skip to avoid double fees;
        bool skip = _routersAndPools[from] && _routersAndPools[to];

        if (!_exemptFees[from] && !_exemptFees[to] && !skip && _routersAndPools[from]) {
            uint256 feesCreator = amount.mulDiv(feeBps, 1e4);
            uint256 feesProtocol = amount.mulDiv(_pFeesBps, 1e4);
            amount = amount - feesCreator - feesProtocol;
            if (isTransferFrom) {
                if (feesCreator > 0) super.transferFrom(from, creator, feesCreator);
                if (feesProtocol > 0) super.transferFrom(from, _pTreasury, feesProtocol);
            } else {
                if (feesCreator > 0) super.transfer(creator, feesCreator);
                if (feesProtocol > 0) super.transfer(_pTreasury, feesProtocol);
            }
        }

        return amount;
    }

    function isExempt(address account) public view returns (bool) {
        return _exemptFees[account];
    }

    function isPoolOrRouter(address account) public view returns (bool) {
        return _routersAndPools[account];
    }

    function addPoolOrRouter(address _contract) public onlyCreator {
        _addPoolOrRouter(_contract);
    }

    function _addPoolOrRouter(address _contract) internal {
        _routersAndPools[_contract] = true;
        emit PoolOrRouterAdded(_contract);
    }

    function addExempt(address _contract) public onlyCreator {
        _addExempt(_contract);
    }

    function _addExempt(address _contract) internal {
        _exemptFees[_contract] = true;
        emit ExemptAdded(_contract);
    }

    function setCreatorFeeBps(uint256 _newFeeBps) public onlyCreator {
        if (_newFeeBps > MEME20Constant.MAX_CREATOR_FEE_BPS) revert CreatorFeeTooHigh();
        emit CreatorFeesUpdated(feeBps, _newFeeBps);
        feeBps = _newFeeBps;
    }

    function setCreatorAddress(address _creator) public onlyCreator {
        emit CreatorAddressUpdated(creator, _creator);
        creator = _creator;
    }

    function setProtocolFeeBps(uint256 _newFeeBps) public onlyProtocol {
        if (_newFeeBps > MEME20Constant.MAX_PROTOCOL_FEE_BPS) revert ProtocolFeeTooHigh();
        emit ProtocolFeesUpdated(_pFeesBps, _newFeeBps);
        _pFeesBps = _newFeeBps;
    }

    function setProtocolAddress(address _protocolAddress) public onlyProtocol {
        if (_protocolAddress == address(0)) revert AddressZero();
        emit ProtocolAddressUpdated(_protocol, _protocolAddress);
        _protocol = _protocolAddress;
    }

    function setTreasuryAddress(address _treasury) public onlyProtocol {
        if (_treasury == address(0)) revert AddressZero();
        emit TreasuryUpdated(_pTreasury, _treasury);
        _pTreasury = _treasury;
    }

    function initialize(
        address _protocolAddr,
        address _protocolTreasury,
        uint256 _protocolFeesBps,
        uint256 _creatorFeesBps,
        address _pool,
        address[] calldata _routers,
        address[] calldata _exemptsAddr
    ) public onlyProtocol {
        if (_initialized) revert AlreadyInitialized();
        _initialized = true;
        feeBps = _creatorFeesBps;

        // Set Protocol
        _pFeesBps = _protocolFeesBps;
        _pTreasury = _protocolTreasury;

        _addExempt(_pTreasury);
        _addExempt(_protocolAddr);

        // Uniswap
        for (uint256 i = 0; i < _routers.length; i++) {
            _addPoolOrRouter(_routers[i]);
        }
        _addPoolOrRouter(_pool);

        for (uint256 i = 0; i < _exemptsAddr.length; i++) {
            _addExempt(_exemptsAddr[i]);
        }

        // Transfer to Protocol
        setProtocolAddress(_protocolAddr);
    }
}

// src/types/MEME404.sol

/// @title Trugly's MEME404
/// @notice Contract automatically generated by https://www.trugly.meme
contract MEME404 is IMEME404, MEME20, ReentrancyGuard {
    using FixedPointMathLib for uint256;
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       EVENTS                      */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/

    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       ERRORS                      */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/

    /// @dev No _tiers are provided
    error NoTiers();
    /// @dev Too many _tiers are provided
    error MaxTiers();
    /// @dev When a fungible sequence has upperId that is not equal to lowerId
    error FungibleThreshold();
    /// @dev When a prev tier has a higher amount threshold than the current tier
    error AmountThreshold();
    /// @dev When a non-fungible sequence has incorrect upperId and lowerId
    error NonFungibleIds();
    /// @dev tokenId is 0
    error InvalidTierParamsZeroId();
    /// @dev Only NFT collection can call this function
    error OnlyNFT();
    /// @dev When the contract is already initialized
    error TiersAlreadyInitialized();
    /// @dev When there's not enough NFTS based on amount threshold
    error NotEnoughNFTs();
    /// @dev When a NFT sequence is followed by a fungible one
    error FungibleAfterNonFungible();
    /// @dev When a NFT sequence has nftId that is less than the previous one or is the same but isFungible is different
    error IncorrectOrder();
    /// @dev Thrown when the upperId is greater than the max uint32 value
    error MaxUpperId();

    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       STORAGE                     */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /// @dev A uint32 map in storage.
    struct Uint32Map {
        uint256 spacer;
    }

    struct Tier {
        string baseURL;
        uint256 lowerId;
        uint256 upperId;
        uint256 amountThreshold;
        bool isFungible;
        address nft;
        uint256 nextUnmintedId;
        Uint32Map burnIds;
        uint256 burnLength;
        uint256 tierId;
    }

    /// @dev NFT ID to NFT address mapping
    mapping(uint256 => address) public nftIdToAddress;

    /// @dev Tier ID to Tier mapping
    mapping(uint256 => Tier) internal _tiers;

    mapping(address => bool) internal _exemptNFTMint;

    uint256 internal _tierCount;

    bool internal _initialized;

    address internal factory;

    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    /*                       IMPLEMENTATION              */
    /* ¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯¯\_(ツ)_/¯*/
    constructor(string memory _name, string memory _symbol, address _memeception, address _creator, address _factoryNFT)
        MEME20(_name, _symbol, _memeception, _creator)
    {
        factory = _factoryNFT;
    }

    /// @dev Initialize the _tiers
    /// @dev Is called automatically by the Memeception contract
    function initializeTiers(IMEME404.TierCreateParam[] memory _tierParams, address[] memory _exempt) external {
        if (_initialized) revert TiersAlreadyInitialized();
        _initialized = true;

        if (_tierParams.length == 0) revert NoTiers();
        if (_tierParams.length > 9) revert MaxTiers();

        for (uint256 i = 0; i < _tierParams.length; i++) {
            if (_tierParams[i].lowerId == 0) revert InvalidTierParamsZeroId();
            if (_tierParams[i].amountThreshold == 0 || _tierParams[i].amountThreshold > totalSupply) {
                revert AmountThreshold();
            }
            if (_tierParams[i].upperId >= type(uint32).max) revert MaxUpperId();

            if (_tierParams[i].isFungible) {
                if (_tierParams[i].lowerId != _tierParams[i].upperId) revert FungibleThreshold();
            } else {
                if (_tierParams[i].lowerId >= _tierParams[i].upperId) revert NonFungibleIds();

                uint256 maxNFT = totalSupply.rawDiv(_tierParams[i].amountThreshold);
                if ((_tierParams[i].upperId - _tierParams[i].lowerId + 1) < maxNFT) {
                    revert NotEnoughNFTs();
                }
            }

            address existingNFTAddr = nftIdToAddress[_tierParams[i].nftId];
            Tier memory tier = Tier({
                baseURL: _tierParams[i].baseURL,
                lowerId: _tierParams[i].lowerId,
                upperId: _tierParams[i].upperId,
                amountThreshold: _tierParams[i].amountThreshold,
                isFungible: _tierParams[i].isFungible,
                nft: address(0),
                nextUnmintedId: _tierParams[i].isFungible ? 0 : _tierParams[i].lowerId,
                burnIds: Uint32Map(0),
                burnLength: 0,
                tierId: i + 1
            });
            if (i > 0) {
                Tier memory previousTier = _tiers[i];
                if (_tierParams[i - 1].nftId > _tierParams[i].nftId) revert IncorrectOrder();
                if (
                    _tierParams[i - 1].nftId == _tierParams[i].nftId
                        && _tierParams[i - 1].isFungible != _tierParams[i].isFungible
                ) revert IncorrectOrder();
                if (previousTier.amountThreshold >= tier.amountThreshold) revert AmountThreshold();
                if (!previousTier.isFungible && tier.isFungible) revert FungibleAfterNonFungible();
                if (existingNFTAddr != address(0) && previousTier.upperId >= tier.lowerId) {
                    revert NonFungibleIds();
                }
            }
            tier.nft = existingNFTAddr != address(0) ? existingNFTAddr : _createNewNFT(creator, _tierParams[i]);
            _tiers[i + 1] = tier;
        }

        for (uint256 i = 0; i < _exempt.length; i++) {
            _exemptNFTMint[_exempt[i]] = true;
        }

        _tierCount = _tierParams.length;
    }

    /// @dev Transfer of memecoins
    /// @dev If balance of sender or recipient changes tier, mint or burn NFTs accordingly
    function transfer(address to, uint256 amount) public override returns (bool) {
        _TierEligibility memory beforeTierFrom = _getTierEligibility(msg.sender);
        _TierEligibility memory beforeTierTo = _getTierEligibility(to);

        bool success = super.transfer(to, amount);

        _TierEligibility memory afterTierFrom = _getTierEligibility(msg.sender);
        _TierEligibility memory afterTierTo = _getTierEligibility(to);

        // handle burn
        _burnTier(msg.sender, beforeTierFrom, afterTierFrom, 0);
        _burnTier(to, beforeTierTo, afterTierTo, 0);
        // handle mint
        _mintTier(msg.sender, afterTierFrom);
        _mintTier(to, afterTierTo);

        return success;
    }

    /// @dev Transfer of memecoins
    /// @dev If balance of sender or recipient changes tier, mint or burn NFTs accordingly
    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        _TierEligibility memory beforeTierFrom = _getTierEligibility(from);
        _TierEligibility memory beforeTierTo = _getTierEligibility(to);

        bool success = super.transferFrom(from, to, amount);

        _TierEligibility memory afterTierFrom = _getTierEligibility(from);
        _TierEligibility memory afterTierTo = _getTierEligibility(to);

        _burnTier(from, beforeTierFrom, afterTierFrom, 0);
        _burnTier(to, beforeTierTo, afterTierTo, 0);
        _mintTier(from, afterTierFrom);
        _mintTier(to, afterTierTo);

        return success;
    }

    /// @dev Create a new NFT collection
    /// @notice ERC1155 if fungible, ERC721 if non-fungible
    function _createNewNFT(address _creator, IMEME404.TierCreateParam memory _tier)
        internal
        virtual
        returns (address)
    {
        if (_tier.isFungible) {
            address nft = ITruglyFactoryNFT(factory).createMeme1155(
                _tier.nftName, _tier.nftSymbol, address(this), _creator, _tier.baseURL
            );
            nftIdToAddress[_tier.nftId] = nft;
        } else {
            address nft = ITruglyFactoryNFT(factory).createMeme721(
                _tier.nftName, _tier.nftSymbol, address(this), _creator, _tier.baseURL
            );
            nftIdToAddress[_tier.nftId] = nft;
        }

        return nftIdToAddress[_tier.nftId];
    }

    /// @notice Can only be called by NFT collection
    /// @dev Raw transfer of memecoins
    /// @dev This function bypasses the NFT mint/burn, approval and any fees
    function transferFromNFT(address from, address to, uint256 nftTokenId) public returns (bool) {
        Tier memory tier = _getTierFromNftTokenId(msg.sender, nftTokenId);
        if (tier.nft == address(0)) revert OnlyNFT();

        _TierEligibility memory beforeTierTo = _getTierEligibility(to);

        balanceOf[from] -= tier.amountThreshold;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += tier.amountThreshold;
        }

        _TierEligibility memory afterTierFrom = _getTierEligibility(from);
        _TierEligibility memory afterTierTo = _getTierEligibility(to);

        /// @dev NFT has already been transferred
        /// Need to check if the user has decreased in tier and mint the NFTs
        _mintTier(from, afterTierFrom);

        if (afterTierTo.tierId > 0 && tier.tierId != uint256(afterTierTo.tierId)) {
            if (tier.isFungible) {
                IMEME1155(tier.nft).burn(to, nftTokenId, 1);
            } else {
                IMEME721(tier.nft).burn(nftTokenId);
                Tier storage _tierStorage = _tiers[tier.tierId];
                _set(_tierStorage.burnIds, ++_tierStorage.burnLength, uint32(nftTokenId));
            }
        }

        if (afterTierTo.tierId != beforeTierTo.tierId) {
            if (tier.nft == beforeTierTo.nft) {
                _burnTier(to, beforeTierTo, afterTierTo, 1);
            } else {
                _burnTier(to, beforeTierTo, afterTierTo, 0);
            }
            _mintTier(to, afterTierTo);
        }

        emit Transfer(from, to, tier.amountThreshold);

        return true;
    }

    /// @dev Mint NFTs once a user reaches a new tier
    /// @param _owner Address of the user
    /// @param _afterTierEligibility Current Tier + NFT balance
    function _mintTier(address _owner, _TierEligibility memory _afterTierEligibility) internal nonReentrant {
        if (_afterTierEligibility.tierId < 0 || _owner == address(0) || _exemptNFTMint[_owner]) return;

        Tier storage tier = _tiers[uint256(_afterTierEligibility.tierId)];
        if (tier.isFungible) {
            if ((_owner.code.length != 0) && !_checkERC1155Received(_owner, msg.sender, address(0), tier.lowerId, 1)) {
                return;
            }
            IMEME1155 nft = IMEME1155(tier.nft);
            if (nft.balanceOf(_owner, tier.lowerId) >= 1) return;
            nft.mint(_owner, tier.lowerId, 1, "");
        } else {
            if ((_owner.code.length != 0) && !_checkERC721Received(_owner, msg.sender, address(0), tier.lowerId, "")) {
                return;
            }

            IMEME721 nft = IMEME721(tier.nft);

            uint256 numToMint = _afterTierEligibility.expectedNFTBal > nft.balanceOf(_owner)
                ? _afterTierEligibility.expectedNFTBal - nft.balanceOf(_owner)
                : 0;

            for (uint256 i = 0; i < numToMint; i++) {
                uint256 nftIdToMint;
                if (tier.nextUnmintedId <= tier.upperId) {
                    nftIdToMint = tier.nextUnmintedId++;
                } else {
                    /// @dev this should never happen but in case it does
                    /// @dev we wouldn't want to mint any NFT and let the coins be transferred
                    if (tier.burnLength == 0) return;

                    nftIdToMint = _get(tier.burnIds, tier.burnLength--);
                }

                nft.mint(_owner, nftIdToMint);
            }
        }
    }

    /// @dev Burn NFTs once a user reaches a new tier (either going up or down)
    /// @param _owner Address of the user
    /// @param _beforeTierEligibility Before Transfer: Tier + NFT balance
    /// @param _afterTierEligibility Current Tier + NFT balance
    /// @param _incrementFromNFTTransfer Balance is incremented from transferFromNFT
    function _burnTier(
        address _owner,
        _TierEligibility memory _beforeTierEligibility,
        _TierEligibility memory _afterTierEligibility,
        uint256 _incrementFromNFTTransfer
    ) internal nonReentrant {
        if (_beforeTierEligibility.tierId < 0 || _owner == address(0)) return;

        Tier storage tier = _tiers[uint256(_beforeTierEligibility.tierId)];
        if (tier.isFungible) {
            IMEME1155 nft = IMEME1155(tier.nft);
            if (
                (nft.balanceOf(_owner, tier.lowerId) == 0)
                    || _beforeTierEligibility.tierId == _afterTierEligibility.tierId
            ) return;
            nft.burn(_owner, tier.lowerId, nft.balanceOf(_owner, tier.lowerId));
        } else {
            IMEME721 nft = IMEME721(tier.nft);

            uint256 numToBurn = _beforeTierEligibility.tierId != _afterTierEligibility.tierId
                ? _beforeTierEligibility.currentNFTBal > _incrementFromNFTTransfer
                    ? _beforeTierEligibility.currentNFTBal - _incrementFromNFTTransfer
                    : 0
                : _afterTierEligibility.currentNFTBal > _afterTierEligibility.expectedNFTBal
                    ? _afterTierEligibility.currentNFTBal - _afterTierEligibility.expectedNFTBal
                    : 0;

            for (uint256 i = 0; i < numToBurn; i++) {
                uint256 nftIdToburn = nft.nextOwnedTokenId(_owner);
                nft.burn(nftIdToburn);
                _set(tier.burnIds, ++tier.burnLength, uint32(nftIdToburn));
            }
        }
    }

    /// @dev Get the tier eligibility of a user based on their memecoin balance
    /// @param _owner Address of the user
    /// @return _TierEligibility
    function _getTierEligibility(address _owner) internal view returns (_TierEligibility memory) {
        if (_owner != address(0)) {
            uint256 balance = balanceOf[_owner];
            for (uint256 i = _tierCount; i > 0; i--) {
                if (balance >= _tiers[i].amountThreshold) {
                    return _TierEligibility({
                        tierId: int256(_tiers[i].tierId),
                        expectedNFTBal: balance.rawDiv(_tiers[i].amountThreshold),
                        nft: _tiers[i].nft,
                        currentNFTBal: _tiers[i].isFungible
                            ? IMEME1155(_tiers[i].nft).balanceOf(_owner, _tiers[i].lowerId)
                            : IMEME721(_tiers[i].nft).balanceOf(_owner)
                    });
                }
            }
        }
        return _TierEligibility({tierId: -1, expectedNFTBal: 0, nft: address(0), currentNFTBal: 0});
    }

    /// @dev Get the tier by ID
    /// @param tierId Tier ID
    /// @return Tier
    function getTier(uint256 tierId) public view returns (Tier memory) {
        return _tiers[tierId];
    }

    /// @dev Get the tier by NFT token ID
    /// @param nft NFT address
    /// @param tokenId NFT token ID
    /// @return Tier
    function _getTierFromNftTokenId(address nft, uint256 tokenId) internal view returns (Tier memory) {
        for (uint256 i = 1; i <= _tierCount; i++) {
            Tier memory tier = _tiers[i];
            if (tier.nft != nft) continue;

            if (tier.isFungible) {
                if (tier.lowerId == tokenId) {
                    return tier;
                }
            } else {
                if (tokenId >= tier.lowerId && tokenId <= tier.upperId) {
                    return tier;
                }
            }
        }
        return Tier({
            baseURL: "",
            lowerId: 0,
            upperId: 0,
            amountThreshold: 0,
            isFungible: false,
            nft: address(0),
            nextUnmintedId: 0,
            burnIds: Uint32Map(0),
            burnLength: 0,
            tierId: 0
        });
    }

    function _checkERC1155Received(address _contract, address _operator, address _from, uint256 _id, uint256 _value)
        internal
        returns (bool)
    {
        bytes memory callData = abi.encodeWithSelector(
            ERC1155TokenReceiver(_contract).onERC1155Received.selector, _operator, _from, _id, _value, ""
        );

        (bool success, bytes memory returnData) = _contract.call(callData);

        // Check both call success and return value
        if (success && returnData.length >= 32) {
            // Make sure there is enough data to cover a `bytes4` return
            bytes4 returned = abi.decode(returnData, (bytes4));
            return returned == ERC1155TokenReceiver.onERC1155Received.selector;
        }

        return false;
    }

    function _checkERC721Received(address _contract, address _operator, address _from, uint256 _id, bytes memory _data)
        internal
        returns (bool)
    {
        bytes memory callData = abi.encodeWithSelector(
            ERC721TokenReceiver(_contract).onERC721Received.selector, _operator, _from, _id, _data
        );

        (bool success, bytes memory returnData) = _contract.call(callData);

        // Check both call success and return value
        if (success && returnData.length >= 32) {
            // Make sure there is enough data to cover a `bytes4` return
            bytes4 returned = abi.decode(returnData, (bytes4));
            return returned == ERC721TokenReceiver.onERC721Received.selector;
        }

        return false;
    }

    /// @dev Returns the uint32 value at `index` in `map`.
    function _get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
            result := and(0xffffffff, shr(shl(5, and(index, 7)), sload(s)))
        }
    }

    /// @dev Updates the uint32 value at `index` in `map`.
    function _set(Uint32Map storage map, uint256 index, uint32 value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
            let o := shl(5, and(index, 7)) // Storage slot offset (bits).
            let v := sload(s) // Storage slot value.
            sstore(s, xor(v, shl(o, and(0xffffffff, xor(value, shr(o, v))))))
        }
    }

    struct _TierEligibility {
        int256 tierId;
        uint256 expectedNFTBal;
        address nft;
        uint256 currentNFTBal;
    }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_memeception","type":"address"},{"internalType":"address","name":"_creator","type":"address"},{"internalType":"address","name":"_factoryNFT","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"AmountThreshold","type":"error"},{"inputs":[],"name":"CreatorFeeTooHigh","type":"error"},{"inputs":[],"name":"FungibleAfterNonFungible","type":"error"},{"inputs":[],"name":"FungibleThreshold","type":"error"},{"inputs":[],"name":"IncorrectOrder","type":"error"},{"inputs":[],"name":"InvalidTierParamsZeroId","type":"error"},{"inputs":[],"name":"MaxTiers","type":"error"},{"inputs":[],"name":"MaxUpperId","type":"error"},{"inputs":[],"name":"NoTiers","type":"error"},{"inputs":[],"name":"NonFungibleIds","type":"error"},{"inputs":[],"name":"NotEnoughNFTs","type":"error"},{"inputs":[],"name":"OnlyCreator","type":"error"},{"inputs":[],"name":"OnlyNFT","type":"error"},{"inputs":[],"name":"OnlyProtocol","type":"error"},{"inputs":[],"name":"PoolNotInitialized","type":"error"},{"inputs":[],"name":"ProtocolFeeTooHigh","type":"error"},{"inputs":[],"name":"TiersAlreadyInitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldCreator","type":"address"},{"indexed":false,"internalType":"address","name":"newCreator","type":"address"}],"name":"CreatorAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFeesBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFeesBps","type":"uint256"}],"name":"CreatorFeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"ExemptAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"PoolOrRouterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldProtocol","type":"address"},{"indexed":false,"internalType":"address","name":"newProtocol","type":"address"}],"name":"ProtocolAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFeesBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFeesBps","type":"uint256"}],"name":"ProtocolFeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldTreasury","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"addExempt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"addPoolOrRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tierId","type":"uint256"}],"name":"getTier","outputs":[{"components":[{"internalType":"string","name":"baseURL","type":"string"},{"internalType":"uint256","name":"lowerId","type":"uint256"},{"internalType":"uint256","name":"upperId","type":"uint256"},{"internalType":"uint256","name":"amountThreshold","type":"uint256"},{"internalType":"bool","name":"isFungible","type":"bool"},{"internalType":"address","name":"nft","type":"address"},{"internalType":"uint256","name":"nextUnmintedId","type":"uint256"},{"components":[{"internalType":"uint256","name":"spacer","type":"uint256"}],"internalType":"struct MEME404.Uint32Map","name":"burnIds","type":"tuple"},{"internalType":"uint256","name":"burnLength","type":"uint256"},{"internalType":"uint256","name":"tierId","type":"uint256"}],"internalType":"struct MEME404.Tier","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolAddr","type":"address"},{"internalType":"address","name":"_protocolTreasury","type":"address"},{"internalType":"uint256","name":"_protocolFeesBps","type":"uint256"},{"internalType":"uint256","name":"_creatorFeesBps","type":"uint256"},{"internalType":"address","name":"_pool","type":"address"},{"internalType":"address[]","name":"_routers","type":"address[]"},{"internalType":"address[]","name":"_exemptsAddr","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"baseURL","type":"string"},{"internalType":"string","name":"nftName","type":"string"},{"internalType":"string","name":"nftSymbol","type":"string"},{"internalType":"uint256","name":"amountThreshold","type":"uint256"},{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint256","name":"lowerId","type":"uint256"},{"internalType":"uint256","name":"upperId","type":"uint256"},{"internalType":"bool","name":"isFungible","type":"bool"}],"internalType":"struct IMEME404.TierCreateParam[]","name":"_tierParams","type":"tuple[]"},{"internalType":"address[]","name":"_exempt","type":"address[]"}],"name":"initializeTiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isPoolOrRouter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftIdToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_creator","type":"address"}],"name":"setCreatorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFeeBps","type":"uint256"}],"name":"setCreatorFeeBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolAddress","type":"address"}],"name":"setProtocolAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFeeBps","type":"uint256"}],"name":"setProtocolFeeBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasuryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"nftTokenId","type":"uint256"}],"name":"transferFromNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

60e06040526001600d5534801562000015575f80fd5b5060405162003f7338038062003f7383398101604081905262000038916200035e565b84848484838360125f6200004d848262000484565b5060016200005c838262000484565b5060ff81166080524660a0526200007262000131565b60c052505060068054610100600160a81b0319166101006001600160a01b038581169190910291909117909155600780546001600160a01b03191691851691909117905550620000c282620001cb565b620000cd81620001cb565b620000d830620001cb565b620000e35f620001cb565b620000fb826b204fce5e3e2502611000000062000216565b5050601280546001600160a01b0390941661010002610100600160a81b03199094169390931790925550620005f0945050505050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f60405162000163919062000550565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b0381165f818152600b6020526040808220805460ff19166001179055517fdcc4d7bff655001015f308c794375175b9e1616a6ca241b536e472ac47f93f769190a250565b8060025f828254620002299190620005ca565b90915550506001600160a01b0382165f818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112620002a5575f80fd5b81516001600160401b0380821115620002c257620002c262000281565b604051601f8301601f19908116603f01168101908282118183101715620002ed57620002ed62000281565b81604052838152602092508660208588010111156200030a575f80fd5b5f91505b838210156200032d57858201830151818301840152908201906200030e565b5f602085830101528094505050505092915050565b80516001600160a01b038116811462000359575f80fd5b919050565b5f805f805f60a0868803121562000373575f80fd5b85516001600160401b03808211156200038a575f80fd5b6200039889838a0162000295565b96506020880151915080821115620003ae575f80fd5b50620003bd8882890162000295565b945050620003ce6040870162000342565b9250620003de6060870162000342565b9150620003ee6080870162000342565b90509295509295909350565b600181811c908216806200040f57607f821691505b6020821081036200042e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200047f57805f5260205f20601f840160051c810160208510156200045b5750805b601f840160051c820191505b818110156200047c575f815560010162000467565b50505b505050565b81516001600160401b03811115620004a057620004a062000281565b620004b881620004b18454620003fa565b8462000434565b602080601f831160018114620004ee575f8415620004d65750858301515b5f19600386901b1c1916600185901b17855562000548565b5f85815260208120601f198616915b828110156200051e57888601518255948401946001909101908401620004fd565b50858210156200053c57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f8083546200055f81620003fa565b600182811680156200057a57600181146200059057620005be565b60ff1984168752821515830287019450620005be565b875f526020805f205f5b85811015620005b55781548a8201529084019082016200059a565b50505082870194505b50929695505050505050565b80820180821115620005ea57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c0516139586200061b5f395f610a3b01525f610a0601525f61026d01526139585ff3fe608060405234801561000f575f80fd5b5060043610610152575f3560e01c806302d05d3f1461015657806306fdde0314610184578063095ea7b3146101995780630ee7642d146101bc578063129763c2146101e757806318160ddd146101fa5780631ac5f606146102115780631cdd35c91461022657806323b872dd1461023957806324a9d8531461024c57806329a771f314610255578063313ce567146102685780633644e515146102a15780634f062c5a146102a957806358e47004146102c95780636605bfda146102dc5780636da2d116146102ef57806370a082311461031757806372a4ff75146103365780637ecebe00146103495780639127cc691461036857806395d89b411461037b5780639a195a1314610383578063a9059cbb14610396578063ad5dff73146103a9578063c0417e58146103d4578063d505accf146103e7578063dd62ed3e146103fa575b5f80fd5b60065461016e9061010090046001600160a01b031681565b60405161017b9190612ec3565b60405180910390f35b61018c610424565b60405161017b9190612f24565b6101ac6101a7366004612f4a565b6104af565b604051901515815260200161017b565b6101ac6101ca366004612f74565b6001600160a01b03165f908152600c602052604090205460ff1690565b6101ac6101f5366004612f8f565b610508565b61020360025481565b60405190815260200161017b565b61022461021f366004612fcd565b610785565b005b610224610234366004612f74565b610818565b6101ac610247366004612f8f565b610854565b61020360095481565b61022461026336600461302b565b6108cc565b61028f7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161017b565b610203610a03565b6102bc6102b7366004612fcd565b610a5d565b60405161017b91906130df565b6102246102d7366004612f74565b610b94565b6102246102ea366004612f74565b610c4f565b61016e6102fd366004612fcd565b600e6020525f90815260409020546001600160a01b031681565b610203610325366004612f74565b60036020525f908152604090205481565b610224610344366004613304565b610d0a565b610203610357366004612f74565b60056020525f908152604090205481565b610224610376366004612f74565b6115f2565b61018c611698565b610224610391366004612f74565b6116a5565b6101ac6103a4366004612f4a565b6116de565b6101ac6103b7366004612f74565b6001600160a01b03165f908152600b602052604090205460ff1690565b6102246103e2366004612fcd565b611754565b6102246103f5366004613487565b6117e2565b6102036104083660046134f8565b600460209081525f928352604080842090915290825290205481565b5f80546104309061352f565b80601f016020809104026020016040519081016040528092919081815260200182805461045c9061352f565b80156104a75780601f1061047e576101008083540402835291602001916104a7565b820191905f5260205f20905b81548152906001019060200180831161048a57829003601f168201915b505050505081565b335f8181526004602090815260408083206001600160a01b038716808552925280832085905551919290915f80516020613903833981519152906104f69086815260200190565b60405180910390a35060015b92915050565b5f806105143384611a0c565b60a08101519091506001600160a01b03166105425760405163043d000960e51b815260040160405180910390fd5b5f61054c85611c3b565b60608301516001600160a01b0388165f9081526003602052604081208054939450919261057a90849061357b565b909155505060608201516001600160a01b0386165f90815260036020526040812080549092019091556105ac87611c3b565b90505f6105b887611c3b565b90506105c48883611e68565b80515f1280156105da5750805161012085015114155b156106e557836080015115610650578360a001516001600160a01b031663f5298aca888860016040518463ffffffff1660e01b815260040161061e9392919061358e565b5f604051808303815f87803b158015610635575f80fd5b505af1158015610647573d5f803e3d5ffd5b505050506106e5565b60a0840151604051630852cd8d60e31b8152600481018890526001600160a01b03909116906342966c68906024015f604051808303815f87803b158015610695575f80fd5b505af11580156106a7573d5f803e3d5ffd5b5050506101208501515f908152600f602052604081206007810180549193506106e39260068501926106d8906135af565b918290555089612271565b505b82518151146107375782604001516001600160a01b03168460a001516001600160a01b0316036107215761071c878483600161229c565b61072d565b61072d8784835f61229c565b6107378782611e68565b866001600160a01b0316886001600160a01b03165f805160206138e3833981519152866060015160405161076d91815260200190565b60405180910390a360019450505050505b9392505050565b60065461010090046001600160a01b031633146107b5576040516308f78f9960e31b815260040160405180910390fd5b60508111156107d757604051632a52987760e21b815260040160405180910390fd5b60095460408051918252602082018390527f0a8167de874bdafbcdb7afb1989f0a655113928c1f558cfcd8062e4590929db6910160405180910390a1600955565b60065461010090046001600160a01b03163314610848576040516308f78f9960e31b815260040160405180910390fd5b610851816125d4565b50565b5f8061085f85611c3b565b90505f61086b85611c3b565b90505f61087987878761261f565b90505f61088588611c3b565b90505f61089188611c3b565b905061089f8986845f61229c565b6108ab8885835f61229c565b6108b58983611e68565b6108bf8882611e68565b5090979650505050505050565b6007546001600160a01b031633146108f75760405163d35a97ab60e01b815260040160405180910390fd5b60065460ff161561091a5760405162dc149f60e41b815260040160405180910390fd5b6006805460ff191660011790556009869055600a879055600880546001600160a01b038a166001600160a01b03199091168117909155610959906125d4565b610962896125d4565b5f5b838110156109a35761099b858583818110610981576109816135c7565b90506020020160208101906109969190612f74565b61265b565b600101610964565b506109ad8561265b565b5f5b818110156109ee576109e68383838181106109cc576109cc6135c7565b90506020020160208101906109e19190612f74565b6125d4565b6001016109af565b506109f889610b94565b505050505050505050565b5f7f00000000000000000000000000000000000000000000000000000000000000004614610a3857610a336126a6565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b610a65612e5a565b5f828152600f60205260409081902081516101408101909252805482908290610a8d9061352f565b80601f0160208091040260200160405190810160405280929190818152602001828054610ab99061352f565b8015610b045780601f10610adb57610100808354040283529160200191610b04565b820191905f5260205f20905b815481529060010190602001808311610ae757829003601f168201915b50505091835250506001820154602080830191909152600283015460408084019190915260038401546060840152600484015460ff8116151560808501526001600160a01b03610100918290041660a0850152600585015460c085015281519283019091526006840154825260e08301919091526007830154908201526008909101546101209091015292915050565b6007546001600160a01b03163314610bbf5760405163d35a97ab60e01b815260040160405180910390fd5b6001600160a01b038116610be657604051639fabe1c160e01b815260040160405180910390fd5b6007546040517f2756814479f687d805be12ef7dfd27c72bc645462236412ee56a627f389e4e3391610c25916001600160a01b039091169084906135db565b60405180910390a1600780546001600160a01b0319166001600160a01b0392909216919091179055565b6007546001600160a01b03163314610c7a5760405163d35a97ab60e01b815260040160405180910390fd5b6001600160a01b038116610ca157604051639fabe1c160e01b815260040160405180910390fd5b6008546040517f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a91610ce0916001600160a01b039091169084906135db565b60405180910390a1600880546001600160a01b0319166001600160a01b0392909216919091179055565b60125460ff1615610d2e57604051634a0dba0760e11b815260040160405180910390fd5b6012805460ff1916600117905581515f03610d5c57604051631e8e92af60e21b815260040160405180910390fd5b600982511115610d7f5760405163335b59ab60e11b815260040160405180910390fd5b5f5b825181101561158e57828181518110610d9c57610d9c6135c7565b602002602001015160a001515f03610dc75760405163111bcb0160e01b815260040160405180910390fd5b828181518110610dd957610dd96135c7565b6020026020010151606001515f1480610e105750600254838281518110610e0257610e026135c7565b602002602001015160600151115b15610e2e57604051632324d1df60e01b815260040160405180910390fd5b63ffffffff8016838281518110610e4757610e476135c7565b602002602001015160c0015110610e71576040516376b5325960e11b815260040160405180910390fd5b828181518110610e8357610e836135c7565b602002602001015160e0015115610ef357828181518110610ea657610ea66135c7565b602002602001015160c00151838281518110610ec457610ec46135c7565b602002602001015160a0015114610eee576040516301304d1b60e41b815260040160405180910390fd5b610ff5565b828181518110610f0557610f056135c7565b602002602001015160c00151838281518110610f2357610f236135c7565b602002602001015160a0015110610f4d57604051636eebf03560e01b815260040160405180910390fd5b5f610f80848381518110610f6357610f636135c7565b60200260200101516060015160025461273e90919063ffffffff16565b905080848381518110610f9557610f956135c7565b602002602001015160a00151858481518110610fb357610fb36135c7565b602002602001015160c00151610fc9919061357b565b610fd49060016135f5565b1015610ff35760405163e1ace8ad60e01b815260040160405180910390fd5b505b5f600e5f85848151811061100b5761100b6135c7565b60200260200101516080015181526020019081526020015f205f9054906101000a90046001600160a01b031690505f604051806101400160405280868581518110611058576110586135c7565b60200260200101515f0151815260200186858151811061107a5761107a6135c7565b602002602001015160a00151815260200186858151811061109d5761109d6135c7565b602002602001015160c0015181526020018685815181106110c0576110c06135c7565b60200260200101516060015181526020018685815181106110e3576110e36135c7565b602002602001015160e00151151581526020015f6001600160a01b03168152602001868581518110611117576111176135c7565b602002602001015160e0015161114a57868581518110611139576111396135c7565b602002602001015160a0015161114c565b5f5b815260200160405180602001604052805f81525081526020015f815260200184600161117891906135f5565b90529050821561146c575f838152600f6020526040808220815161014081019092528054829082906111a99061352f565b80601f01602080910402602001604051908101604052809291908181526020018280546111d59061352f565b80156112205780601f106111f757610100808354040283529160200191611220565b820191905f5260205f20905b81548152906001019060200180831161120357829003601f168201915b50505091835250506001820154602080830191909152600283015460408084019190915260038401546060840152600484015460ff8116151560808501526001600160a01b03610100918290041660a0850152600585015460c085015281519283019091526006840154825260e08301919091526007830154908201526008909101546101209091015286519091508690859081106112c1576112c16135c7565b602002602001015160800151866001866112db919061357b565b815181106112eb576112eb6135c7565b602002602001015160800151111561131657604051638080c2ed60e01b815260040160405180910390fd5b858481518110611328576113286135c7565b60200260200101516080015186600186611342919061357b565b81518110611352576113526135c7565b6020026020010151608001511480156113b55750858481518110611378576113786135c7565b602002602001015160e00151151586600186611394919061357b565b815181106113a4576113a46135c7565b602002602001015160e00151151514155b156113d357604051638080c2ed60e01b815260040160405180910390fd5b81606001518160600151106113fb57604051632324d1df60e01b815260040160405180910390fd5b806080015115801561140e575081608001515b1561142c57604051632b76654560e11b815260040160405180910390fd5b6001600160a01b0383161580159061144c57508160200151816040015110155b1561146a57604051636eebf03560e01b815260040160405180910390fd5b505b6001600160a01b0382166114b7576114b2600660019054906101000a90046001600160a01b03168685815181106114a5576114a56135c7565b6020026020010151612743565b6114b9565b815b6001600160a01b031660a082015280600f5f6114d68660016135f5565b815260208101919091526040015f20815181906114f39082613654565b5060208201516001828101919091556040830151600283015560608301516003830155608083015160048301805460a08601516001600160a81b0319909116921515610100600160a81b031916929092176101006001600160a01b03909316830217905560c0840151600584015560e0840151516006840155830151600783015561012090920151600890910155929092019150610d819050565b505f5b81518110156115ea57600160105f8484815181106115b1576115b16135c7565b6020908102919091018101516001600160a01b031682528101919091526040015f20805460ff1916911515919091179055600101611591565b505051601155565b60065461010090046001600160a01b03163314611622576040516308f78f9960e31b815260040160405180910390fd5b7f4c1d69ffe6fad068e437c6e17f4068f125ab0b7e50bec2d7f4519d7ab1ee504f600660019054906101000a90046001600160a01b0316826040516116689291906135db565b60405180910390a1600680546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600180546104309061352f565b60065461010090046001600160a01b031633146116d5576040516308f78f9960e31b815260040160405180910390fd5b6108518161265b565b5f806116e933611c3b565b90505f6116f585611c3b565b90505f61170286866128e1565b90505f61170e33611c3b565b90505f61171a88611c3b565b90506117283386845f61229c565b6117348885835f61229c565b61173e3383611e68565b6117488882611e68565b50909695505050505050565b6007546001600160a01b0316331461177f5760405163d35a97ab60e01b815260040160405180910390fd5b60328111156117a15760405163499fddb160e01b815260040160405180910390fd5b600a5460408051918252602082018390527fe9c032fb7419b343f83add64d4513ecbd7602547133916538d3f3f12a061a30e910160405180910390a1600a55565b428410156118315760405162461bcd60e51b815260206004820152601760248201527614115493525517d11150511312539157d1561412549151604a1b60448201526064015b60405180910390fd5b5f600161183c610a03565b6001600160a01b038a81165f8181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611944573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381161580159061197a5750876001600160a01b0316816001600160a01b0316145b6119b75760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401611828565b6001600160a01b039081165f9081526004602090815260408083208a8516808552908352928190208990555188815291928a16915f80516020613903833981519152910160405180910390a350505050505050565b611a14612e5a565b60015b6011548111611bc5575f818152600f602052604080822081516101408101909252805482908290611a479061352f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a739061352f565b8015611abe5780601f10611a9557610100808354040283529160200191611abe565b820191905f5260205f20905b815481529060010190602001808311611aa157829003601f168201915b50505091835250506001820154602080830191909152600283015460408084019190915260038401546060840152600484015460ff8116151560808501526001600160a01b0361010091829004811660a080870191909152600587015460c087015283519485019093526006860154845260e0850193909352600785015490840152600890930154610120909201919091529082015191925090811690861614611b685750611bb3565b806080015115611b8b5783816020015103611b865791506105029050565b611bb1565b80602001518410158015611ba3575080604001518411155b15611bb15791506105029050565b505b80611bbd816135af565b915050611a17565b5060405180610140016040528060405180602001604052805f81525081526020015f81526020015f81526020015f81526020015f151581526020015f6001600160a01b031681526020015f815260200160405180602001604052805f81525081526020015f81526020015f815250905092915050565b611c6b60405180608001604052805f81526020015f81526020015f6001600160a01b031681526020015f81525090565b6001600160a01b03821615611e36576001600160a01b0382165f908152600360205260409020546011545b8015611e33575f818152600f60205260409020600301548210611e2157604080516080810182525f838152600f60208181528483206008810154855260038101548804828601526004015461010081046001600160a01b03169585019590955291859052905290606082019060ff16611d90575f838152600f60205260409081902060049081015491516370a0823160e01b81526101009092046001600160a01b0316916370a0823191611d4c918a9101612ec3565b602060405180830381865afa158015611d67573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d8b9190613713565b611e17565b5f838152600f6020526040908190206004808201546001909201549251627eeac760e11b81526101009092046001600160a01b03169262fdd58e92611dd8928b92910161372a565b602060405180830381865afa158015611df3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e179190613713565b9052949350505050565b80611e2b81613743565b915050611c96565b50505b60405180608001604052805f1981526020015f81526020015f6001600160a01b031681526020015f8152509050919050565b600d54600114611e8a5760405162461bcd60e51b815260040161182890613758565b6002600d5580515f1380611ea557506001600160a01b038216155b80611ec757506001600160a01b0382165f9081526010602052604090205460ff165b6122685780515f908152600f60205260409020600481015460ff1615612021576001600160a01b0383163b15801590611f0f5750611f0d83335f84600101546001612912565b155b15611f1a5750612268565b600481810154600180840154604051627eeac760e11b81526101009093046001600160a01b0316939192849262fdd58e92611f57928a920161372a565b602060405180830381865afa158015611f72573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f969190613713565b10611fa2575050612268565b60018281015460405163731133e960e01b81526001600160a01b03878116600483015260248201929092526044810192909252608060648301525f608483015282169063731133e99060a4015f604051808303815f87803b158015612005575f80fd5b505af1158015612017573d5f803e3d5ffd5b5050505050612266565b6001600160a01b0383163b15801590612056575061205483335f846001015460405180602001604052805f815250612a27565b155b156120615750612268565b6004818101546040516370a0823160e01b81526101009091046001600160a01b0316915f9183916370a082319161209a91899101612ec3565b602060405180830381865afa1580156120b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120d99190613713565b8460200151116120e9575f612163565b6040516370a0823160e01b81526001600160a01b038316906370a0823190612115908890600401612ec3565b602060405180830381865afa158015612130573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121549190613713565b8460200151612163919061357b565b90505f5b81811015612262575f846002015485600501541161219b57600585018054905f612190836135af565b9190505590506121fa565b84600701545f036121b0575050505050612268565b6007850180546121f1916006880191905f6121ca83613743565b919050555f8160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1690505b6040516340c10f1960e01b81526001600160a01b038516906340c10f1990612228908a90859060040161372a565b5f604051808303815f87803b15801561223f575f80fd5b505af1158015612251573d5f803e3d5ffd5b505060019093019250612167915050565b5050505b505b50506001600d55565b8160031c8360601b016007831660051b815480821c841863ffffffff16821b81188355505050505050565b600d546001146122be5760405162461bcd60e51b815260040161182890613758565b6002600d5582515f13806122d957506001600160a01b038416155b6125c95782515f908152600f60205260409020600481015460ff1615612457576004818101546001830154604051627eeac760e11b81526101009092046001600160a01b031692839262fdd58e92612333928b920161372a565b602060405180830381865afa15801561234e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123729190613713565b158061237f575083518551145b1561238b5750506125c9565b6001820154604051627eeac760e11b81526001600160a01b0383169163f5298aca91899190849062fdd58e906123c7908590859060040161372a565b602060405180830381865afa1580156123e2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124069190613713565b6040518463ffffffff1660e01b81526004016124249392919061358e565b5f604051808303815f87803b15801561243b575f80fd5b505af115801561244d573d5f803e3d5ffd5b50505050506125c7565b6004810154835185516101009092046001600160a01b0316915f9190036124a657846020015185606001511161248d575f6124c7565b846020015185606001516124a1919061357b565b6124c7565b838660600151116124b7575f6124c7565b8386606001516124c7919061357b565b90505f5b818110156125c35760405163096a643360e11b81525f906001600160a01b038516906312d4c86690612501908c90600401612ec3565b602060405180830381865afa15801561251c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125409190613713565b604051630852cd8d60e31b8152600481018290529091506001600160a01b038516906342966c68906024015f604051808303815f87803b158015612582575f80fd5b505af1158015612594573d5f803e3d5ffd5b505050506125ba85600601866007015f81546125af906135af565b918290555083612271565b506001016124cb565b5050505b505b50506001600d555050565b6001600160a01b0381165f818152600b6020526040808220805460ff19166001179055517fdcc4d7bff655001015f308c794375175b9e1616a6ca241b536e472ac47f93f769190a250565b5f815f036126395761263284845f612b22565b905061077e565b6126468484846001612bfc565b9150612653848484612b22565b949350505050565b6001600160a01b0381165f818152600c6020526040808220805460ff19166001179055517f13d186064a60bb5c6c906315fa4b26bc5107ed969f69895c70fefafbeeb591759190a250565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f6040516126d6919061377c565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b900490565b5f8160e001511561280957601254602083015160408085015185519151634ace39d760e01b81525f9461010090046001600160a01b031693634ace39d79361279393919230918b916004016137ee565b6020604051808303815f875af11580156127af573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127d39190613849565b60808401515f908152600e6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055506128c0565b6012546020830151604080850151855191516313e6d20960e11b81525f9461010090046001600160a01b0316936327cda4129361284e93919230918b916004016137ee565b6020604051808303815f875af115801561286a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061288e9190613849565b60808401515f908152600e6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055505b50608001515f908152600e60205260409020546001600160a01b0316919050565b5f815f036128fa576128f3835f612dd3565b9050610502565b6129063384845f612bfc565b915061077e8383612dd3565b604080516001600160a01b0386811660248301528581166044830152606482018590526084820184905260a060a48301525f60c48084018290528451808503909101815260e490930184526020830180516001600160e01b031663f23a6e6160e01b179052925183918291908a169061298c908590613864565b5f604051808303815f865af19150503d805f81146129c5576040519150601f19603f3d011682016040523d82523d5f602084013e6129ca565b606091505b50915091508180156129de57506020815110155b15612a17575f818060200190518101906129f8919061387f565b6001600160e01b03191663f23a6e6160e01b149450612a1e9350505050565b5f93505050505b95945050505050565b5f8063150b7a0260e01b86868686604051602401612a4894939291906138a6565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090505f80886001600160a01b031683604051612a979190613864565b5f604051808303815f865af19150503d805f8114612ad0576040519150601f19603f3d011682016040523d82523d5f602084013e612ad5565b606091505b5091509150818015612ae957506020815110155b15612a17575f81806020019051810190612b03919061387f565b6001600160e01b031916630a85bd0160e11b149450612a1e9350505050565b6001600160a01b0383165f9081526004602090815260408083203384529091528120545f198114612b7b57612b57838261357b565b6001600160a01b0386165f9081526004602090815260408083203384529091529020555b6001600160a01b0385165f9081526003602052604081208054859290612ba290849061357b565b90915550506001600160a01b038085165f81815260036020526040908190208054870190555190918716905f805160206138e383398151915290612be99087815260200190565b60405180910390a3506001949350505050565b6006545f9060ff16158015612c1c57506007546001600160a01b03163314155b15612c3a5760405163486aa30760e01b815260040160405180910390fd5b6001600160a01b0385165f908152600c602052604081205460ff168015612c7857506001600160a01b0385165f908152600c602052604090205460ff165b6001600160a01b0387165f908152600b602052604090205490915060ff16158015612cbb57506001600160a01b0385165f908152600b602052604090205460ff16155b8015612cc5575080155b8015612ce857506001600160a01b0386165f908152600c602052604090205460ff165b15612dc9576009545f90612d00908690612710612e36565b90505f612d1c600a5461271088612e369092919063ffffffff16565b905080612d29838861357b565b612d33919061357b565b95508415612d85578115612d6057600654612d5e90899061010090046001600160a01b031684612b22565b505b8015612d8057600854612d7e9089906001600160a01b031683612b22565b505b612dc6565b8115612da857600654612da69061010090046001600160a01b031683612dd3565b505b8015612dc657600854612dc4906001600160a01b031682612dd3565b505b50505b5091949350505050565b335f90815260036020526040812080548391908390612df390849061357b565b90915550506001600160a01b0383165f81815260036020526040908190208054850190555133905f805160206138e3833981519152906104f69086815260200190565b5f825f190484118302158202612e535763ad251c275f526004601cfd5b5091020490565b604051806101400160405280606081526020015f81526020015f81526020015f81526020015f151581526020015f6001600160a01b031681526020015f8152602001612eb160405180602001604052805f81525090565b81526020015f81526020015f81525090565b6001600160a01b0391909116815260200190565b5f5b83811015612ef1578181015183820152602001612ed9565b50505f910152565b5f8151808452612f10816020860160208601612ed7565b601f01601f19169290920160200192915050565b602081525f61077e6020830184612ef9565b6001600160a01b0381168114610851575f80fd5b5f8060408385031215612f5b575f80fd5b8235612f6681612f36565b946020939093013593505050565b5f60208284031215612f84575f80fd5b813561077e81612f36565b5f805f60608486031215612fa1575f80fd5b8335612fac81612f36565b92506020840135612fbc81612f36565b929592945050506040919091013590565b5f60208284031215612fdd575f80fd5b5035919050565b5f8083601f840112612ff4575f80fd5b5081356001600160401b0381111561300a575f80fd5b6020830191508360208260051b8501011115613024575f80fd5b9250929050565b5f805f805f805f805f60e08a8c031215613043575f80fd5b893561304e81612f36565b985060208a013561305e81612f36565b975060408a0135965060608a0135955060808a013561307c81612f36565b945060a08a01356001600160401b0380821115613097575f80fd5b6130a38d838e01612fe4565b909650945060c08c01359150808211156130bb575f80fd5b506130c88c828d01612fe4565b915080935050809150509295985092959850929598565b602081525f82516101408060208501526130fd610160850183612ef9565b9150602085015160408501526040850151606085015260608501516080850152608085015161313060a086018215159052565b5060a08501516001600160a01b03811660c08601525060c085015160e085015260e085015161010061316481870183519052565b860151610120868101919091529095015193019290925250919050565b634e487b7160e01b5f52604160045260245ffd5b60405161010081016001600160401b03811182821017156131b8576131b8613181565b60405290565b604051601f8201601f191681016001600160401b03811182821017156131e6576131e6613181565b604052919050565b5f6001600160401b0382111561320657613206613181565b5060051b60200190565b5f82601f83011261321f575f80fd5b81356001600160401b0381111561323857613238613181565b61324b601f8201601f19166020016131be565b81815284602083860101111561325f575f80fd5b816020850160208301375f918101602001919091529392505050565b8035801515811461328a575f80fd5b919050565b5f82601f83011261329e575f80fd5b813560206132b36132ae836131ee565b6131be565b8083825260208201915060208460051b8701019350868411156132d4575f80fd5b602086015b848110156132f95780356132ec81612f36565b83529183019183016132d9565b509695505050505050565b5f8060408385031215613315575f80fd5b82356001600160401b038082111561332b575f80fd5b818501915085601f83011261333e575f80fd5b8135602061334e6132ae836131ee565b82815260059290921b8401810191818101908984111561336c575f80fd5b8286015b8481101561345a57803586811115613386575f80fd5b8701610100818d03601f190181131561339d575f80fd5b6133a5613195565b86830135898111156133b5575f80fd5b6133c38f8983870101613210565b8252506040830135898111156133d7575f80fd5b6133e58f8983870101613210565b88830152506060830135898111156133fb575f80fd5b6134098f8983870101613210565b604083015250608080840135606083015260a0808501358284015260c0915081850135818401525060e0808501358284015261344684860161327b565b908301525085525050918301918301613370565b5096505086013592505080821115613470575f80fd5b5061347d8582860161328f565b9150509250929050565b5f805f805f805f60e0888a03121561349d575f80fd5b87356134a881612f36565b965060208801356134b881612f36565b95506040880135945060608801359350608088013560ff811681146134db575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f8060408385031215613509575f80fd5b823561351481612f36565b9150602083013561352481612f36565b809150509250929050565b600181811c9082168061354357607f821691505b60208210810361356157634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561050257610502613567565b6001600160a01b039390931683526020830191909152604082015260600190565b5f600182016135c0576135c0613567565b5060010190565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b0392831681529116602082015260400190565b8082018082111561050257610502613567565b601f82111561364f57805f5260205f20601f840160051c8101602085101561362d5750805b601f840160051c820191505b8181101561364c575f8155600101613639565b50505b505050565b81516001600160401b0381111561366d5761366d613181565b6136818161367b845461352f565b84613608565b602080601f8311600181146136b4575f841561369d5750858301515b5f19600386901b1c1916600185901b17855561370b565b5f85815260208120601f198616915b828110156136e2578886015182559484019460019091019084016136c3565b50858210156136ff57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f60208284031215613723575f80fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b5f8161375157613751613567565b505f190190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b5f8083546137898161352f565b600182811680156137a157600181146137b6576137e2565b60ff19841687528215158302870194506137e2565b875f526020805f205f5b858110156137d95781548a8201529084019082016137c0565b50505082870194505b50929695505050505050565b60a081525f61380060a0830188612ef9565b82810360208401526138128188612ef9565b6001600160a01b038781166040860152861660608501528381036080850152905061383d8185612ef9565b98975050505050505050565b5f60208284031215613859575f80fd5b815161077e81612f36565b5f8251613875818460208701612ed7565b9190910192915050565b5f6020828403121561388f575f80fd5b81516001600160e01b03198116811461077e575f80fd5b6001600160a01b03858116825284166020820152604081018390526080606082018190525f906138d890830184612ef9565b969550505050505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a2646970667358221220e3e24853e7ab6bf4d6dabdbfe36d92d3ea1b25aca81257d06213cfa3e7beb70f64736f6c6343000817003300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000002cd3c02a734559472d91b285b544202a3c8b129e000000000000000000000000a2aace327172f303ab431455469cd65f3b5b63510000000000000000000000009401ae767955581e3ad0910d0e3651a2fcb37301000000000000000000000000000000000000000000000000000000000000001d554e434841494e454420454c455048414e5453203430342024454c695a0000000000000000000000000000000000000000000000000000000000000000000004454c495a00000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610152575f3560e01c806302d05d3f1461015657806306fdde0314610184578063095ea7b3146101995780630ee7642d146101bc578063129763c2146101e757806318160ddd146101fa5780631ac5f606146102115780631cdd35c91461022657806323b872dd1461023957806324a9d8531461024c57806329a771f314610255578063313ce567146102685780633644e515146102a15780634f062c5a146102a957806358e47004146102c95780636605bfda146102dc5780636da2d116146102ef57806370a082311461031757806372a4ff75146103365780637ecebe00146103495780639127cc691461036857806395d89b411461037b5780639a195a1314610383578063a9059cbb14610396578063ad5dff73146103a9578063c0417e58146103d4578063d505accf146103e7578063dd62ed3e146103fa575b5f80fd5b60065461016e9061010090046001600160a01b031681565b60405161017b9190612ec3565b60405180910390f35b61018c610424565b60405161017b9190612f24565b6101ac6101a7366004612f4a565b6104af565b604051901515815260200161017b565b6101ac6101ca366004612f74565b6001600160a01b03165f908152600c602052604090205460ff1690565b6101ac6101f5366004612f8f565b610508565b61020360025481565b60405190815260200161017b565b61022461021f366004612fcd565b610785565b005b610224610234366004612f74565b610818565b6101ac610247366004612f8f565b610854565b61020360095481565b61022461026336600461302b565b6108cc565b61028f7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff909116815260200161017b565b610203610a03565b6102bc6102b7366004612fcd565b610a5d565b60405161017b91906130df565b6102246102d7366004612f74565b610b94565b6102246102ea366004612f74565b610c4f565b61016e6102fd366004612fcd565b600e6020525f90815260409020546001600160a01b031681565b610203610325366004612f74565b60036020525f908152604090205481565b610224610344366004613304565b610d0a565b610203610357366004612f74565b60056020525f908152604090205481565b610224610376366004612f74565b6115f2565b61018c611698565b610224610391366004612f74565b6116a5565b6101ac6103a4366004612f4a565b6116de565b6101ac6103b7366004612f74565b6001600160a01b03165f908152600b602052604090205460ff1690565b6102246103e2366004612fcd565b611754565b6102246103f5366004613487565b6117e2565b6102036104083660046134f8565b600460209081525f928352604080842090915290825290205481565b5f80546104309061352f565b80601f016020809104026020016040519081016040528092919081815260200182805461045c9061352f565b80156104a75780601f1061047e576101008083540402835291602001916104a7565b820191905f5260205f20905b81548152906001019060200180831161048a57829003601f168201915b505050505081565b335f8181526004602090815260408083206001600160a01b038716808552925280832085905551919290915f80516020613903833981519152906104f69086815260200190565b60405180910390a35060015b92915050565b5f806105143384611a0c565b60a08101519091506001600160a01b03166105425760405163043d000960e51b815260040160405180910390fd5b5f61054c85611c3b565b60608301516001600160a01b0388165f9081526003602052604081208054939450919261057a90849061357b565b909155505060608201516001600160a01b0386165f90815260036020526040812080549092019091556105ac87611c3b565b90505f6105b887611c3b565b90506105c48883611e68565b80515f1280156105da5750805161012085015114155b156106e557836080015115610650578360a001516001600160a01b031663f5298aca888860016040518463ffffffff1660e01b815260040161061e9392919061358e565b5f604051808303815f87803b158015610635575f80fd5b505af1158015610647573d5f803e3d5ffd5b505050506106e5565b60a0840151604051630852cd8d60e31b8152600481018890526001600160a01b03909116906342966c68906024015f604051808303815f87803b158015610695575f80fd5b505af11580156106a7573d5f803e3d5ffd5b5050506101208501515f908152600f602052604081206007810180549193506106e39260068501926106d8906135af565b918290555089612271565b505b82518151146107375782604001516001600160a01b03168460a001516001600160a01b0316036107215761071c878483600161229c565b61072d565b61072d8784835f61229c565b6107378782611e68565b866001600160a01b0316886001600160a01b03165f805160206138e3833981519152866060015160405161076d91815260200190565b60405180910390a360019450505050505b9392505050565b60065461010090046001600160a01b031633146107b5576040516308f78f9960e31b815260040160405180910390fd5b60508111156107d757604051632a52987760e21b815260040160405180910390fd5b60095460408051918252602082018390527f0a8167de874bdafbcdb7afb1989f0a655113928c1f558cfcd8062e4590929db6910160405180910390a1600955565b60065461010090046001600160a01b03163314610848576040516308f78f9960e31b815260040160405180910390fd5b610851816125d4565b50565b5f8061085f85611c3b565b90505f61086b85611c3b565b90505f61087987878761261f565b90505f61088588611c3b565b90505f61089188611c3b565b905061089f8986845f61229c565b6108ab8885835f61229c565b6108b58983611e68565b6108bf8882611e68565b5090979650505050505050565b6007546001600160a01b031633146108f75760405163d35a97ab60e01b815260040160405180910390fd5b60065460ff161561091a5760405162dc149f60e41b815260040160405180910390fd5b6006805460ff191660011790556009869055600a879055600880546001600160a01b038a166001600160a01b03199091168117909155610959906125d4565b610962896125d4565b5f5b838110156109a35761099b858583818110610981576109816135c7565b90506020020160208101906109969190612f74565b61265b565b600101610964565b506109ad8561265b565b5f5b818110156109ee576109e68383838181106109cc576109cc6135c7565b90506020020160208101906109e19190612f74565b6125d4565b6001016109af565b506109f889610b94565b505050505050505050565b5f7f00000000000000000000000000000000000000000000000000000000000021054614610a3857610a336126a6565b905090565b507f2f1e7a2bbe02d952b525dfe2ed55bb54ff644817c2125b02f206d7312013e43e90565b610a65612e5a565b5f828152600f60205260409081902081516101408101909252805482908290610a8d9061352f565b80601f0160208091040260200160405190810160405280929190818152602001828054610ab99061352f565b8015610b045780601f10610adb57610100808354040283529160200191610b04565b820191905f5260205f20905b815481529060010190602001808311610ae757829003601f168201915b50505091835250506001820154602080830191909152600283015460408084019190915260038401546060840152600484015460ff8116151560808501526001600160a01b03610100918290041660a0850152600585015460c085015281519283019091526006840154825260e08301919091526007830154908201526008909101546101209091015292915050565b6007546001600160a01b03163314610bbf5760405163d35a97ab60e01b815260040160405180910390fd5b6001600160a01b038116610be657604051639fabe1c160e01b815260040160405180910390fd5b6007546040517f2756814479f687d805be12ef7dfd27c72bc645462236412ee56a627f389e4e3391610c25916001600160a01b039091169084906135db565b60405180910390a1600780546001600160a01b0319166001600160a01b0392909216919091179055565b6007546001600160a01b03163314610c7a5760405163d35a97ab60e01b815260040160405180910390fd5b6001600160a01b038116610ca157604051639fabe1c160e01b815260040160405180910390fd5b6008546040517f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a91610ce0916001600160a01b039091169084906135db565b60405180910390a1600880546001600160a01b0319166001600160a01b0392909216919091179055565b60125460ff1615610d2e57604051634a0dba0760e11b815260040160405180910390fd5b6012805460ff1916600117905581515f03610d5c57604051631e8e92af60e21b815260040160405180910390fd5b600982511115610d7f5760405163335b59ab60e11b815260040160405180910390fd5b5f5b825181101561158e57828181518110610d9c57610d9c6135c7565b602002602001015160a001515f03610dc75760405163111bcb0160e01b815260040160405180910390fd5b828181518110610dd957610dd96135c7565b6020026020010151606001515f1480610e105750600254838281518110610e0257610e026135c7565b602002602001015160600151115b15610e2e57604051632324d1df60e01b815260040160405180910390fd5b63ffffffff8016838281518110610e4757610e476135c7565b602002602001015160c0015110610e71576040516376b5325960e11b815260040160405180910390fd5b828181518110610e8357610e836135c7565b602002602001015160e0015115610ef357828181518110610ea657610ea66135c7565b602002602001015160c00151838281518110610ec457610ec46135c7565b602002602001015160a0015114610eee576040516301304d1b60e41b815260040160405180910390fd5b610ff5565b828181518110610f0557610f056135c7565b602002602001015160c00151838281518110610f2357610f236135c7565b602002602001015160a0015110610f4d57604051636eebf03560e01b815260040160405180910390fd5b5f610f80848381518110610f6357610f636135c7565b60200260200101516060015160025461273e90919063ffffffff16565b905080848381518110610f9557610f956135c7565b602002602001015160a00151858481518110610fb357610fb36135c7565b602002602001015160c00151610fc9919061357b565b610fd49060016135f5565b1015610ff35760405163e1ace8ad60e01b815260040160405180910390fd5b505b5f600e5f85848151811061100b5761100b6135c7565b60200260200101516080015181526020019081526020015f205f9054906101000a90046001600160a01b031690505f604051806101400160405280868581518110611058576110586135c7565b60200260200101515f0151815260200186858151811061107a5761107a6135c7565b602002602001015160a00151815260200186858151811061109d5761109d6135c7565b602002602001015160c0015181526020018685815181106110c0576110c06135c7565b60200260200101516060015181526020018685815181106110e3576110e36135c7565b602002602001015160e00151151581526020015f6001600160a01b03168152602001868581518110611117576111176135c7565b602002602001015160e0015161114a57868581518110611139576111396135c7565b602002602001015160a0015161114c565b5f5b815260200160405180602001604052805f81525081526020015f815260200184600161117891906135f5565b90529050821561146c575f838152600f6020526040808220815161014081019092528054829082906111a99061352f565b80601f01602080910402602001604051908101604052809291908181526020018280546111d59061352f565b80156112205780601f106111f757610100808354040283529160200191611220565b820191905f5260205f20905b81548152906001019060200180831161120357829003601f168201915b50505091835250506001820154602080830191909152600283015460408084019190915260038401546060840152600484015460ff8116151560808501526001600160a01b03610100918290041660a0850152600585015460c085015281519283019091526006840154825260e08301919091526007830154908201526008909101546101209091015286519091508690859081106112c1576112c16135c7565b602002602001015160800151866001866112db919061357b565b815181106112eb576112eb6135c7565b602002602001015160800151111561131657604051638080c2ed60e01b815260040160405180910390fd5b858481518110611328576113286135c7565b60200260200101516080015186600186611342919061357b565b81518110611352576113526135c7565b6020026020010151608001511480156113b55750858481518110611378576113786135c7565b602002602001015160e00151151586600186611394919061357b565b815181106113a4576113a46135c7565b602002602001015160e00151151514155b156113d357604051638080c2ed60e01b815260040160405180910390fd5b81606001518160600151106113fb57604051632324d1df60e01b815260040160405180910390fd5b806080015115801561140e575081608001515b1561142c57604051632b76654560e11b815260040160405180910390fd5b6001600160a01b0383161580159061144c57508160200151816040015110155b1561146a57604051636eebf03560e01b815260040160405180910390fd5b505b6001600160a01b0382166114b7576114b2600660019054906101000a90046001600160a01b03168685815181106114a5576114a56135c7565b6020026020010151612743565b6114b9565b815b6001600160a01b031660a082015280600f5f6114d68660016135f5565b815260208101919091526040015f20815181906114f39082613654565b5060208201516001828101919091556040830151600283015560608301516003830155608083015160048301805460a08601516001600160a81b0319909116921515610100600160a81b031916929092176101006001600160a01b03909316830217905560c0840151600584015560e0840151516006840155830151600783015561012090920151600890910155929092019150610d819050565b505f5b81518110156115ea57600160105f8484815181106115b1576115b16135c7565b6020908102919091018101516001600160a01b031682528101919091526040015f20805460ff1916911515919091179055600101611591565b505051601155565b60065461010090046001600160a01b03163314611622576040516308f78f9960e31b815260040160405180910390fd5b7f4c1d69ffe6fad068e437c6e17f4068f125ab0b7e50bec2d7f4519d7ab1ee504f600660019054906101000a90046001600160a01b0316826040516116689291906135db565b60405180910390a1600680546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600180546104309061352f565b60065461010090046001600160a01b031633146116d5576040516308f78f9960e31b815260040160405180910390fd5b6108518161265b565b5f806116e933611c3b565b90505f6116f585611c3b565b90505f61170286866128e1565b90505f61170e33611c3b565b90505f61171a88611c3b565b90506117283386845f61229c565b6117348885835f61229c565b61173e3383611e68565b6117488882611e68565b50909695505050505050565b6007546001600160a01b0316331461177f5760405163d35a97ab60e01b815260040160405180910390fd5b60328111156117a15760405163499fddb160e01b815260040160405180910390fd5b600a5460408051918252602082018390527fe9c032fb7419b343f83add64d4513ecbd7602547133916538d3f3f12a061a30e910160405180910390a1600a55565b428410156118315760405162461bcd60e51b815260206004820152601760248201527614115493525517d11150511312539157d1561412549151604a1b60448201526064015b60405180910390fd5b5f600161183c610a03565b6001600160a01b038a81165f8181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611944573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381161580159061197a5750876001600160a01b0316816001600160a01b0316145b6119b75760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401611828565b6001600160a01b039081165f9081526004602090815260408083208a8516808552908352928190208990555188815291928a16915f80516020613903833981519152910160405180910390a350505050505050565b611a14612e5a565b60015b6011548111611bc5575f818152600f602052604080822081516101408101909252805482908290611a479061352f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a739061352f565b8015611abe5780601f10611a9557610100808354040283529160200191611abe565b820191905f5260205f20905b815481529060010190602001808311611aa157829003601f168201915b50505091835250506001820154602080830191909152600283015460408084019190915260038401546060840152600484015460ff8116151560808501526001600160a01b0361010091829004811660a080870191909152600587015460c087015283519485019093526006860154845260e0850193909352600785015490840152600890930154610120909201919091529082015191925090811690861614611b685750611bb3565b806080015115611b8b5783816020015103611b865791506105029050565b611bb1565b80602001518410158015611ba3575080604001518411155b15611bb15791506105029050565b505b80611bbd816135af565b915050611a17565b5060405180610140016040528060405180602001604052805f81525081526020015f81526020015f81526020015f81526020015f151581526020015f6001600160a01b031681526020015f815260200160405180602001604052805f81525081526020015f81526020015f815250905092915050565b611c6b60405180608001604052805f81526020015f81526020015f6001600160a01b031681526020015f81525090565b6001600160a01b03821615611e36576001600160a01b0382165f908152600360205260409020546011545b8015611e33575f818152600f60205260409020600301548210611e2157604080516080810182525f838152600f60208181528483206008810154855260038101548804828601526004015461010081046001600160a01b03169585019590955291859052905290606082019060ff16611d90575f838152600f60205260409081902060049081015491516370a0823160e01b81526101009092046001600160a01b0316916370a0823191611d4c918a9101612ec3565b602060405180830381865afa158015611d67573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d8b9190613713565b611e17565b5f838152600f6020526040908190206004808201546001909201549251627eeac760e11b81526101009092046001600160a01b03169262fdd58e92611dd8928b92910161372a565b602060405180830381865afa158015611df3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e179190613713565b9052949350505050565b80611e2b81613743565b915050611c96565b50505b60405180608001604052805f1981526020015f81526020015f6001600160a01b031681526020015f8152509050919050565b600d54600114611e8a5760405162461bcd60e51b815260040161182890613758565b6002600d5580515f1380611ea557506001600160a01b038216155b80611ec757506001600160a01b0382165f9081526010602052604090205460ff165b6122685780515f908152600f60205260409020600481015460ff1615612021576001600160a01b0383163b15801590611f0f5750611f0d83335f84600101546001612912565b155b15611f1a5750612268565b600481810154600180840154604051627eeac760e11b81526101009093046001600160a01b0316939192849262fdd58e92611f57928a920161372a565b602060405180830381865afa158015611f72573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f969190613713565b10611fa2575050612268565b60018281015460405163731133e960e01b81526001600160a01b03878116600483015260248201929092526044810192909252608060648301525f608483015282169063731133e99060a4015f604051808303815f87803b158015612005575f80fd5b505af1158015612017573d5f803e3d5ffd5b5050505050612266565b6001600160a01b0383163b15801590612056575061205483335f846001015460405180602001604052805f815250612a27565b155b156120615750612268565b6004818101546040516370a0823160e01b81526101009091046001600160a01b0316915f9183916370a082319161209a91899101612ec3565b602060405180830381865afa1580156120b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120d99190613713565b8460200151116120e9575f612163565b6040516370a0823160e01b81526001600160a01b038316906370a0823190612115908890600401612ec3565b602060405180830381865afa158015612130573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121549190613713565b8460200151612163919061357b565b90505f5b81811015612262575f846002015485600501541161219b57600585018054905f612190836135af565b9190505590506121fa565b84600701545f036121b0575050505050612268565b6007850180546121f1916006880191905f6121ca83613743565b919050555f8160031c8360601b0180546007841660051b1c63ffffffff1691505092915050565b63ffffffff1690505b6040516340c10f1960e01b81526001600160a01b038516906340c10f1990612228908a90859060040161372a565b5f604051808303815f87803b15801561223f575f80fd5b505af1158015612251573d5f803e3d5ffd5b505060019093019250612167915050565b5050505b505b50506001600d55565b8160031c8360601b016007831660051b815480821c841863ffffffff16821b81188355505050505050565b600d546001146122be5760405162461bcd60e51b815260040161182890613758565b6002600d5582515f13806122d957506001600160a01b038416155b6125c95782515f908152600f60205260409020600481015460ff1615612457576004818101546001830154604051627eeac760e11b81526101009092046001600160a01b031692839262fdd58e92612333928b920161372a565b602060405180830381865afa15801561234e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123729190613713565b158061237f575083518551145b1561238b5750506125c9565b6001820154604051627eeac760e11b81526001600160a01b0383169163f5298aca91899190849062fdd58e906123c7908590859060040161372a565b602060405180830381865afa1580156123e2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124069190613713565b6040518463ffffffff1660e01b81526004016124249392919061358e565b5f604051808303815f87803b15801561243b575f80fd5b505af115801561244d573d5f803e3d5ffd5b50505050506125c7565b6004810154835185516101009092046001600160a01b0316915f9190036124a657846020015185606001511161248d575f6124c7565b846020015185606001516124a1919061357b565b6124c7565b838660600151116124b7575f6124c7565b8386606001516124c7919061357b565b90505f5b818110156125c35760405163096a643360e11b81525f906001600160a01b038516906312d4c86690612501908c90600401612ec3565b602060405180830381865afa15801561251c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125409190613713565b604051630852cd8d60e31b8152600481018290529091506001600160a01b038516906342966c68906024015f604051808303815f87803b158015612582575f80fd5b505af1158015612594573d5f803e3d5ffd5b505050506125ba85600601866007015f81546125af906135af565b918290555083612271565b506001016124cb565b5050505b505b50506001600d555050565b6001600160a01b0381165f818152600b6020526040808220805460ff19166001179055517fdcc4d7bff655001015f308c794375175b9e1616a6ca241b536e472ac47f93f769190a250565b5f815f036126395761263284845f612b22565b905061077e565b6126468484846001612bfc565b9150612653848484612b22565b949350505050565b6001600160a01b0381165f818152600c6020526040808220805460ff19166001179055517f13d186064a60bb5c6c906315fa4b26bc5107ed969f69895c70fefafbeeb591759190a250565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f6040516126d6919061377c565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b900490565b5f8160e001511561280957601254602083015160408085015185519151634ace39d760e01b81525f9461010090046001600160a01b031693634ace39d79361279393919230918b916004016137ee565b6020604051808303815f875af11580156127af573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127d39190613849565b60808401515f908152600e6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055506128c0565b6012546020830151604080850151855191516313e6d20960e11b81525f9461010090046001600160a01b0316936327cda4129361284e93919230918b916004016137ee565b6020604051808303815f875af115801561286a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061288e9190613849565b60808401515f908152600e6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055505b50608001515f908152600e60205260409020546001600160a01b0316919050565b5f815f036128fa576128f3835f612dd3565b9050610502565b6129063384845f612bfc565b915061077e8383612dd3565b604080516001600160a01b0386811660248301528581166044830152606482018590526084820184905260a060a48301525f60c48084018290528451808503909101815260e490930184526020830180516001600160e01b031663f23a6e6160e01b179052925183918291908a169061298c908590613864565b5f604051808303815f865af19150503d805f81146129c5576040519150601f19603f3d011682016040523d82523d5f602084013e6129ca565b606091505b50915091508180156129de57506020815110155b15612a17575f818060200190518101906129f8919061387f565b6001600160e01b03191663f23a6e6160e01b149450612a1e9350505050565b5f93505050505b95945050505050565b5f8063150b7a0260e01b86868686604051602401612a4894939291906138a6565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090505f80886001600160a01b031683604051612a979190613864565b5f604051808303815f865af19150503d805f8114612ad0576040519150601f19603f3d011682016040523d82523d5f602084013e612ad5565b606091505b5091509150818015612ae957506020815110155b15612a17575f81806020019051810190612b03919061387f565b6001600160e01b031916630a85bd0160e11b149450612a1e9350505050565b6001600160a01b0383165f9081526004602090815260408083203384529091528120545f198114612b7b57612b57838261357b565b6001600160a01b0386165f9081526004602090815260408083203384529091529020555b6001600160a01b0385165f9081526003602052604081208054859290612ba290849061357b565b90915550506001600160a01b038085165f81815260036020526040908190208054870190555190918716905f805160206138e383398151915290612be99087815260200190565b60405180910390a3506001949350505050565b6006545f9060ff16158015612c1c57506007546001600160a01b03163314155b15612c3a5760405163486aa30760e01b815260040160405180910390fd5b6001600160a01b0385165f908152600c602052604081205460ff168015612c7857506001600160a01b0385165f908152600c602052604090205460ff165b6001600160a01b0387165f908152600b602052604090205490915060ff16158015612cbb57506001600160a01b0385165f908152600b602052604090205460ff16155b8015612cc5575080155b8015612ce857506001600160a01b0386165f908152600c602052604090205460ff165b15612dc9576009545f90612d00908690612710612e36565b90505f612d1c600a5461271088612e369092919063ffffffff16565b905080612d29838861357b565b612d33919061357b565b95508415612d85578115612d6057600654612d5e90899061010090046001600160a01b031684612b22565b505b8015612d8057600854612d7e9089906001600160a01b031683612b22565b505b612dc6565b8115612da857600654612da69061010090046001600160a01b031683612dd3565b505b8015612dc657600854612dc4906001600160a01b031682612dd3565b505b50505b5091949350505050565b335f90815260036020526040812080548391908390612df390849061357b565b90915550506001600160a01b0383165f81815260036020526040908190208054850190555133905f805160206138e3833981519152906104f69086815260200190565b5f825f190484118302158202612e535763ad251c275f526004601cfd5b5091020490565b604051806101400160405280606081526020015f81526020015f81526020015f81526020015f151581526020015f6001600160a01b031681526020015f8152602001612eb160405180602001604052805f81525090565b81526020015f81526020015f81525090565b6001600160a01b0391909116815260200190565b5f5b83811015612ef1578181015183820152602001612ed9565b50505f910152565b5f8151808452612f10816020860160208601612ed7565b601f01601f19169290920160200192915050565b602081525f61077e6020830184612ef9565b6001600160a01b0381168114610851575f80fd5b5f8060408385031215612f5b575f80fd5b8235612f6681612f36565b946020939093013593505050565b5f60208284031215612f84575f80fd5b813561077e81612f36565b5f805f60608486031215612fa1575f80fd5b8335612fac81612f36565b92506020840135612fbc81612f36565b929592945050506040919091013590565b5f60208284031215612fdd575f80fd5b5035919050565b5f8083601f840112612ff4575f80fd5b5081356001600160401b0381111561300a575f80fd5b6020830191508360208260051b8501011115613024575f80fd5b9250929050565b5f805f805f805f805f60e08a8c031215613043575f80fd5b893561304e81612f36565b985060208a013561305e81612f36565b975060408a0135965060608a0135955060808a013561307c81612f36565b945060a08a01356001600160401b0380821115613097575f80fd5b6130a38d838e01612fe4565b909650945060c08c01359150808211156130bb575f80fd5b506130c88c828d01612fe4565b915080935050809150509295985092959850929598565b602081525f82516101408060208501526130fd610160850183612ef9565b9150602085015160408501526040850151606085015260608501516080850152608085015161313060a086018215159052565b5060a08501516001600160a01b03811660c08601525060c085015160e085015260e085015161010061316481870183519052565b860151610120868101919091529095015193019290925250919050565b634e487b7160e01b5f52604160045260245ffd5b60405161010081016001600160401b03811182821017156131b8576131b8613181565b60405290565b604051601f8201601f191681016001600160401b03811182821017156131e6576131e6613181565b604052919050565b5f6001600160401b0382111561320657613206613181565b5060051b60200190565b5f82601f83011261321f575f80fd5b81356001600160401b0381111561323857613238613181565b61324b601f8201601f19166020016131be565b81815284602083860101111561325f575f80fd5b816020850160208301375f918101602001919091529392505050565b8035801515811461328a575f80fd5b919050565b5f82601f83011261329e575f80fd5b813560206132b36132ae836131ee565b6131be565b8083825260208201915060208460051b8701019350868411156132d4575f80fd5b602086015b848110156132f95780356132ec81612f36565b83529183019183016132d9565b509695505050505050565b5f8060408385031215613315575f80fd5b82356001600160401b038082111561332b575f80fd5b818501915085601f83011261333e575f80fd5b8135602061334e6132ae836131ee565b82815260059290921b8401810191818101908984111561336c575f80fd5b8286015b8481101561345a57803586811115613386575f80fd5b8701610100818d03601f190181131561339d575f80fd5b6133a5613195565b86830135898111156133b5575f80fd5b6133c38f8983870101613210565b8252506040830135898111156133d7575f80fd5b6133e58f8983870101613210565b88830152506060830135898111156133fb575f80fd5b6134098f8983870101613210565b604083015250608080840135606083015260a0808501358284015260c0915081850135818401525060e0808501358284015261344684860161327b565b908301525085525050918301918301613370565b5096505086013592505080821115613470575f80fd5b5061347d8582860161328f565b9150509250929050565b5f805f805f805f60e0888a03121561349d575f80fd5b87356134a881612f36565b965060208801356134b881612f36565b95506040880135945060608801359350608088013560ff811681146134db575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f8060408385031215613509575f80fd5b823561351481612f36565b9150602083013561352481612f36565b809150509250929050565b600181811c9082168061354357607f821691505b60208210810361356157634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561050257610502613567565b6001600160a01b039390931683526020830191909152604082015260600190565b5f600182016135c0576135c0613567565b5060010190565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b0392831681529116602082015260400190565b8082018082111561050257610502613567565b601f82111561364f57805f5260205f20601f840160051c8101602085101561362d5750805b601f840160051c820191505b8181101561364c575f8155600101613639565b50505b505050565b81516001600160401b0381111561366d5761366d613181565b6136818161367b845461352f565b84613608565b602080601f8311600181146136b4575f841561369d5750858301515b5f19600386901b1c1916600185901b17855561370b565b5f85815260208120601f198616915b828110156136e2578886015182559484019460019091019084016136c3565b50858210156136ff57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f60208284031215613723575f80fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b5f8161375157613751613567565b505f190190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b5f8083546137898161352f565b600182811680156137a157600181146137b6576137e2565b60ff19841687528215158302870194506137e2565b875f526020805f205f5b858110156137d95781548a8201529084019082016137c0565b50505082870194505b50929695505050505050565b60a081525f61380060a0830188612ef9565b82810360208401526138128188612ef9565b6001600160a01b038781166040860152861660608501528381036080850152905061383d8185612ef9565b98975050505050505050565b5f60208284031215613859575f80fd5b815161077e81612f36565b5f8251613875818460208701612ed7565b9190910192915050565b5f6020828403121561388f575f80fd5b81516001600160e01b03198116811461077e575f80fd5b6001600160a01b03858116825284166020820152604081018390526080606082018190525f906138d890830184612ef9565b969550505050505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a2646970667358221220e3e24853e7ab6bf4d6dabdbfe36d92d3ea1b25aca81257d06213cfa3e7beb70f64736f6c63430008170033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000002cd3c02a734559472d91b285b544202a3c8b129e000000000000000000000000a2aace327172f303ab431455469cd65f3b5b63510000000000000000000000009401ae767955581e3ad0910d0e3651a2fcb37301000000000000000000000000000000000000000000000000000000000000001d554e434841494e454420454c455048414e5453203430342024454c695a0000000000000000000000000000000000000000000000000000000000000000000004454c495a00000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _name (string): UNCHAINED ELEPHANTS 404 $ELiZ
Arg [1] : _symbol (string): ELIZ
Arg [2] : _memeception (address): 0x2cd3c02A734559472d91b285B544202A3C8B129e
Arg [3] : _creator (address): 0xA2aAcE327172f303aB431455469cD65f3b5b6351
Arg [4] : _factoryNFT (address): 0x9401ae767955581e3Ad0910d0E3651A2FcB37301

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [2] : 0000000000000000000000002cd3c02a734559472d91b285b544202a3c8b129e
Arg [3] : 000000000000000000000000a2aace327172f303ab431455469cd65f3b5b6351
Arg [4] : 0000000000000000000000009401ae767955581e3ad0910d0e3651a2fcb37301
Arg [5] : 000000000000000000000000000000000000000000000000000000000000001d
Arg [6] : 554e434841494e454420454c455048414e5453203430342024454c695a000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 454c495a00000000000000000000000000000000000000000000000000000000


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.