// File: @ensdomains/buffer/contracts/Buffer.sol



pragma solidity ^0.8.4;

/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library Buffer {
    /**
    * @dev Represents a mutable buffer. Buffers have a current value (buf) and
    *      a capacity. The capacity may be longer than the current value, in
    *      which case it can be extended without the need to allocate more memory.
    */
    struct buffer {
        bytes buf;
        uint capacity;
    }

    /**
    * @dev Initializes a buffer with an initial capacity.
    * @param buf The buffer to initialize.
    * @param capacity The number of bytes of space to allocate the buffer.
    * @return The buffer, for chaining.
    */
    function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
        if (capacity % 32 != 0) {
            capacity += 32 - (capacity % 32);
        }
        // Allocate space for the buffer data
        buf.capacity = capacity;
        assembly {
            let ptr := mload(0x40)
            mstore(buf, ptr)
            mstore(ptr, 0)
            mstore(0x40, add(32, add(ptr, capacity)))
        }
        return buf;
    }

    /**
    * @dev Initializes a new buffer from an existing bytes object.
    *      Changes to the buffer may mutate the original value.
    * @param b The bytes object to initialize the buffer with.
    * @return A new buffer.
    */
    function fromBytes(bytes memory b) internal pure returns(buffer memory) {
        buffer memory buf;
        buf.buf = b;
        buf.capacity = b.length;
        return buf;
    }

    function resize(buffer memory buf, uint capacity) private pure {
        bytes memory oldbuf = buf.buf;
        init(buf, capacity);
        append(buf, oldbuf);
    }

    function max(uint a, uint b) private pure returns(uint) {
        if (a > b) {
            return a;
        }
        return b;
    }

    /**
    * @dev Sets buffer length to 0.
    * @param buf The buffer to truncate.
    * @return The original buffer, for chaining..
    */
    function truncate(buffer memory buf) internal pure returns (buffer memory) {
        assembly {
            let bufptr := mload(buf)
            mstore(bufptr, 0)
        }
        return buf;
    }

    /**
    * @dev Writes a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The start offset to write to.
    * @param data The data to append.
    * @param len The number of bytes to copy.
    * @return The original buffer, for chaining.
    */
    function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) {
        require(len <= data.length);

        if (off + len > buf.capacity) {
            resize(buf, max(buf.capacity, len + off) * 2);
        }

        uint dest;
        uint src;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Start address = buffer address + offset + sizeof(buffer length)
            dest := add(add(bufptr, 32), off)
            // Update buffer length if we're extending it
            if gt(add(len, off), buflen) {
                mstore(bufptr, add(len, off))
            }
            src := add(data, 32)
        }

        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        unchecked {
            uint mask = (256 ** (32 - len)) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask))
                let destpart := and(mload(dest), mask)
                mstore(dest, or(destpart, srcpart))
            }
        }

        return buf;
    }

    /**
    * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @param len The number of bytes to copy.
    * @return The original buffer, for chaining.
    */
    function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, len);
    }

    /**
    * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, data.length);
    }

    /**
    * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write the byte at.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
        if (off >= buf.capacity) {
            resize(buf, buf.capacity * 2);
        }

        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Address = buffer address + sizeof(buffer length) + off
            let dest := add(add(bufptr, off), 32)
            mstore8(dest, data)
            // Update buffer length if we extended it
            if eq(off, buflen) {
                mstore(bufptr, add(buflen, 1))
            }
        }
        return buf;
    }

    /**
    * @dev Appends a byte to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) {
        return writeUint8(buf, buf.buf.length, data);
    }

    /**
    * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
    *      exceed the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @param len The number of bytes to write (left-aligned).
    * @return The original buffer, for chaining.
    */
    function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) {
        if (len + off > buf.capacity) {
            resize(buf, (len + off) * 2);
        }

        unchecked {
            uint mask = (256 ** len) - 1;
            // Right-align data
            data = data >> (8 * (32 - len));
            assembly {
                // Memory address of the buffer data
                let bufptr := mload(buf)
                // Address = buffer address + sizeof(buffer length) + off + len
                let dest := add(add(bufptr, off), len)
                mstore(dest, or(and(mload(dest), not(mask)), data))
                // Update buffer length if we extended it
                if gt(add(off, len), mload(bufptr)) {
                    mstore(bufptr, add(off, len))
                }
            }
        }
        return buf;
    }

    /**
    * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) {
        return write(buf, off, bytes32(data), 20);
    }

    /**
    * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chhaining.
    */
    function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, bytes32(data), 20);
    }

    /**
    * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, 32);
    }

    /**
    * @dev Writes an integer to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @param len The number of bytes to write (right-aligned).
    * @return The original buffer, for chaining.
    */
    function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
        if (len + off > buf.capacity) {
            resize(buf, (len + off) * 2);
        }

        uint mask = (256 ** len) - 1;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Address = buffer address + off + sizeof(buffer length) + len
            let dest := add(add(bufptr, off), len)
            mstore(dest, or(and(mload(dest), not(mask)), data))
            // Update buffer length if we extended it
            if gt(add(off, len), mload(bufptr)) {
                mstore(bufptr, add(off, len))
            }
        }
        return buf;
    }

    /**
     * @dev Appends a byte to the end of the buffer. Resizes if doing so would
     * exceed the capacity of the buffer.
     * @param buf The buffer to append to.
     * @param data The data to append.
     * @return The original buffer.
     */
    function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) {
        return writeInt(buf, buf.buf.length, data, len);
    }
}

// File: @ensdomains/ens-contracts/contracts/dnssec-oracle/BytesUtils.sol



pragma solidity ^0.8.4;

library BytesUtils {
    /*
    * @dev Returns the keccak-256 hash of a byte range.
    * @param self The byte string to hash.
    * @param offset The position to start hashing at.
    * @param len The number of bytes to hash.
    * @return The hash of the byte range.
    */
    function keccak(bytes memory self, uint offset, uint len) internal pure returns (bytes32 ret) {
        require(offset + len <= self.length);
        assembly {
            ret := keccak256(add(add(self, 32), offset), len)
        }
    }


    /*
    * @dev Returns a positive number if `other` comes lexicographically after
    *      `self`, a negative number if it comes before, or zero if the
    *      contents of the two bytes are equal.
    * @param self The first bytes to compare.
    * @param other The second bytes to compare.
    * @return The result of the comparison.
    */
    function compare(bytes memory self, bytes memory other) internal pure returns (int) {
        return compare(self, 0, self.length, other, 0, other.length);
    }

    /*
    * @dev Returns a positive number if `other` comes lexicographically after
    *      `self`, a negative number if it comes before, or zero if the
    *      contents of the two bytes are equal. Comparison is done per-rune,
    *      on unicode codepoints.
    * @param self The first bytes to compare.
    * @param offset The offset of self.
    * @param len    The length of self.
    * @param other The second bytes to compare.
    * @param otheroffset The offset of the other string.
    * @param otherlen    The length of the other string.
    * @return The result of the comparison.
    */
    function compare(bytes memory self, uint offset, uint len, bytes memory other, uint otheroffset, uint otherlen) internal pure returns (int) {
        uint shortest = len;
        if (otherlen < len)
        shortest = otherlen;

        uint selfptr;
        uint otherptr;

        assembly {
            selfptr := add(self, add(offset, 32))
            otherptr := add(other, add(otheroffset, 32))
        }
        for (uint idx = 0; idx < shortest; idx += 32) {
            uint a;
            uint b;
            assembly {
                a := mload(selfptr)
                b := mload(otherptr)
            }
            if (a != b) {
                // Mask out irrelevant bytes and check again
                uint mask;
                if (shortest > 32) {
                    mask = type(uint256).max;
                } else {
                    mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
                }
                int diff = int(a & mask) - int(b & mask);
                if (diff != 0)
                return diff;
            }
            selfptr += 32;
            otherptr += 32;
        }

        return int(len) - int(otherlen);
    }

    /*
    * @dev Returns true if the two byte ranges are equal.
    * @param self The first byte range to compare.
    * @param offset The offset into the first byte range.
    * @param other The second byte range to compare.
    * @param otherOffset The offset into the second byte range.
    * @param len The number of bytes to compare
    * @return True if the byte ranges are equal, false otherwise.
    */
    function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset, uint len) internal pure returns (bool) {
        return keccak(self, offset, len) == keccak(other, otherOffset, len);
    }

    /*
    * @dev Returns true if the two byte ranges are equal with offsets.
    * @param self The first byte range to compare.
    * @param offset The offset into the first byte range.
    * @param other The second byte range to compare.
    * @param otherOffset The offset into the second byte range.
    * @return True if the byte ranges are equal, false otherwise.
    */
    function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset) internal pure returns (bool) {
        return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);
    }

    /*
    * @dev Compares a range of 'self' to all of 'other' and returns True iff
    *      they are equal.
    * @param self The first byte range to compare.
    * @param offset The offset into the first byte range.
    * @param other The second byte range to compare.
    * @return True if the byte ranges are equal, false otherwise.
    */
    function equals(bytes memory self, uint offset, bytes memory other) internal pure returns (bool) {
        return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);
    }

    /*
    * @dev Returns true if the two byte ranges are equal.
    * @param self The first byte range to compare.
    * @param other The second byte range to compare.
    * @return True if the byte ranges are equal, false otherwise.
    */
    function equals(bytes memory self, bytes memory other) internal pure returns(bool) {
        return self.length == other.length && equals(self, 0, other, 0, self.length);
    }

    /*
    * @dev Returns the 8-bit number at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 8 bits of the string, interpreted as an integer.
    */
    function readUint8(bytes memory self, uint idx) internal pure returns (uint8 ret) {
        return uint8(self[idx]);
    }

    /*
    * @dev Returns the 16-bit number at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 16 bits of the string, interpreted as an integer.
    */
    function readUint16(bytes memory self, uint idx) internal pure returns (uint16 ret) {
        require(idx + 2 <= self.length);
        assembly {
            ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
        }
    }

    /*
    * @dev Returns the 32-bit number at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 32 bits of the string, interpreted as an integer.
    */
    function readUint32(bytes memory self, uint idx) internal pure returns (uint32 ret) {
        require(idx + 4 <= self.length);
        assembly {
            ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)
        }
    }

    /*
    * @dev Returns the 32 byte value at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 32 bytes of the string.
    */
    function readBytes32(bytes memory self, uint idx) internal pure returns (bytes32 ret) {
        require(idx + 32 <= self.length);
        assembly {
            ret := mload(add(add(self, 32), idx))
        }
    }

    /*
    * @dev Returns the 32 byte value at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 32 bytes of the string.
    */
    function readBytes20(bytes memory self, uint idx) internal pure returns (bytes20 ret) {
        require(idx + 20 <= self.length);
        assembly {
            ret := and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)
        }
    }

    /*
    * @dev Returns the n byte value at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes.
    * @param len The number of bytes.
    * @return The specified 32 bytes of the string.
    */
    function readBytesN(bytes memory self, uint idx, uint len) internal pure returns (bytes32 ret) {
        require(len <= 32);
        require(idx + len <= self.length);
        assembly {
            let mask := not(sub(exp(256, sub(32, len)), 1))
            ret := and(mload(add(add(self, 32), idx)),  mask)
        }
    }

    function memcpy(uint dest, uint src, uint len) private pure {
        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        unchecked {
            uint mask = (256 ** (32 - len)) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask))
                let destpart := and(mload(dest), mask)
                mstore(dest, or(destpart, srcpart))
            }
        }
    }

    /*
    * @dev Copies a substring into a new byte string.
    * @param self The byte string to copy from.
    * @param offset The offset to start copying at.
    * @param len The number of bytes to copy.
    */
    function substring(bytes memory self, uint offset, uint len) internal pure returns(bytes memory) {
        require(offset + len <= self.length);

        bytes memory ret = new bytes(len);
        uint dest;
        uint src;

        assembly {
            dest := add(ret, 32)
            src := add(add(self, 32), offset)
        }
        memcpy(dest, src, len);

        return ret;
    }

    // Maps characters from 0x30 to 0x7A to their base32 values.
    // 0xFF represents invalid characters in that range.
    bytes constant base32HexTable = hex'00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F';

    /**
     * @dev Decodes unpadded base32 data of up to one word in length.
     * @param self The data to decode.
     * @param off Offset into the string to start at.
     * @param len Number of characters to decode.
     * @return The decoded data, left aligned.
     */
    function base32HexDecodeWord(bytes memory self, uint off, uint len) internal pure returns(bytes32) {
        require(len <= 52);

        uint ret = 0;
        uint8 decoded;
        for(uint i = 0; i < len; i++) {
            bytes1 char = self[off + i];
            require(char >= 0x30 && char <= 0x7A);
            decoded = uint8(base32HexTable[uint(uint8(char)) - 0x30]);
            require(decoded <= 0x20);
            if(i == len - 1) {
                break;
            }
            ret = (ret << 5) | decoded;
        }

        uint bitlen = len * 5;
        if(len % 8 == 0) {
            // Multiple of 8 characters, no padding
            ret = (ret << 5) | decoded;
        } else if(len % 8 == 2) {
            // Two extra characters - 1 byte
            ret = (ret << 3) | (decoded >> 2);
            bitlen -= 2;
        } else if(len % 8 == 4) {
            // Four extra characters - 2 bytes
            ret = (ret << 1) | (decoded >> 4);
            bitlen -= 4;
        } else if(len % 8 == 5) {
            // Five extra characters - 3 bytes
            ret = (ret << 4) | (decoded >> 1);
            bitlen -= 1;
        } else if(len % 8 == 7) {
            // Seven extra characters - 4 bytes
            ret = (ret << 2) | (decoded >> 3);
            bitlen -= 3;
        } else {
            revert();
        }

        return bytes32(ret << (256 - bitlen));
    }
}
// File: @ensdomains/ens-contracts/contracts/dnssec-oracle/RRUtils.sol



pragma solidity ^0.8.4;

import "./BytesUtils.sol";
import "@ensdomains/buffer/contracts/Buffer.sol";

/**
* @dev RRUtils is a library that provides utilities for parsing DNS resource records.
*/
library RRUtils {
    using BytesUtils for *;
    using Buffer for *;

    /**
    * @dev Returns the number of bytes in the DNS name at 'offset' in 'self'.
    * @param self The byte array to read a name from.
    * @param offset The offset to start reading at.
    * @return The length of the DNS name at 'offset', in bytes.
    */
    function nameLength(bytes memory self, uint offset) internal pure returns(uint) {
        uint idx = offset;
        while (true) {
            assert(idx < self.length);
            uint labelLen = self.readUint8(idx);
            idx += labelLen + 1;
            if (labelLen == 0) {
                break;
            }
        }
        return idx - offset;
    }

    /**
    * @dev Returns a DNS format name at the specified offset of self.
    * @param self The byte array to read a name from.
    * @param offset The offset to start reading at.
    * @return ret The name.
    */
    function readName(bytes memory self, uint offset) internal pure returns(bytes memory ret) {
        uint len = nameLength(self, offset);
        return self.substring(offset, len);
    }

    /**
    * @dev Returns the number of labels in the DNS name at 'offset' in 'self'.
    * @param self The byte array to read a name from.
    * @param offset The offset to start reading at.
    * @return The number of labels in the DNS name at 'offset', in bytes.
    */
    function labelCount(bytes memory self, uint offset) internal pure returns(uint) {
        uint count = 0;
        while (true) {
            assert(offset < self.length);
            uint labelLen = self.readUint8(offset);
            offset += labelLen + 1;
            if (labelLen == 0) {
                break;
            }
            count += 1;
        }
        return count;
    }

    uint constant RRSIG_TYPE = 0;
    uint constant RRSIG_ALGORITHM = 2;
    uint constant RRSIG_LABELS = 3;
    uint constant RRSIG_TTL = 4;
    uint constant RRSIG_EXPIRATION = 8;
    uint constant RRSIG_INCEPTION = 12;
    uint constant RRSIG_KEY_TAG = 16;
    uint constant RRSIG_SIGNER_NAME = 18;

    struct SignedSet {
        uint16 typeCovered;
        uint8 algorithm;
        uint8 labels;
        uint32 ttl;
        uint32 expiration;
        uint32 inception;
        uint16 keytag;
        bytes signerName;
        bytes data;
        bytes name;
    }

    function readSignedSet(bytes memory data) internal pure returns(SignedSet memory self) {
        self.typeCovered = data.readUint16(RRSIG_TYPE);
        self.algorithm = data.readUint8(RRSIG_ALGORITHM);
        self.labels = data.readUint8(RRSIG_LABELS);
        self.ttl = data.readUint32(RRSIG_TTL);
        self.expiration = data.readUint32(RRSIG_EXPIRATION);
        self.inception = data.readUint32(RRSIG_INCEPTION);
        self.keytag = data.readUint16(RRSIG_KEY_TAG);
        self.signerName = readName(data, RRSIG_SIGNER_NAME);
        self.data = data.substring(RRSIG_SIGNER_NAME + self.signerName.length, data.length - RRSIG_SIGNER_NAME - self.signerName.length);
    }

    function rrs(SignedSet memory rrset) internal pure returns(RRIterator memory) {
        return iterateRRs(rrset.data, 0);
    }

    /**
    * @dev An iterator over resource records.
    */
    struct RRIterator {
        bytes data;
        uint offset;
        uint16 dnstype;
        uint16 class;
        uint32 ttl;
        uint rdataOffset;
        uint nextOffset;
    }

    /**
    * @dev Begins iterating over resource records.
    * @param self The byte string to read from.
    * @param offset The offset to start reading at.
    * @return ret An iterator object.
    */
    function iterateRRs(bytes memory self, uint offset) internal pure returns (RRIterator memory ret) {
        ret.data = self;
        ret.nextOffset = offset;
        next(ret);
    }

    /**
    * @dev Returns true iff there are more RRs to iterate.
    * @param iter The iterator to check.
    * @return True iff the iterator has finished.
    */
    function done(RRIterator memory iter) internal pure returns(bool) {
        return iter.offset >= iter.data.length;
    }

    /**
    * @dev Moves the iterator to the next resource record.
    * @param iter The iterator to advance.
    */
    function next(RRIterator memory iter) internal pure {
        iter.offset = iter.nextOffset;
        if (iter.offset >= iter.data.length) {
            return;
        }

        // Skip the name
        uint off = iter.offset + nameLength(iter.data, iter.offset);

        // Read type, class, and ttl
        iter.dnstype = iter.data.readUint16(off);
        off += 2;
        iter.class = iter.data.readUint16(off);
        off += 2;
        iter.ttl = iter.data.readUint32(off);
        off += 4;

        // Read the rdata
        uint rdataLength = iter.data.readUint16(off);
        off += 2;
        iter.rdataOffset = off;
        iter.nextOffset = off + rdataLength;
    }

    /**
    * @dev Returns the name of the current record.
    * @param iter The iterator.
    * @return A new bytes object containing the owner name from the RR.
    */
    function name(RRIterator memory iter) internal pure returns(bytes memory) {
        return iter.data.substring(iter.offset, nameLength(iter.data, iter.offset));
    }

    /**
    * @dev Returns the rdata portion of the current record.
    * @param iter The iterator.
    * @return A new bytes object containing the RR's RDATA.
    */
    function rdata(RRIterator memory iter) internal pure returns(bytes memory) {
        return iter.data.substring(iter.rdataOffset, iter.nextOffset - iter.rdataOffset);
    }

    uint constant DNSKEY_FLAGS = 0;
    uint constant DNSKEY_PROTOCOL = 2;
    uint constant DNSKEY_ALGORITHM = 3;
    uint constant DNSKEY_PUBKEY = 4;

    struct DNSKEY {
        uint16 flags;
        uint8 protocol;
        uint8 algorithm;
        bytes publicKey;
    }

    function readDNSKEY(bytes memory data, uint offset, uint length) internal pure returns(DNSKEY memory self) {
        self.flags = data.readUint16(offset + DNSKEY_FLAGS);
        self.protocol = data.readUint8(offset + DNSKEY_PROTOCOL);
        self.algorithm = data.readUint8(offset + DNSKEY_ALGORITHM);
        self.publicKey = data.substring(offset + DNSKEY_PUBKEY, length - DNSKEY_PUBKEY);
    } 

    uint constant DS_KEY_TAG = 0;
    uint constant DS_ALGORITHM = 2;
    uint constant DS_DIGEST_TYPE = 3;
    uint constant DS_DIGEST = 4;

    struct DS {
        uint16 keytag;
        uint8 algorithm;
        uint8 digestType;
        bytes digest;
    }

    function readDS(bytes memory data, uint offset, uint length) internal pure returns(DS memory self) {
        self.keytag = data.readUint16(offset + DS_KEY_TAG);
        self.algorithm = data.readUint8(offset + DS_ALGORITHM);
        self.digestType = data.readUint8(offset + DS_DIGEST_TYPE);
        self.digest = data.substring(offset + DS_DIGEST, length - DS_DIGEST);
    }

    struct NSEC3 {
        uint8 hashAlgorithm;
        uint8 flags;
        uint16 iterations;
        bytes salt;
        bytes32 nextHashedOwnerName;
        bytes typeBitmap;
    }

    uint constant NSEC3_HASH_ALGORITHM = 0;
    uint constant NSEC3_FLAGS = 1;
    uint constant NSEC3_ITERATIONS = 2;
    uint constant NSEC3_SALT_LENGTH = 4;
    uint constant NSEC3_SALT = 5;

    function readNSEC3(bytes memory data, uint offset, uint length) internal pure returns(NSEC3 memory self) {
        uint end = offset + length;
        self.hashAlgorithm = data.readUint8(offset + NSEC3_HASH_ALGORITHM);
        self.flags = data.readUint8(offset + NSEC3_FLAGS);
        self.iterations = data.readUint16(offset + NSEC3_ITERATIONS);
        uint8 saltLength = data.readUint8(offset + NSEC3_SALT_LENGTH);
        offset = offset + NSEC3_SALT;
        self.salt = data.substring(offset, saltLength);
        offset += saltLength;
        uint8 nextLength = data.readUint8(offset);
        require(nextLength <= 32);
        offset += 1;
        self.nextHashedOwnerName = data.readBytesN(offset, nextLength);
        offset += nextLength;
        self.typeBitmap = data.substring(offset, end - offset);
    }

    function checkTypeBitmap(NSEC3 memory self, uint16 rrtype) internal pure returns(bool) {
        return checkTypeBitmap(self.typeBitmap, 0, rrtype);
    }

    /**
    * @dev Checks if a given RR type exists in a type bitmap.
    * @param bitmap The byte string to read the type bitmap from.
    * @param offset The offset to start reading at.
    * @param rrtype The RR type to check for.
    * @return True if the type is found in the bitmap, false otherwise.
    */
    function checkTypeBitmap(bytes memory bitmap, uint offset, uint16 rrtype) internal pure returns (bool) {
        uint8 typeWindow = uint8(rrtype >> 8);
        uint8 windowByte = uint8((rrtype & 0xff) / 8);
        uint8 windowBitmask = uint8(uint8(1) << (uint8(7) - uint8(rrtype & 0x7)));
        for (uint off = offset; off < bitmap.length;) {
            uint8 window = bitmap.readUint8(off);
            uint8 len = bitmap.readUint8(off + 1);
            if (typeWindow < window) {
                // We've gone past our window; it's not here.
                return false;
            } else if (typeWindow == window) {
                // Check this type bitmap
                if (len <= windowByte) {
                    // Our type is past the end of the bitmap
                    return false;
                }
                return (bitmap.readUint8(off + windowByte + 2) & windowBitmask) != 0;
            } else {
                // Skip this type bitmap
                off += len + 2;
            }
        }

        return false;
    }

    function compareNames(bytes memory self, bytes memory other) internal pure returns (int) {
        if (self.equals(other)) {
            return 0;
        }

        uint off;
        uint otheroff;
        uint prevoff;
        uint otherprevoff;
        uint counts = labelCount(self, 0);
        uint othercounts = labelCount(other, 0);

        // Keep removing labels from the front of the name until both names are equal length
        while (counts > othercounts) {
            prevoff = off;
            off = progress(self, off);
            counts--;
        }

        while (othercounts > counts) {
            otherprevoff = otheroff;
            otheroff = progress(other, otheroff);
            othercounts--;
        }

        // Compare the last nonequal labels to each other
        while (counts > 0 && !self.equals(off, other, otheroff)) {
            prevoff = off;
            off = progress(self, off);
            otherprevoff = otheroff;
            otheroff = progress(other, otheroff);
            counts -= 1;
        }

        if (off == 0) {
            return -1;
        }
        if(otheroff == 0) {
            return 1;
        }

        return self.compare(prevoff + 1, self.readUint8(prevoff), other, otherprevoff + 1, other.readUint8(otherprevoff));
    }

    /**
     * @dev Compares two serial numbers using RFC1982 serial number math.
     */
    function serialNumberGte(uint32 i1, uint32 i2) internal pure returns(bool) {
        return int32(i1) - int32(i2) >= 0;
    }

    function progress(bytes memory body, uint off) internal pure returns(uint) {
        return off + 1 + body.readUint8(off);
    }

    /**
     * @dev Computes the keytag for a chunk of data.
     * @param data The data to compute a keytag for.
     * @return The computed key tag.
     */
    function computeKeytag(bytes memory data) internal pure returns (uint16) {
        /* This function probably deserves some explanation.
         * The DNSSEC keytag function is a checksum that relies on summing up individual bytes
         * from the input string, with some mild bitshifting. Here's a Naive solidity implementation:
         *
         *     function computeKeytag(bytes memory data) internal pure returns (uint16) {
         *         uint ac;
         *         for (uint i = 0; i < data.length; i++) {
         *             ac += i & 1 == 0 ? uint16(data.readUint8(i)) << 8 : data.readUint8(i);
         *         }
         *         return uint16(ac + (ac >> 16));
         *     }
         *
         * The EVM, with its 256 bit words, is exceedingly inefficient at doing byte-by-byte operations;
         * the code above, on reasonable length inputs, consumes over 100k gas. But we can make the EVM's
         * large words work in our favour.
         *
         * The code below works by treating the input as a series of 256 bit words. It first masks out
         * even and odd bytes from each input word, adding them to two separate accumulators `ac1` and `ac2`.
         * The bytes are separated by empty bytes, so as long as no individual sum exceeds 2^16-1, we're
         * effectively summing 16 different numbers with each EVM ADD opcode.
         *
         * Once it's added up all the inputs, it has to add all the 16 bit values in `ac1` and `ac2` together.
         * It does this using the same trick - mask out every other value, shift to align them, add them together.
         * After the first addition on both accumulators, there's enough room to add the two accumulators together,
         * and the remaining sums can be done just on ac1.
         */
        unchecked {
            require(data.length <= 8192, "Long keys not permitted");
            uint ac1;
            uint ac2;
            for(uint i = 0; i < data.length + 31; i += 32) {
                uint word;
                assembly {
                    word := mload(add(add(data, 32), i))
                }
                if(i + 32 > data.length) {
                    uint unused = 256 - (data.length - i) * 8;
                    word = (word >> unused) << unused;
                }
                ac1 += (word & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8;
                ac2 += (word & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF);
            }
            ac1 = (ac1 & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF)
                + ((ac1 & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16);
            ac2 = (ac2 & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF)
                + ((ac2 & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16);
            ac1 = (ac1 << 8) + ac2;
            ac1 = (ac1 & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF)
                + ((ac1 & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32);
            ac1 = (ac1 & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF)
                + ((ac1 & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64);
            ac1 = (ac1 & 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
                + (ac1 >> 128);
            ac1 += (ac1 >> 16) & 0xFFFF;
            return uint16(ac1);
        }
    }
}
// File: @ensdomains/ens-contracts/contracts/registry/ENS.sol



pragma solidity >=0.8.4;

interface ENS {

    // Logged when the owner of a node assigns a new owner to a subnode.
    event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);

    // Logged when the owner of a node transfers ownership to a new account.
    event Transfer(bytes32 indexed node, address owner);

    // Logged when the resolver for a node changes.
    event NewResolver(bytes32 indexed node, address resolver);

    // Logged when the TTL of a node changes
    event NewTTL(bytes32 indexed node, uint64 ttl);

    // Logged when an operator is added or removed.
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual;
    function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual;
    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32);
    function setResolver(bytes32 node, address resolver) external virtual;
    function setOwner(bytes32 node, address owner) external virtual;
    function setTTL(bytes32 node, uint64 ttl) external virtual;
    function setApprovalForAll(address operator, bool approved) external virtual;
    function owner(bytes32 node) external virtual view returns (address);
    function resolver(bytes32 node) external virtual view returns (address);
    function ttl(bytes32 node) external virtual view returns (uint64);
    function recordExists(bytes32 node) external virtual view returns (bool);
    function isApprovedForAll(address owner, address operator) external virtual view returns (bool);
}

// File: @ensdomains/ens-contracts/contracts/registry/ENSRegistry.sol



pragma solidity >=0.8.4;

import "./ENS.sol";

/**
 * The ENS registry contract.
 */
contract ENSRegistry is ENS {

    struct Record {
        address owner;
        address resolver;
        uint64 ttl;
    }

    mapping (bytes32 => Record) records;
    mapping (address => mapping(address => bool)) operators;

    // Permits modifications only by the owner of the specified node.
    modifier authorised(bytes32 node) {
        address owner = records[node].owner;
        require(owner == msg.sender || operators[owner][msg.sender]);
        _;
    }

    /**
     * @dev Constructs a new ENS registrar.
     */
    constructor() public {
        records[0x0].owner = msg.sender;
    }

    /**
     * @dev Sets the record for a node.
     * @param node The node to update.
     * @param owner The address of the new owner.
     * @param resolver The address of the resolver.
     * @param ttl The TTL in seconds.
     */
    function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual override {
        setOwner(node, owner);
        _setResolverAndTTL(node, resolver, ttl);
    }

    /**
     * @dev Sets the record for a subnode.
     * @param node The parent node.
     * @param label The hash of the label specifying the subnode.
     * @param owner The address of the new owner.
     * @param resolver The address of the resolver.
     * @param ttl The TTL in seconds.
     */
    function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual override {
        bytes32 subnode = setSubnodeOwner(node, label, owner);
        _setResolverAndTTL(subnode, resolver, ttl);
    }

    /**
     * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
     * @param node The node to transfer ownership of.
     * @param owner The address of the new owner.
     */
    function setOwner(bytes32 node, address owner) public virtual override authorised(node) {
        _setOwner(node, owner);
        emit Transfer(node, owner);
    }

    /**
     * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
     * @param node The parent node.
     * @param label The hash of the label specifying the subnode.
     * @param owner The address of the new owner.
     */
    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) {
        bytes32 subnode = keccak256(abi.encodePacked(node, label));
        _setOwner(subnode, owner);
        emit NewOwner(node, label, owner);
        return subnode;
    }

    /**
     * @dev Sets the resolver address for the specified node.
     * @param node The node to update.
     * @param resolver The address of the resolver.
     */
    function setResolver(bytes32 node, address resolver) public virtual override authorised(node) {
        emit NewResolver(node, resolver);
        records[node].resolver = resolver;
    }

    /**
     * @dev Sets the TTL for the specified node.
     * @param node The node to update.
     * @param ttl The TTL in seconds.
     */
    function setTTL(bytes32 node, uint64 ttl) public virtual override authorised(node) {
        emit NewTTL(node, ttl);
        records[node].ttl = ttl;
    }

    /**
     * @dev Enable or disable approval for a third party ("operator") to manage
     *  all of `msg.sender`'s ENS records. Emits the ApprovalForAll event.
     * @param operator Address to add to the set of authorized operators.
     * @param approved True if the operator is approved, false to revoke approval.
     */
    function setApprovalForAll(address operator, bool approved) external virtual override {
        operators[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    /**
     * @dev Returns the address that owns the specified node.
     * @param node The specified node.
     * @return address of the owner.
     */
    function owner(bytes32 node) public virtual override view returns (address) {
        address addr = records[node].owner;
        if (addr == address(this)) {
            return address(0x0);
        }

        return addr;
    }

    /**
     * @dev Returns the address of the resolver for the specified node.
     * @param node The specified node.
     * @return address of the resolver.
     */
    function resolver(bytes32 node) public virtual override view returns (address) {
        return records[node].resolver;
    }

    /**
     * @dev Returns the TTL of a node, and any records associated with it.
     * @param node The specified node.
     * @return ttl of the node.
     */
    function ttl(bytes32 node) public virtual override view returns (uint64) {
        return records[node].ttl;
    }

    /**
     * @dev Returns whether a record has been imported to the registry.
     * @param node The specified node.
     * @return Bool if record exists
     */
    function recordExists(bytes32 node) public virtual override view returns (bool) {
        return records[node].owner != address(0x0);
    }

    /**
     * @dev Query if an address is an authorized operator for another address.
     * @param owner The address that owns the records.
     * @param operator The address that acts on behalf of the owner.
     * @return True if `operator` is an approved operator for `owner`, false otherwise.
     */
    function isApprovedForAll(address owner, address operator) external virtual override view returns (bool) {
        return operators[owner][operator];
    }

    function _setOwner(bytes32 node, address owner) internal virtual {
        records[node].owner = owner;
    }

    function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal {
        if(resolver != records[node].resolver) {
            records[node].resolver = resolver;
            emit NewResolver(node, resolver);
        }

        if(ttl != records[node].ttl) {
            records[node].ttl = ttl;
            emit NewTTL(node, ttl);
        }
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/IMulticallable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IMulticallable {
    function multicall(bytes[] calldata data) external returns(bytes[] memory results);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/ISupportsInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface ISupportsInterface {
    function supportsInterface(bytes4 interfaceID) external pure returns(bool);
}
// File: @ensdomains/ens-contracts/contracts/resolvers/Multicallable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./IMulticallable.sol";
import "./SupportsInterface.sol";

abstract contract Multicallable is IMulticallable, SupportsInterface {
    function multicall(bytes[] calldata data) external override returns(bytes[] memory results) {
        results = new bytes[](data.length);
        for(uint i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);
            require(success);
            results[i] = result;
        }
        return results;
    }

    function supportsInterface(bytes4 interfaceID) public override virtual pure returns(bool) {
        return interfaceID == type(IMulticallable).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../registry/ENS.sol";
import "./profiles/ABIResolver.sol";
import "./profiles/AddrResolver.sol";
import "./profiles/ContentHashResolver.sol";
import "./profiles/DNSResolver.sol";
import "./profiles/InterfaceResolver.sol";
import "./profiles/NameResolver.sol";
import "./profiles/PubkeyResolver.sol";
import "./profiles/TextResolver.sol";
import "./Multicallable.sol";

interface INameWrapper {
    function ownerOf(uint256 id) external view returns (address);
}

/**
 * A simple resolver anyone can use; only allows the owner of a node to set its
 * address.
 */
contract PublicResolver is Multicallable, ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver {
    ENS ens;
    INameWrapper nameWrapper;

    /**
     * A mapping of operators. An address that is authorised for an address
     * may make any changes to the name that the owner could, but may not update
     * the set of authorisations.
     * (owner, operator) => approved
     */
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Logged when an operator is added or removed.
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    constructor(ENS _ens, INameWrapper wrapperAddress){
        ens = _ens;
        nameWrapper = wrapperAddress;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) external{
        require(
            msg.sender != operator,
            "ERC1155: setting approval status for self"
        );

        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function isAuthorised(bytes32 node) internal override view returns(bool) {
        address owner = ens.owner(node);
        if(owner == address(nameWrapper) ){
            owner = nameWrapper.ownerOf(uint256(node));
        }
        return owner == msg.sender || isApprovedForAll(owner, msg.sender);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view returns (bool){
        return _operatorApprovals[account][operator];
    }

    function supportsInterface(bytes4 interfaceID) public override(Multicallable, ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver) pure returns(bool) {
        return interfaceID == type(IMulticallable).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/ResolverBase.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./SupportsInterface.sol";

abstract contract ResolverBase is SupportsInterface {
    function isAuthorised(bytes32 node) internal virtual view returns(bool);

    modifier authorised(bytes32 node) {
        require(isAuthorised(node));
        _;
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/SupportsInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./ISupportsInterface.sol";

abstract contract SupportsInterface is ISupportsInterface {
    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(ISupportsInterface).interfaceId;
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/ABIResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./IABIResolver.sol";
import "../ResolverBase.sol";

abstract contract ABIResolver is IABIResolver, ResolverBase {
    mapping(bytes32=>mapping(uint256=>bytes)) abis;

    /**
     * Sets the ABI associated with an ENS node.
     * Nodes may have one ABI of each content type. To remove an ABI, set it to
     * the empty string.
     * @param node The node to update.
     * @param contentType The content type of the ABI
     * @param data The ABI data.
     */
    function setABI(bytes32 node, uint256 contentType, bytes calldata data) virtual external authorised(node) {
        // Content types must be powers of 2
        require(((contentType - 1) & contentType) == 0);

        abis[node][contentType] = data;
        emit ABIChanged(node, contentType);
    }

    /**
     * Returns the ABI associated with an ENS node.
     * Defined in EIP205.
     * @param node The ENS node to query
     * @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
     * @return contentType The content type of the return value
     * @return data The ABI data
     */
    function ABI(bytes32 node, uint256 contentTypes) virtual override external view returns (uint256, bytes memory) {
        mapping(uint256=>bytes) storage abiset = abis[node];

        for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) {
            if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) {
                return (contentType, abiset[contentType]);
            }
        }

        return (0, bytes(""));
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(IABIResolver).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/AddrResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "./IAddrResolver.sol";
import "./IAddressResolver.sol";

abstract contract AddrResolver is IAddrResolver, IAddressResolver, ResolverBase {
    uint constant private COIN_TYPE_ETH = 60;

    mapping(bytes32=>mapping(uint=>bytes)) _addresses;

    /**
     * Sets the address associated with an ENS node.
     * May only be called by the owner of that node in the ENS registry.
     * @param node The node to update.
     * @param a The address to set.
     */
    function setAddr(bytes32 node, address a) virtual external authorised(node) {
        setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
    }

    /**
     * Returns the address associated with an ENS node.
     * @param node The ENS node to query.
     * @return The associated address.
     */
    function addr(bytes32 node) virtual override public view returns (address payable) {
        bytes memory a = addr(node, COIN_TYPE_ETH);
        if(a.length == 0) {
            return payable(0);
        }
        return bytesToAddress(a);
    }

    function setAddr(bytes32 node, uint coinType, bytes memory a) virtual public authorised(node) {
        emit AddressChanged(node, coinType, a);
        if(coinType == COIN_TYPE_ETH) {
            emit AddrChanged(node, bytesToAddress(a));
        }
        _addresses[node][coinType] = a;
    }

    function addr(bytes32 node, uint coinType) virtual override public view returns(bytes memory) {
        return _addresses[node][coinType];
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(IAddrResolver).interfaceId || interfaceID == type(IAddressResolver).interfaceId || super.supportsInterface(interfaceID);
    }

    function bytesToAddress(bytes memory b) internal pure returns(address payable a) {
        require(b.length == 20);
        assembly {
            a := div(mload(add(b, 32)), exp(256, 12))
        }
    }

    function addressToBytes(address a) internal pure returns(bytes memory b) {
        b = new bytes(20);
        assembly {
            mstore(add(b, 32), mul(a, exp(256, 12)))
        }
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/ContentHashResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "./IContentHashResolver.sol";

abstract contract ContentHashResolver is IContentHashResolver, ResolverBase {
    mapping(bytes32=>bytes) hashes;

    /**
     * Sets the contenthash associated with an ENS node.
     * May only be called by the owner of that node in the ENS registry.
     * @param node The node to update.
     * @param hash The contenthash to set
     */
    function setContenthash(bytes32 node, bytes calldata hash) virtual external authorised(node) {
        hashes[node] = hash;
        emit ContenthashChanged(node, hash);
    }

    /**
     * Returns the contenthash associated with an ENS node.
     * @param node The ENS node to query.
     * @return The associated contenthash.
     */
    function contenthash(bytes32 node) virtual external override view returns (bytes memory) {
        return hashes[node];
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(IContentHashResolver).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/DNSResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "../../dnssec-oracle/RRUtils.sol";
import "./IDNSRecordResolver.sol";
import "./IDNSZoneResolver.sol";

abstract contract DNSResolver is IDNSRecordResolver, IDNSZoneResolver, ResolverBase {
    using RRUtils for *;
    using BytesUtils for bytes;

    // Zone hashes for the domains.
    // A zone hash is an EIP-1577 content hash in binary format that should point to a
    // resource containing a single zonefile.
    // node => contenthash
    mapping(bytes32=>bytes) private zonehashes;

    // Version the mapping for each zone.  This allows users who have lost
    // track of their entries to effectively delete an entire zone by bumping
    // the version number.
    // node => version
    mapping(bytes32=>uint256) private versions;

    // The records themselves.  Stored as binary RRSETs
    // node => version => name => resource => data
    mapping(bytes32=>mapping(uint256=>mapping(bytes32=>mapping(uint16=>bytes)))) private records;

    // Count of number of entries for a given name.  Required for DNS resolvers
    // when resolving wildcards.
    // node => version => name => number of records
    mapping(bytes32=>mapping(uint256=>mapping(bytes32=>uint16))) private nameEntriesCount;

    /**
     * Set one or more DNS records.  Records are supplied in wire-format.
     * Records with the same node/name/resource must be supplied one after the
     * other to ensure the data is updated correctly. For example, if the data
     * was supplied:
     *     a.example.com IN A 1.2.3.4
     *     a.example.com IN A 5.6.7.8
     *     www.example.com IN CNAME a.example.com.
     * then this would store the two A records for a.example.com correctly as a
     * single RRSET, however if the data was supplied:
     *     a.example.com IN A 1.2.3.4
     *     www.example.com IN CNAME a.example.com.
     *     a.example.com IN A 5.6.7.8
     * then this would store the first A record, the CNAME, then the second A
     * record which would overwrite the first.
     *
     * @param node the namehash of the node for which to set the records
     * @param data the DNS wire format records to set
     */
    function setDNSRecords(bytes32 node, bytes calldata data) virtual external authorised(node) {
        uint16 resource = 0;
        uint256 offset = 0;
        bytes memory name;
        bytes memory value;
        bytes32 nameHash;
        // Iterate over the data to add the resource records
        for (RRUtils.RRIterator memory iter = data.iterateRRs(0); !iter.done(); iter.next()) {
            if (resource == 0) {
                resource = iter.dnstype;
                name = iter.name();
                nameHash = keccak256(abi.encodePacked(name));
                value = bytes(iter.rdata());
            } else {
                bytes memory newName = iter.name();
                if (resource != iter.dnstype || !name.equals(newName)) {
                    setDNSRRSet(node, name, resource, data, offset, iter.offset - offset, value.length == 0);
                    resource = iter.dnstype;
                    offset = iter.offset;
                    name = newName;
                    nameHash = keccak256(name);
                    value = bytes(iter.rdata());
                }
            }
        }
        if (name.length > 0) {
            setDNSRRSet(node, name, resource, data, offset, data.length - offset, value.length == 0);
        }
    }

    /**
     * Obtain a DNS record.
     * @param node the namehash of the node for which to fetch the record
     * @param name the keccak-256 hash of the fully-qualified name for which to fetch the record
     * @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types
     * @return the DNS record in wire format if present, otherwise empty
     */
    function dnsRecord(bytes32 node, bytes32 name, uint16 resource) virtual override public view returns (bytes memory) {
        return records[node][versions[node]][name][resource];
    }

    /**
     * Check if a given node has records.
     * @param node the namehash of the node for which to check the records
     * @param name the namehash of the node for which to check the records
     */
    function hasDNSRecords(bytes32 node, bytes32 name) virtual public view returns (bool) {
        return (nameEntriesCount[node][versions[node]][name] != 0);
    }

    /**
     * Clear all information for a DNS zone.
     * @param node the namehash of the node for which to clear the zone
     */
    function clearDNSZone(bytes32 node) virtual public authorised(node) {
        versions[node]++;
        emit DNSZoneCleared(node);
    }

    /**
     * setZonehash sets the hash for the zone.
     * May only be called by the owner of that node in the ENS registry.
     * @param node The node to update.
     * @param hash The zonehash to set
     */
    function setZonehash(bytes32 node, bytes calldata hash) virtual external authorised(node) {
        bytes memory oldhash = zonehashes[node];
        zonehashes[node] = hash;
        emit DNSZonehashChanged(node, oldhash, hash);
    }

    /**
     * zonehash obtains the hash for the zone.
     * @param node The ENS node to query.
     * @return The associated contenthash.
     */
    function zonehash(bytes32 node) virtual override external view returns (bytes memory) {
        return zonehashes[node];
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(IDNSRecordResolver).interfaceId ||
               interfaceID == type(IDNSZoneResolver).interfaceId ||
               super.supportsInterface(interfaceID);
    }

    function setDNSRRSet(
        bytes32 node,
        bytes memory name,
        uint16 resource,
        bytes memory data,
        uint256 offset,
        uint256 size,
        bool deleteRecord) private
    {
        uint256 version = versions[node];
        bytes32 nameHash = keccak256(name);
        bytes memory rrData = data.substring(offset, size);
        if (deleteRecord) {
            if (records[node][version][nameHash][resource].length != 0) {
                nameEntriesCount[node][version][nameHash]--;
            }
            delete(records[node][version][nameHash][resource]);
            emit DNSRecordDeleted(node, name, resource);
        } else {
            if (records[node][version][nameHash][resource].length == 0) {
                nameEntriesCount[node][version][nameHash]++;
            }
            records[node][version][nameHash][resource] = rrData;
            emit DNSRecordChanged(node, name, resource, rrData);
        }
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IABIResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./IABIResolver.sol";
import "../ResolverBase.sol";

interface IABIResolver {
    event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
    /**
     * Returns the ABI associated with an ENS node.
     * Defined in EIP205.
     * @param node The ENS node to query
     * @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
     * @return contentType The content type of the return value
     * @return data The ABI data
     */
    function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IAddrResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

/**
 * Interface for the legacy (ETH-only) addr function.
 */
interface IAddrResolver {
    event AddrChanged(bytes32 indexed node, address a);

    /**
     * Returns the address associated with an ENS node.
     * @param node The ENS node to query.
     * @return The associated address.
     */
    function addr(bytes32 node) external view returns (address payable);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IAddressResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

/**
 * Interface for the new (multicoin) addr function.
 */
interface IAddressResolver {
    event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);

    function addr(bytes32 node, uint coinType) external view returns(bytes memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IContentHashResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IContentHashResolver {
    event ContenthashChanged(bytes32 indexed node, bytes hash);

    /**
     * Returns the contenthash associated with an ENS node.
     * @param node The ENS node to query.
     * @return The associated contenthash.
     */
    function contenthash(bytes32 node) external view returns (bytes memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IDNSRecordResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IDNSRecordResolver {
    // DNSRecordChanged is emitted whenever a given node/name/resource's RRSET is updated.
    event DNSRecordChanged(bytes32 indexed node, bytes name, uint16 resource, bytes record);
    // DNSRecordDeleted is emitted whenever a given node/name/resource's RRSET is deleted.
    event DNSRecordDeleted(bytes32 indexed node, bytes name, uint16 resource);
    // DNSZoneCleared is emitted whenever a given node's zone information is cleared.
    event DNSZoneCleared(bytes32 indexed node);

    /**
     * Obtain a DNS record.
     * @param node the namehash of the node for which to fetch the record
     * @param name the keccak-256 hash of the fully-qualified name for which to fetch the record
     * @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types
     * @return the DNS record in wire format if present, otherwise empty
     */
    function dnsRecord(bytes32 node, bytes32 name, uint16 resource) external view returns (bytes memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IDNSZoneResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IDNSZoneResolver {
    // DNSZonehashChanged is emitted whenever a given node's zone hash is updated.
    event DNSZonehashChanged(bytes32 indexed node, bytes lastzonehash, bytes zonehash);

    /**
     * zonehash obtains the hash for the zone.
     * @param node The ENS node to query.
     * @return The associated contenthash.
     */
    function zonehash(bytes32 node) external view returns (bytes memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IInterfaceResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IInterfaceResolver {
    event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer);

    /**
     * Returns the address of a contract that implements the specified interface for this name.
     * If an implementer has not been set for this interfaceID and name, the resolver will query
     * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that
     * contract implements EIP165 and returns `true` for the specified interfaceID, its address
     * will be returned.
     * @param node The ENS node to query.
     * @param interfaceID The EIP 165 interface ID to check for.
     * @return The address that implements this interface, or 0 if the interface is unsupported.
     */
    function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/INameResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface INameResolver {
    event NameChanged(bytes32 indexed node, string name);

    /**
     * Returns the name associated with an ENS node, for reverse records.
     * Defined in EIP181.
     * @param node The ENS node to query.
     * @return The associated name.
     */
    function name(bytes32 node) external view returns (string memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/IPubkeyResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IPubkeyResolver {
    event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);

    /**
     * Returns the SECP256k1 public key associated with an ENS node.
     * Defined in EIP 619.
     * @param node The ENS node to query
     * @return x The X coordinate of the curve point for the public key.
     * @return y The Y coordinate of the curve point for the public key.
     */
    function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/ITextResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface ITextResolver {
    event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);

    /**
     * Returns the text data associated with an ENS node and key.
     * @param node The ENS node to query.
     * @param key The text data key to query.
     * @return The associated text data.
     */
    function text(bytes32 node, string calldata key) external view returns (string memory);
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/InterfaceResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "../ISupportsInterface.sol";
import "./AddrResolver.sol";
import "./IInterfaceResolver.sol";

abstract contract InterfaceResolver is IInterfaceResolver, AddrResolver {
    mapping(bytes32=>mapping(bytes4=>address)) interfaces;

    /**
     * Sets an interface associated with a name.
     * Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support.
     * @param node The node to update.
     * @param interfaceID The EIP 165 interface ID.
     * @param implementer The address of a contract that implements this interface for this node.
     */
    function setInterface(bytes32 node, bytes4 interfaceID, address implementer) virtual external authorised(node) {
        interfaces[node][interfaceID] = implementer;
        emit InterfaceChanged(node, interfaceID, implementer);
    }

    /**
     * Returns the address of a contract that implements the specified interface for this name.
     * If an implementer has not been set for this interfaceID and name, the resolver will query
     * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that
     * contract implements EIP165 and returns `true` for the specified interfaceID, its address
     * will be returned.
     * @param node The ENS node to query.
     * @param interfaceID The EIP 165 interface ID to check for.
     * @return The address that implements this interface, or 0 if the interface is unsupported.
     */
    function interfaceImplementer(bytes32 node, bytes4 interfaceID) virtual override external view returns (address) {
        address implementer = interfaces[node][interfaceID];
        if(implementer != address(0)) {
            return implementer;
        }

        address a = addr(node);
        if(a == address(0)) {
            return address(0);
        }

        (bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", type(ISupportsInterface).interfaceId));
        if(!success || returnData.length < 32 || returnData[31] == 0) {
            // EIP 165 not supported by target
            return address(0);
        }

        (success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID));
        if(!success || returnData.length < 32 || returnData[31] == 0) {
            // Specified interface not supported by target
            return address(0);
        }

        return a;
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(IInterfaceResolver).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/NameResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "./INameResolver.sol";

abstract contract NameResolver is INameResolver, ResolverBase {
    mapping(bytes32=>string) names;

    /**
     * Sets the name associated with an ENS node, for reverse records.
     * May only be called by the owner of that node in the ENS registry.
     * @param node The node to update.
     */
    function setName(bytes32 node, string calldata newName) virtual external authorised(node) {
        names[node] = newName;
        emit NameChanged(node, newName);
    }

    /**
     * Returns the name associated with an ENS node, for reverse records.
     * Defined in EIP181.
     * @param node The ENS node to query.
     * @return The associated name.
     */
    function name(bytes32 node) virtual override external view returns (string memory) {
        return names[node];
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(INameResolver).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/PubkeyResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "./IPubkeyResolver.sol";

abstract contract PubkeyResolver is IPubkeyResolver, ResolverBase {
    struct PublicKey {
        bytes32 x;
        bytes32 y;
    }

    mapping(bytes32=>PublicKey) pubkeys;

    /**
     * Sets the SECP256k1 public key associated with an ENS node.
     * @param node The ENS node to query
     * @param x the X coordinate of the curve point for the public key.
     * @param y the Y coordinate of the curve point for the public key.
     */
    function setPubkey(bytes32 node, bytes32 x, bytes32 y) virtual external authorised(node) {
        pubkeys[node] = PublicKey(x, y);
        emit PubkeyChanged(node, x, y);
    }

    /**
     * Returns the SECP256k1 public key associated with an ENS node.
     * Defined in EIP 619.
     * @param node The ENS node to query
     * @return x The X coordinate of the curve point for the public key.
     * @return y The Y coordinate of the curve point for the public key.
     */
    function pubkey(bytes32 node) virtual override external view returns (bytes32 x, bytes32 y) {
        return (pubkeys[node].x, pubkeys[node].y);
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(IPubkeyResolver).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @ensdomains/ens-contracts/contracts/resolvers/profiles/TextResolver.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "../ResolverBase.sol";
import "./ITextResolver.sol";

abstract contract TextResolver is ITextResolver, ResolverBase {
    mapping(bytes32=>mapping(string=>string)) texts;

    /**
     * Sets the text data associated with an ENS node and key.
     * May only be called by the owner of that node in the ENS registry.
     * @param node The node to update.
     * @param key The key to set.
     * @param value The text data value to set.
     */
    function setText(bytes32 node, string calldata key, string calldata value) virtual external authorised(node) {
        texts[node][key] = value;
        emit TextChanged(node, key, key);
    }

    /**
     * Returns the text data associated with an ENS node and key.
     * @param node The ENS node to query.
     * @param key The text data key to query.
     * @return The associated text data.
     */
    function text(bytes32 node, string calldata key) virtual override external view returns (string memory) {
        return texts[node][key];
    }

    function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) {
        return interfaceID == type(ITextResolver).interfaceId || super.supportsInterface(interfaceID);
    }
}

// File: @gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxy.sol



// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

/// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
/// @author Richard Meissner - <richard@gnosis.io>
interface IProxy {
    function masterCopy() external view returns (address);
}

/// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
/// @author Stefan George - <stefan@gnosis.io>
/// @author Richard Meissner - <richard@gnosis.io>
contract GnosisSafeProxy {
    // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
    // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
    address internal singleton;

    /// @dev Constructor function sets address of singleton contract.
    /// @param _singleton Singleton address.
    constructor(address _singleton) {
        require(_singleton != address(0), "Invalid singleton address provided");
        singleton = _singleton;
    }

    /// @dev Fallback function forwards all transactions and returns all received return data.
    fallback() external payable {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
            // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
            if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                mstore(0, _singleton)
                return(0, 0x20)
            }
            calldatacopy(0, 0, calldatasize())
            let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            if eq(success, 0) {
                revert(0, returndatasize())
            }
            return(0, returndatasize())
        }
    }
}

// File: @gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol



// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

import "./GnosisSafeProxy.sol";
import "./IProxyCreationCallback.sol";

/// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @author Stefan George - <stefan@gnosis.pm>
contract GnosisSafeProxyFactory {
    event ProxyCreation(GnosisSafeProxy proxy, address singleton);

    /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
    /// @param singleton Address of singleton contract.
    /// @param data Payload for message call sent to new proxy contract.
    function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
        proxy = new GnosisSafeProxy(singleton);
        if (data.length > 0)
            // solhint-disable-next-line no-inline-assembly
            assembly {
                if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                    revert(0, 0)
                }
            }
        emit ProxyCreation(proxy, singleton);
    }

    /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
    function proxyRuntimeCode() public pure returns (bytes memory) {
        return type(GnosisSafeProxy).runtimeCode;
    }

    /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
    function proxyCreationCode() public pure returns (bytes memory) {
        return type(GnosisSafeProxy).creationCode;
    }

    /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
    ///      This method is only meant as an utility to be called from other methods
    /// @param _singleton Address of singleton contract.
    /// @param initializer Payload for message call sent to new proxy contract.
    /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
    function deployProxyWithNonce(
        address _singleton,
        bytes memory initializer,
        uint256 saltNonce
    ) internal returns (GnosisSafeProxy proxy) {
        // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
        bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
        bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
        // solhint-disable-next-line no-inline-assembly
        assembly {
            proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
        }
        require(address(proxy) != address(0), "Create2 call failed");
    }

    /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
    /// @param _singleton Address of singleton contract.
    /// @param initializer Payload for message call sent to new proxy contract.
    /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
    function createProxyWithNonce(
        address _singleton,
        bytes memory initializer,
        uint256 saltNonce
    ) public returns (GnosisSafeProxy proxy) {
        proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
        if (initializer.length > 0)
            // solhint-disable-next-line no-inline-assembly
            assembly {
                if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                    revert(0, 0)
                }
            }
        emit ProxyCreation(proxy, _singleton);
    }

    /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
    /// @param _singleton Address of singleton contract.
    /// @param initializer Payload for message call sent to new proxy contract.
    /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
    /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
    function createProxyWithCallback(
        address _singleton,
        bytes memory initializer,
        uint256 saltNonce,
        IProxyCreationCallback callback
    ) public returns (GnosisSafeProxy proxy) {
        uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
        proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
        if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
    }

    /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
    ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
    ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
    /// @param _singleton Address of singleton contract.
    /// @param initializer Payload for message call sent to new proxy contract.
    /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
    function calculateCreateProxyWithNonceAddress(
        address _singleton,
        bytes calldata initializer,
        uint256 saltNonce
    ) external returns (GnosisSafeProxy proxy) {
        proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
        revert(string(abi.encodePacked(proxy)));
    }
}

// File: @gnosis.pm/safe-contracts/contracts/proxies/IProxyCreationCallback.sol



// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./GnosisSafeProxy.sol";

interface IProxyCreationCallback {
    function proxyCreated(
        GnosisSafeProxy proxy,
        address _singleton,
        bytes calldata initializer,
        uint256 saltNonce
    ) external;
}

// File: @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// File: @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

// File: @openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, _allowances[owner][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = _allowances[owner][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Spend `amount` form the allowance of `owner` toward `spender`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}

// File: @openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: @openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// File: @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/proxy/Clones.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

// File: @openzeppelin/contracts/security/Pausable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// File: @openzeppelin/contracts/token/ERC20/ERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, _allowances[owner][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = _allowances[owner][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Spend `amount` form the allowance of `owner` toward `spender`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// File: @openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

// File: @openzeppelin/contracts/utils/Address.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @prb/math/contracts/PRBMath.sol



// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivFixedPointOverflow(uint256 prod1);

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);

/// @notice Emitted when one of the inputs is type(int256).min.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows int256.
error PRBMath__MulDivSignedOverflow(uint256 rAbs);

/// @notice Emitted when the input is MIN_SD59x18.
error PRBMathSD59x18__AbsInputTooSmall();

/// @notice Emitted when ceiling a number overflows SD59x18.
error PRBMathSD59x18__CeilOverflow(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__DivInputTooSmall();

/// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
error PRBMathSD59x18__DivOverflow(uint256 rAbs);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathSD59x18__ExpInputTooBig(int256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathSD59x18__Exp2InputTooBig(int256 x);

/// @notice Emitted when flooring a number underflows SD59x18.
error PRBMathSD59x18__FloorUnderflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMathSD59x18__FromIntOverflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMathSD59x18__FromIntUnderflow(int256 x);

/// @notice Emitted when the product of the inputs is negative.
error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);

/// @notice Emitted when multiplying the inputs overflows SD59x18.
error PRBMathSD59x18__GmOverflow(int256 x, int256 y);

/// @notice Emitted when the input is less than or equal to zero.
error PRBMathSD59x18__LogInputTooSmall(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__MulInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__MulOverflow(uint256 rAbs);

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__PowuOverflow(uint256 rAbs);

/// @notice Emitted when the input is negative.
error PRBMathSD59x18__SqrtNegativeInput(int256 x);

/// @notice Emitted when the calculating the square root overflows SD59x18.
error PRBMathSD59x18__SqrtOverflow(int256 x);

/// @notice Emitted when addition overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(uint256 x);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(uint256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(uint256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
error PRBMathUD60x18__FromUintOverflow(uint256 x);

/// @notice Emitted when multiplying the inputs overflows UD60x18.
error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);

/// @notice Emitted when the input is less than 1.
error PRBMathUD60x18__LogInputTooSmall(uint256 x);

/// @notice Emitted when the calculating the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(uint256 x);

/// @notice Emitted when subtraction underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);

/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
/// representation. When it does not, it is explicitly mentioned in the NatSpec documentation.
library PRBMath {
    /// STRUCTS ///

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @dev Largest power of two divisor of SCALE.
    uint256 internal constant SCALE_LPOTD = 262144;

    /// @dev SCALE inverted mod 2^256.
    uint256 internal constant SCALE_INVERSE =
        78156646155174841979727994598816262306175212592076161876661_508869554232690281;

    /// FUNCTIONS ///

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    /// @dev Has to use 192.64-bit fixed-point numbers.
    /// See https://ethereum.stackexchange.com/a/96594/24693.
    /// @param x The exponent as an unsigned 192.64-bit fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // Start from 0.5 in the 192.64-bit fixed-point format.
            result = 0x800000000000000000000000000000000000000000000000;

            // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
            // because the initial result is 2^191 and all magic factors are less than 2^65.
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }

            // We're doing two things at the same time:
            //
            //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
            //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
            //      rather than 192.
            //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
            //
            // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
            result *= SCALE;
            result >>= (191 - (x >> 64));
        }
    }

    /// @notice Finds the zero-based index of the first one in the binary representation of x.
    /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
    /// @param x The uint256 number for which to find the index of the most significant bit.
    /// @return msb The index of the most significant bit as an uint256.
    function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
        if (x >= 2**128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2**64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2**32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2**16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2**8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2**4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2**2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2**1) {
            // No need to shift x any more.
            msb += 1;
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2^256 + prod0.
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division.
        if (prod1 == 0) {
            unchecked {
                result = prod0 / denominator;
            }
            return result;
        }

        // Make sure the result is less than 2^256. Also prevents denominator == 0.
        if (prod1 >= denominator) {
            revert PRBMath__MulDivOverflow(prod1, denominator);
        }

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0].
        uint256 remainder;
        assembly {
            // Compute remainder using mulmod.
            remainder := mulmod(x, y, denominator)

            // Subtract 256 bit number from 512 bit number.
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
        // See https://cs.stackexchange.com/q/138556/92363.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

                // Divide [prod1 prod0] by lpotdod.
                prod0 := div(prod0, lpotdod)

                // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one.
                lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * lpotdod;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the 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.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /// @notice Calculates floor(x*y÷1e18) with full precision.
    ///
    /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
    /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
    /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
    ///
    /// Requirements:
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
    ///     1. x * y = type(uint256).max * SCALE
    ///     2. (x * y) % SCALE >= SCALE / 2
    ///
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (prod1 >= SCALE) {
            revert PRBMath__MulDivFixedPointOverflow(prod1);
        }

        uint256 remainder;
        uint256 roundUpUnit;
        assembly {
            remainder := mulmod(x, y, SCALE)
            roundUpUnit := gt(remainder, 499999999999999999)
        }

        if (prod1 == 0) {
            unchecked {
                result = (prod0 / SCALE) + roundUpUnit;
                return result;
            }
        }

        assembly {
            result := add(
                mul(
                    or(
                        div(sub(prod0, remainder), SCALE_LPOTD),
                        mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                    ),
                    SCALE_INVERSE
                ),
                roundUpUnit
            )
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - None of the inputs can be type(int256).min.
    /// - The result must fit within int256.
    ///
    /// @param x The multiplicand as an int256.
    /// @param y The multiplier as an int256.
    /// @param denominator The divisor as an int256.
    /// @return result The result as an int256.
    function mulDivSigned(
        int256 x,
        int256 y,
        int256 denominator
    ) internal pure returns (int256 result) {
        if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
            revert PRBMath__MulDivSignedInputTooSmall();
        }

        // Get hold of the absolute values of x, y and the denominator.
        uint256 ax;
        uint256 ay;
        uint256 ad;
        unchecked {
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);
            ad = denominator < 0 ? uint256(-denominator) : uint256(denominator);
        }

        // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
        uint256 rAbs = mulDiv(ax, ay, ad);
        if (rAbs > uint256(type(int256).max)) {
            revert PRBMath__MulDivSignedOverflow(rAbs);
        }

        // Get the signs of x, y and the denominator.
        uint256 sx;
        uint256 sy;
        uint256 sd;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
            sd := sgt(denominator, sub(0, 1))
        }

        // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
        // If yes, the result should be negative.
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The uint256 number for which to calculate the square root.
    /// @return result The result as an uint256.
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        // Set the initial guess to the least power of two that is greater than or equal to sqrt(x).
        uint256 xAux = uint256(x);
        result = 1;
        if (xAux >= 0x100000000000000000000000000000000) {
            xAux >>= 128;
            result <<= 64;
        }
        if (xAux >= 0x10000000000000000) {
            xAux >>= 64;
            result <<= 32;
        }
        if (xAux >= 0x100000000) {
            xAux >>= 32;
            result <<= 16;
        }
        if (xAux >= 0x10000) {
            xAux >>= 16;
            result <<= 8;
        }
        if (xAux >= 0x100) {
            xAux >>= 8;
            result <<= 4;
        }
        if (xAux >= 0x10) {
            xAux >>= 4;
            result <<= 2;
        }
        if (xAux >= 0x8) {
            result <<= 1;
        }

        // The operations can never overflow because the result is max 2^127 when it enters this block.
        unchecked {
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1; // Seven iterations should be enough
            uint256 roundedDownResult = x / result;
            return result >= roundedDownResult ? roundedDownResult : result;
        }
    }
}

// File: @prb/math/contracts/PRBMathUD60x18.sol



// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

import "./PRBMath.sol";

/// @title PRBMathUD60x18
/// @author Paul Razvan Berg
/// @notice Smart contract library for advanced fixed-point math that works with uint256 numbers considered to have 18
/// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60
/// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the
/// maximum values permitted by the Solidity type uint256.
library PRBMathUD60x18 {
    /// @dev Half the SCALE number.
    uint256 internal constant HALF_SCALE = 5e17;

    /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number.
    uint256 internal constant LOG2_E = 1_442695040888963407;

    /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_UD60x18 =
        115792089237316195423570985008687907853269984665640564039457_584007913129639935;

    /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_WHOLE_UD60x18 =
        115792089237316195423570985008687907853269984665640564039457_000000000000000000;

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @notice Calculates the arithmetic average of x and y, rounding down.
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // The operations can never overflow.
        unchecked {
            // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need
            // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice.
            result = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be less than or equal to MAX_WHOLE_UD60x18.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number to ceil.
    /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function ceil(uint256 x) internal pure returns (uint256 result) {
        if (x > MAX_WHOLE_UD60x18) {
            revert PRBMathUD60x18__CeilOverflow(x);
        }
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "SCALE - remainder" but faster.
            let delta := sub(SCALE, remainder)

            // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
            result := add(x, mul(delta, gt(remainder, 0)))
        }
    }

    /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number.
    ///
    /// @dev Uses mulDiv to enable overflow-safe multiplication and division.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    ///
    /// @param x The numerator as an unsigned 60.18-decimal fixed-point number.
    /// @param y The denominator as an unsigned 60.18-decimal fixed-point number.
    /// @param result The quotient as an unsigned 60.18-decimal fixed-point number.
    function div(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDiv(x, SCALE, y);
    }

    /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number.
    /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
    function e() internal pure returns (uint256 result) {
        result = 2_718281828459045235;
    }

    /// @notice Calculates the natural exponent of x.
    ///
    /// @dev Based on the insight that e^x = 2^(x * log2(e)).
    ///
    /// Requirements:
    /// - All from "log2".
    /// - x must be less than 133.084258667509499441.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp(uint256 x) internal pure returns (uint256 result) {
        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133_084258667509499441) {
            revert PRBMathUD60x18__ExpInputTooBig(x);
        }

        // Do the fixed-point multiplication inline to save gas.
        unchecked {
            uint256 doubleScaleProduct = x * LOG2_E;
            result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
        }
    }

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    ///
    /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
    ///
    /// Requirements:
    /// - x must be 192 or less.
    /// - The result must fit within MAX_UD60x18.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
        if (x >= 192e18) {
            revert PRBMathUD60x18__Exp2InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x192x64 = (x << 64) / SCALE;

            // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation.
            result = PRBMath.exp2(x192x64);
        }
    }

    /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x.
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    /// @param x The unsigned 60.18-decimal fixed-point number to floor.
    /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function floor(uint256 x) internal pure returns (uint256 result) {
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
            result := sub(x, mul(remainder, gt(remainder, 0)))
        }
    }

    /// @notice Yields the excess beyond the floor of x.
    /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part.
    /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of.
    /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number.
    function frac(uint256 x) internal pure returns (uint256 result) {
        assembly {
            result := mod(x, SCALE)
        }
    }

    /// @notice Converts a number from basic integer form to unsigned 60.18-decimal fixed-point representation.
    ///
    /// @dev Requirements:
    /// - x must be less than or equal to MAX_UD60x18 divided by SCALE.
    ///
    /// @param x The basic integer to convert.
    /// @param result The same number in unsigned 60.18-decimal fixed-point representation.
    function fromUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__FromUintOverflow(x);
            }
            result = x * SCALE;
        }
    }

    /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
    ///
    /// @dev Requirements:
    /// - x * y must fit within MAX_UD60x18, lest it overflows.
    ///
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function gm(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        unchecked {
            // Checking for overflow this way is faster than letting Solidity do it.
            uint256 xy = x * y;
            if (xy / x != y) {
                revert PRBMathUD60x18__GmOverflow(x, y);
            }

            // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
            // during multiplication. See the comments within the "sqrt" function.
            result = PRBMath.sqrt(xy);
        }
    }

    /// @notice Calculates 1 / x, rounding toward zero.
    ///
    /// @dev Requirements:
    /// - x cannot be zero.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse.
    /// @return result The inverse as an unsigned 60.18-decimal fixed-point number.
    function inv(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // 1e36 is SCALE * SCALE.
            result = 1e36 / x;
        }
    }

    /// @notice Calculates the natural logarithm of x.
    ///
    /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    /// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm.
    /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number.
    function ln(uint256 x) internal pure returns (uint256 result) {
        // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
        // can return is 196205294292027477728.
        unchecked {
            result = (log2(x) * SCALE) / LOG2_E;
        }
    }

    /// @notice Calculates the common logarithm of x.
    ///
    /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
    /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm.
    /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number.
    function log10(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }

        // Note that the "mul" in this block is the assembly multiplication operation, not the "mul" function defined
        // in this contract.
        // prettier-ignore
        assembly {
            switch x
            case 1 { result := mul(SCALE, sub(0, 18)) }
            case 10 { result := mul(SCALE, sub(1, 18)) }
            case 100 { result := mul(SCALE, sub(2, 18)) }
            case 1000 { result := mul(SCALE, sub(3, 18)) }
            case 10000 { result := mul(SCALE, sub(4, 18)) }
            case 100000 { result := mul(SCALE, sub(5, 18)) }
            case 1000000 { result := mul(SCALE, sub(6, 18)) }
            case 10000000 { result := mul(SCALE, sub(7, 18)) }
            case 100000000 { result := mul(SCALE, sub(8, 18)) }
            case 1000000000 { result := mul(SCALE, sub(9, 18)) }
            case 10000000000 { result := mul(SCALE, sub(10, 18)) }
            case 100000000000 { result := mul(SCALE, sub(11, 18)) }
            case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
            case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
            case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
            case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
            case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
            case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
            case 1000000000000000000 { result := 0 }
            case 10000000000000000000 { result := SCALE }
            case 100000000000000000000 { result := mul(SCALE, 2) }
            case 1000000000000000000000 { result := mul(SCALE, 3) }
            case 10000000000000000000000 { result := mul(SCALE, 4) }
            case 100000000000000000000000 { result := mul(SCALE, 5) }
            case 1000000000000000000000000 { result := mul(SCALE, 6) }
            case 10000000000000000000000000 { result := mul(SCALE, 7) }
            case 100000000000000000000000000 { result := mul(SCALE, 8) }
            case 1000000000000000000000000000 { result := mul(SCALE, 9) }
            case 10000000000000000000000000000 { result := mul(SCALE, 10) }
            case 100000000000000000000000000000 { result := mul(SCALE, 11) }
            case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
            case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
            case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
            case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
            case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
            case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
            case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
            case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
            case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
            case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
            case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
            case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
            case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
            case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
            case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
            case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
            case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
            case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
            case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
            case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
            case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
            case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
            case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
            case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
            case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
            case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
            case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
            case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
            case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
            case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
            case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
            case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
            case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
            case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
            case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
            case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
            case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
            case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
            case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) }
            default {
                result := MAX_UD60x18
            }
        }

        if (result == MAX_UD60x18) {
            // Do the fixed-point division inline to save gas. The denominator is log2(10).
            unchecked {
                result = (log2(x) * SCALE) / 3_321928094887362347;
            }
        }
    }

    /// @notice Calculates the binary logarithm of x.
    ///
    /// @dev Based on the iterative approximation algorithm.
    /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
    ///
    /// Requirements:
    /// - x must be greater than or equal to SCALE, otherwise the result would be negative.
    ///
    /// Caveats:
    /// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm.
    /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number.
    function log2(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }
        unchecked {
            // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
            uint256 n = PRBMath.mostSignificantBit(x / SCALE);

            // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow
            // because n is maximum 255 and SCALE is 1e18.
            result = n * SCALE;

            // This is y = x * 2^(-n).
            uint256 y = x >> n;

            // If y = 1, the fractional part is zero.
            if (y == SCALE) {
                return result;
            }

            // Calculate the fractional part via the iterative approximation.
            // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
            for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) {
                y = (y * y) / SCALE;

                // Is y^2 > 2 and so in the range [2,4)?
                if (y >= 2 * SCALE) {
                    // Add the 2^(-m) factor to the logarithm.
                    result += delta;

                    // Corresponds to z/2 on Wikipedia.
                    y >>= 1;
                }
            }
        }
    }

    /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal
    /// fixed-point number.
    /// @dev See the documentation for the "PRBMath.mulDivFixedPoint" function.
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The product as an unsigned 60.18-decimal fixed-point number.
    function mul(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDivFixedPoint(x, y);
    }

    /// @notice Returns PI as an unsigned 60.18-decimal fixed-point number.
    function pi() internal pure returns (uint256 result) {
        result = 3_141592653589793238;
    }

    /// @notice Raises x to the power of y.
    ///
    /// @dev Based on the insight that x^y = 2^(log2(x) * y).
    ///
    /// Requirements:
    /// - All from "exp2", "log2" and "mul".
    ///
    /// Caveats:
    /// - All from "exp2", "log2" and "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number.
    /// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number.
    /// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number.
    function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            result = y == 0 ? SCALE : uint256(0);
        } else {
            result = exp2(mul(log2(x), y));
        }
    }

    /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
    /// famous algorithm "exponentiation by squaring".
    ///
    /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
    ///
    /// Requirements:
    /// - The result must fit within MAX_UD60x18.
    ///
    /// Caveats:
    /// - All from "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x The base as an unsigned 60.18-decimal fixed-point number.
    /// @param y The exponent as an uint256.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // Calculate the first iteration of the loop in advance.
        result = y & 1 > 0 ? x : SCALE;

        // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
        for (y >>= 1; y > 0; y >>= 1) {
            x = PRBMath.mulDivFixedPoint(x, x);

            // Equivalent to "y % 2 == 1" but faster.
            if (y & 1 > 0) {
                result = PRBMath.mulDivFixedPoint(result, x);
            }
        }
    }

    /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number.
    function scale() internal pure returns (uint256 result) {
        result = SCALE;
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Requirements:
    /// - x must be less than MAX_UD60x18 / SCALE.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root.
    /// @return result The result as an unsigned 60.18-decimal fixed-point .
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__SqrtOverflow(x);
            }
            // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned
            // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
            result = PRBMath.sqrt(x * SCALE);
        }
    }

    /// @notice Converts a unsigned 60.18-decimal fixed-point number to basic integer form, rounding down in the process.
    /// @param x The unsigned 60.18-decimal fixed-point number to convert.
    /// @return result The same number in basic integer form.
    function toUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            result = x / SCALE;
        }
    }
}

// File: @reality.eth/contracts/development/contracts/IBalanceHolder.sol



// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.10;

interface IBalanceHolder {
  function withdraw (  ) external;
  function balanceOf ( address ) external view returns ( uint256 );
}

// File: @reality.eth/contracts/development/contracts/IRealityETH.sol



// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.10;

import './IBalanceHolder.sol';

interface IRealityETH is IBalanceHolder {
     event LogAnswerReveal (bytes32 indexed question_id, address indexed user, bytes32 indexed answer_hash, bytes32 answer, uint256 nonce, uint256 bond);
     event LogCancelArbitration (bytes32 indexed question_id);
     event LogClaim (bytes32 indexed question_id, address indexed user, uint256 amount);
     event LogFinalize (bytes32 indexed question_id, bytes32 indexed answer);
     event LogFundAnswerBounty (bytes32 indexed question_id, uint256 bounty_added, uint256 bounty, address indexed user);
     event LogMinimumBond (bytes32 indexed question_id, uint256 min_bond);
     event LogNewAnswer (bytes32 answer, bytes32 indexed question_id, bytes32 history_hash, address indexed user, uint256 bond, uint256 ts, bool is_commitment);
     event LogNewQuestion (bytes32 indexed question_id, address indexed user, uint256 template_id, string question, bytes32 indexed content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 created);
     event LogNewTemplate (uint256 indexed template_id, address indexed user, string question_text);
     event LogNotifyOfArbitrationRequest (bytes32 indexed question_id, address indexed user);
     event LogReopenQuestion (bytes32 indexed question_id, bytes32 indexed reopened_question_id);
     event LogSetQuestionFee (address arbitrator, uint256 amount);

     function assignWinnerAndSubmitAnswerByArbitrator (bytes32 question_id, bytes32 answer, address payee_if_wrong, bytes32 last_history_hash, bytes32 last_answer_or_commitment_id, address last_answerer) external;
     function cancelArbitration (bytes32 question_id) external;
     function claimMultipleAndWithdrawBalance (bytes32[] calldata question_ids, uint256[] calldata lengths, bytes32[] calldata hist_hashes, address[] calldata addrs, uint256[] calldata bonds, bytes32[] calldata answers) external;
     function claimWinnings (bytes32 question_id, bytes32[] calldata history_hashes, address[] calldata addrs, uint256[] calldata bonds, bytes32[] calldata answers) external;
     function createTemplate (string calldata content) external returns (uint256);
     function notifyOfArbitrationRequest (bytes32 question_id, address requester, uint256 max_previous) external;
     function setQuestionFee (uint256 fee) external;
     function submitAnswerByArbitrator (bytes32 question_id, bytes32 answer, address answerer) external;
     function submitAnswerReveal (bytes32 question_id, bytes32 answer, uint256 nonce, uint256 bond) external;
     function askQuestion (uint256 template_id, string calldata question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce) external payable returns (bytes32);
     function askQuestionWithMinBond (uint256 template_id, string calldata question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond) external payable returns (bytes32);
     function createTemplateAndAskQuestion (string calldata content, string calldata question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce) external payable returns (bytes32);
     function fundAnswerBounty (bytes32 question_id) external payable;
     function reopenQuestion (uint256 template_id, string calldata question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond, bytes32 reopens_question_id) external payable returns (bytes32);
     function submitAnswer (bytes32 question_id, bytes32 answer, uint256 max_previous) external payable;
     function submitAnswerCommitment (bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer) external payable;
     function submitAnswerFor (bytes32 question_id, bytes32 answer, uint256 max_previous, address answerer) external payable;
     function arbitrator_question_fees (address) external view returns (uint256);
     function commitments (bytes32) external view returns (uint32 reveal_ts, bool is_revealed, bytes32 revealed_answer);
     function getArbitrator (bytes32 question_id) external view returns (address);
     function getBestAnswer (bytes32 question_id) external view returns (bytes32);
     function getBond (bytes32 question_id) external view returns (uint256);
     function getBounty (bytes32 question_id) external view returns (uint256);
     function getContentHash (bytes32 question_id) external view returns (bytes32);
     function getFinalAnswer (bytes32 question_id) external view returns (bytes32);
     function getFinalAnswerIfMatches (bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 min_timeout, uint256 min_bond) external view returns (bytes32);
     function getFinalizeTS (bytes32 question_id) external view returns (uint32);
     function getHistoryHash (bytes32 question_id) external view returns (bytes32);
     function getMinBond (bytes32 question_id) external view returns (uint256);
     function getOpeningTS (bytes32 question_id) external view returns (uint32);
     function getTimeout (bytes32 question_id) external view returns (uint32);
     function isFinalized (bytes32 question_id) external view returns (bool);
     function isPendingArbitration (bytes32 question_id) external view returns (bool);
     function isSettledTooSoon (bytes32 question_id) external view returns (bool);
     function question_claims (bytes32) external view returns (address payee, uint256 last_bond, uint256 queued_funds);
     function questions (bytes32) external view returns (bytes32 content_hash, address arbitrator, uint32 opening_ts, uint32 timeout, uint32 finalize_ts, bool is_pending_arbitration, uint256 bounty, bytes32 best_answer, bytes32 history_hash, uint256 bond, uint256 min_bond);
     function reopened_questions (bytes32) external view returns (bytes32);
     function reopener_questions (bytes32) external view returns (bool);
     function resultFor (bytes32 question_id) external view returns (bytes32);
     function resultForOnceSettled (bytes32 question_id) external view returns (bytes32);
     function template_hashes (uint256) external view returns (bytes32);
     function templates (uint256) external view returns (uint256);
}

// File: contracts/implementations/ClearingHouseV2.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@prb/math/contracts/PRBMathUD60x18.sol";

import "../interfaces/IClearingHouseV2.sol";
import "../library/SigRecovery.sol";

contract ClearingHouseV2 is IClearingHouseV2, Ownable, Pausable {
  using SafeERC20 for ERC20;
  using SafeERC20 for IWETH;
  using PRBMathUD60x18 for uint256;

  //###############
  //#### STATE ####
  //Cause token address => CauseInformation
  mapping(ERC20Singleton => CauseInformation) public override causeInformation;
  //Cause ID => KYC ID => Running total of amount per user (by KYC ID)
  mapping(uint256 => mapping(bytes => uint256)) public override withdrawnAmount;
  //Message hash => boolean. To track if a message has been used for purchase previously
  mapping(bytes32 => bool) public override usedSignatures;

  DonationsRouter public override donationsRouter;
  BuyInTokenData public override buyInToken;
  StakingRewards public override staking;
  IWETH public override WETH;
  address public override zeroXSwapTarget;
  address public override governor;

  // A backend wallet that can generate valid signatures for KYC.
  // This address should be used to generate KYC signatures.
  address public KYCAuthenticatedSigner;

  constructor(
    address _owner,
    address _donationsRouter,
    address _buyInToken,
    address _staking,
    address _WETH,
    address _swapTarget
  ) {
    _checkZeroAddress(_owner);
    _checkZeroAddress(_donationsRouter);
    _checkZeroAddress(_buyInToken);
    _checkZeroAddress(_staking);
    _checkZeroAddress(_WETH);
    _checkZeroAddress(_swapTarget);

    donationsRouter = DonationsRouter(_donationsRouter);
    buyInToken = BuyInTokenData({
      tokenAddress: ERC20(_buyInToken),
      decimals: ERC20(_buyInToken).decimals()
    });
    staking = StakingRewards(_staking);
    WETH = IWETH(_WETH);
    zeroXSwapTarget = _swapTarget;
    _transferOwnership(_owner);
  }

  receive() external payable {}

  //############################
  //#### INTERNAL FUNCTIONS ####

  function _checkZeroAddress(address _address) internal pure {
    if (_address == address(0)) {
      revert CannotBeZeroAddress();
    }
  }

  function _checkIfCauseOwner(ERC20Singleton _childDaoToken) internal view {
    (address causeOwner, , , ) = donationsRouter.causeRecords(
      donationsRouter.tokenCauseIds(address(_childDaoToken))
    );
    if (msg.sender != causeOwner) {
      revert AccountNotDaoOwner();
    }
  }

  function _verifySignature(
    bytes memory _KYCId,
    address _user,
    uint256 _causeId,
    uint256 _expiry,
    bytes memory _signature
  ) internal {
    if (block.timestamp > _expiry) {
      revert ApprovalExpired();
    }
    (address messageSigner, bytes32 messageHash) = SigRecovery.recoverApproval(
      _KYCId,
      _user,
      _causeId,
      _expiry,
      _signature
    );
    if (messageSigner != KYCAuthenticatedSigner) {
      revert InvalidSignature();
    }
    if (usedSignatures[messageHash]) {
      revert InvalidSignature();
    }
    usedSignatures[messageHash] = true;
  }

  function _checkChildDaoRegistered(uint256 _causeId) internal pure {
    if (_causeId == 0) {
      revert ChildDaoNotRegistered();
    }
  }

  function _checkReleaseStarted(uint256 _release) internal view {
    if (block.timestamp < _release) {
      revert ChildDaoReleaseNotStarted();
    }
  }

  function _checkMaxPerUser(
    uint256 _amount,
    uint256 _maxPerUser,
    uint256 _withdrawnAmount
  ) internal view {
    if (_withdrawnAmount + _amount > _maxPerUser && msg.sender != owner()) {
      revert UserAmountExceeded();
    }
  }

  function _mintDaoToken(
    uint256 _daoAmount,
    ERC20Singleton _childDaoToken,
    bool _autoStaking
  ) internal {
    if (_autoStaking) {
      _childDaoToken.mint(address(this), _daoAmount);
      staking.stakeOnBehalf(msg.sender, address(_childDaoToken), _daoAmount);
    }
    _childDaoToken.mint(msg.sender, _daoAmount);
  }

  function _transferBuyInToken(
    uint256 _daoAmount,
    uint256 _daoExchangeRate,
    address _treasuryAddress,
    BuyInTokenData memory _buyInToken,
    bool _fromSwap
  ) internal {
    uint256 buyAmount;
    if (_buyInToken.decimals < 18) {
      buyAmount = _daoAmount.mul(_daoExchangeRate).mul(
        10**_buyInToken.decimals
      );
    } else {
      buyAmount = _daoAmount.mul(_daoExchangeRate);
    }
    if (_fromSwap) {
      _buyInToken.tokenAddress.safeTransfer(_treasuryAddress, buyAmount);
    } else {
      _buyInToken.tokenAddress.safeTransferFrom(
        msg.sender,
        _treasuryAddress,
        buyAmount
      );
    }
  }

  function _returnExtraBuy(SwapData calldata _swapData, uint256 _amountBought)
    internal
  {
    if (_amountBought > _swapData.buyAmount) {
      _swapData.buyToken.safeTransfer(
        msg.sender,
        _amountBought - _swapData.buyAmount
      );
    }
  }

  function _callZeroXSwap(SwapData calldata _swapData)
    internal
    returns (uint256 amountBought)
  {
    if (_swapData.swapTarget != zeroXSwapTarget) {
      revert WrongSwapTarget();
    }
    (bool success, bytes memory returnData) = _swapData.swapTarget.call(
      _swapData.swapTxData
    );
    if (!success) {
      revert ZeroXSwapFailed();
    }
    amountBought = abi.decode(returnData, ((uint256)));
  }

  function _swapToken(SwapData calldata _swapData) internal {
    _swapData.sellToken.safeTransferFrom(
      msg.sender,
      address(this),
      _swapData.sellAmount
    );
    uint256 sellBalanceBefore = _swapData.sellToken.balanceOf(address(this)) -
      _swapData.sellAmount;
    _swapData.sellToken.approve(_swapData.swapTarget, type(uint256).max);
    uint256 amountBought = _callZeroXSwap(_swapData);
    uint256 sellBalanceAfter = _swapData.sellToken.balanceOf(address(this));
    if (sellBalanceAfter > 0) {
      _swapData.sellToken.safeTransfer(
        msg.sender,
        sellBalanceAfter - sellBalanceBefore
      );
    }
    _returnExtraBuy(_swapData, amountBought);
  }

  function _swapETH(SwapData calldata _swapData) internal {
    uint256 sellBalanceBefore = WETH.balanceOf(address(this));
    WETH.deposit{value: msg.value}();
    WETH.approve(_swapData.swapTarget, type(uint256).max);

    uint256 amountBought = _callZeroXSwap(_swapData);
    uint256 sellBalanceAfter = _swapData.sellToken.balanceOf(address(this));

    if (sellBalanceAfter > 0) {
      WETH.withdraw(sellBalanceAfter);

      (bool transferSuccess, ) = msg.sender.call{
        value: sellBalanceAfter - sellBalanceBefore
      }("");

      if (!transferSuccess) {
        revert EthTransferFailed();
      }
    }
    _returnExtraBuy(_swapData, amountBought);
  }

  function _checkInvariants(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes calldata _KYCId,
    uint256 _expiry,
    bytes memory _signature
  )
    internal
    returns (
      CauseInformation memory causeInfo,
      address defaultWallet,
      address owner
    )
  {
    uint256 causeId = donationsRouter.tokenCauseIds(address(_childDaoToken));

    _checkChildDaoRegistered(causeId);

    causeInfo = causeInformation[_childDaoToken];
    if (causeInfo.paused) {
      revert CausePaused();
    }
    _checkReleaseStarted(causeInfo.release);

    uint256 withdrawnTotal = withdrawnAmount[causeId][_KYCId];
    if (causeInfo.kycEnabled) {
      _verifySignature(_KYCId, msg.sender, causeId, _expiry, _signature);
      _checkMaxPerUser(_amount, causeInfo.maxPerUser, withdrawnTotal);
    } else {
      _checkMaxPerUser(_amount, causeInfo.maxPerUser, 0);
    }
    withdrawnAmount[causeId][_KYCId] = withdrawnTotal + _amount;

    (owner, defaultWallet, , ) = donationsRouter.causeRecords(causeId);
  }

  function _normaliseExchangeRate(uint256 _rate)
    internal
    view
    returns (uint256 normalisedRate)
  {
    BuyInTokenData memory buyInTokenData = buyInToken;
    if (buyInTokenData.decimals < 18) {
      normalisedRate = (_rate * 10**(18 - buyInTokenData.decimals));
    } else {
      normalisedRate = _rate;
    }
  }

  function _checkIfGovernor() internal view {
    if (msg.sender != governor) {
      revert AccountNotGovernor();
    }
  }

  //###################
  //#### MODIFIERS ####

  modifier onlyCauseOwner(ERC20Singleton _childDaoToken) {
    _checkIfCauseOwner(_childDaoToken);
    _;
  }

  modifier onlyGovernor() {
    _checkIfGovernor();
    _;
  }

  //###################
  //#### FUNCTIONS ####

  function registerChildDao(
    ERC20Singleton _childDaoToken,
    bool _autoStaking,
    bool _kycEnabled,
    uint256 _maxSupply,
    uint256 _maxSwap,
    uint256 _release,
    uint256 _exchangeRate
  ) external override onlyGovernor whenNotPaused {
    causeInformation[_childDaoToken] = CauseInformation({
      release: _release,
      maxSupply: _maxSupply,
      maxPerUser: _maxSwap,
      exchangeRate: _normaliseExchangeRate(_exchangeRate),
      childDaoRegistry: true,
      autoStaking: _autoStaking,
      kycEnabled: _kycEnabled,
      paused: false
    });

    emit ChildDaoRegistered(address(_childDaoToken));
    _childDaoToken.approve(address(staking), type(uint256).max);
  }

  function purchaseToken(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes calldata _KYCId,
    uint256 _expiry,
    bytes memory _signature
  ) external override whenNotPaused {
    (CauseInformation memory causeInfo, , address owner) = _checkInvariants(
      _childDaoToken,
      _amount,
      _KYCId,
      _expiry,
      _signature
    );
    _mintDaoToken(_amount, _childDaoToken, causeInfo.autoStaking);
    BuyInTokenData memory buyInTokenData = buyInToken;
    _transferBuyInToken(
      _amount,
      causeInfo.exchangeRate,
      owner,
      buyInTokenData,
      false
    );
    emit DaoTokenPurchased(_amount, msg.sender, causeInfo.autoStaking);
  }

  function swapAndPurchaseToken(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes calldata _KYCId,
    uint256 _expiry,
    bytes memory _signature,
    SwapData calldata _swapData
  ) external override whenNotPaused {
    BuyInTokenData memory buyInTokenData = buyInToken;

    (
      CauseInformation memory causeInfo,
      address defaultWallet,

    ) = _checkInvariants(_childDaoToken, _amount, _KYCId, _expiry, _signature);
    _swapToken(_swapData);
    _mintDaoToken(_amount, _childDaoToken, causeInfo.autoStaking);
    _transferBuyInToken(
      _amount,
      causeInfo.exchangeRate,
      defaultWallet,
      buyInTokenData,
      true
    );
    emit DaoTokenPurchased(_amount, msg.sender, causeInfo.autoStaking);
  }

  function swapETHAndPurchaseToken(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes calldata _KYCId,
    uint256 _expiry,
    bytes memory _signature,
    SwapData calldata _swapData
  ) external payable override whenNotPaused {
    BuyInTokenData memory buyInTokenData = buyInToken;

    if (_swapData.buyToken != buyInTokenData.tokenAddress) {
      revert WrongBuyToken();
    }
    if (address(_swapData.sellToken) != address(WETH)) {
      revert WrongSellToken();
    }
    (
      CauseInformation memory causeInfo,
      address defaultWallet,

    ) = _checkInvariants(_childDaoToken, _amount, _KYCId, _expiry, _signature);
    _swapETH(_swapData);
    _mintDaoToken(_amount, _childDaoToken, causeInfo.autoStaking);
    _transferBuyInToken(
      _amount,
      causeInfo.exchangeRate,
      defaultWallet,
      buyInTokenData,
      true
    );
    emit DaoTokenPurchased(_amount, msg.sender, causeInfo.autoStaking);
  }

  //##########################
  //#### SETTER FUNCTIONS ####

  function setAutoStake(ERC20Singleton _childDaoToken, bool _state)
    external
    override
    onlyCauseOwner(_childDaoToken)
  {
    causeInformation[_childDaoToken].autoStaking = _state;
  }

  function enableKyc(ERC20Singleton _childDaoToken)
    external
    override
    onlyCauseOwner(_childDaoToken)
  {
    causeInformation[_childDaoToken].kycEnabled = true;
  }

  function setMaxPerUser(ERC20Singleton _childDaoToken, uint256 _max)
    external
    override
    onlyCauseOwner(_childDaoToken)
  {
    causeInformation[_childDaoToken].maxPerUser = _max;
  }

  function setExchangeRate(ERC20Singleton _childDaoToken, uint256 _rate)
    external
    override
    onlyCauseOwner(_childDaoToken)
  {
    causeInformation[_childDaoToken].exchangeRate = _normaliseExchangeRate(
      _rate
    );
  }

  function pauseCause(ERC20Singleton _childDaoToken)
    external
    override
    onlyCauseOwner(_childDaoToken)
  {
    causeInformation[_childDaoToken].paused = true;
  }

  function unpauseCause(ERC20Singleton _childDaoToken)
    external
    override
    onlyCauseOwner(_childDaoToken)
  {
    causeInformation[_childDaoToken].paused = false;
  }

  function setDonationsRouter(DonationsRouter _implementation)
    external
    override
    onlyOwner
  {
    _checkZeroAddress(address(_implementation));
    donationsRouter = _implementation;
  }

  function setBuyInToken(ERC20 _implementation) external override onlyOwner {
    _checkZeroAddress(address(_implementation));
    buyInToken = BuyInTokenData({
      tokenAddress: _implementation,
      decimals: _implementation.decimals()
    });
  }

  function setAuthenticatedKYCSigner(address _newSigner)
    external
    override
    onlyOwner
  {
    _checkZeroAddress(_newSigner);
    KYCAuthenticatedSigner = _newSigner;
  }

  function setStakingRewards(StakingRewards _implementation)
    external
    override
    onlyOwner
  {
    _checkZeroAddress(address(_implementation));
    staking = _implementation;
  }

  function setSwapTarget(address _implementation) external override onlyOwner {
    _checkZeroAddress(_implementation);
    zeroXSwapTarget = _implementation;
  }

  function setGovernor(address _implementation) external onlyOwner {
    _checkZeroAddress(_implementation);
    governor = _implementation;
  }

  function pause() external override onlyOwner {
    _pause();
  }

  function unpause() external override onlyOwner {
    _unpause();
  }
}

// File: contracts/implementations/DaoResellQueue.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "../interfaces/IDaoResellQueue.sol";

contract DaoResellQueue is IDaoResellQueue {
  mapping(address => uint32) public override causeSplitId;
  mapping(uint32 => uint256) public override split;
  mapping(address => mapping(uint256 => SaleItem)) public override saleItems;
  mapping(address => CumulativeTotals) public override cumulativeTotals;
  uint256 public override saleItemCounter;

  address public override clearingHouse;

  constructor(address _clearingHouse) {
    _checkZeroAddress(_clearingHouse);
    clearingHouse = _clearingHouse;
  }

  function _checkZeroAddress(address _address) internal pure {
    if (_address == address(0)) {
      revert CannotBeZeroAddress();
    }
  }

  function _incremenetSaleItemCounter()
    internal
    returns (uint256 incrementedCount)
  {}

  function enqueue(address _causeToken, uint128 _amount) external override {}

  function dequeue(address _causeToken, uint128 _saleId) external override {}

  function purchaseAvailable(address _causeToken, uint256 _amount)
    external
    override
    returns (PurchaseItem[] memory purchaseItems)
  {}

  function forceDequeue(address _causeToken, uint128[] calldata _listingIds)
    external
    override
  {}

  function setSaleSplit(address _causeToken, uint256 _splitAmount)
    external
    override
  {}

  function validateProceedsClaim(address _causeToken, uint256 _saleItemId)
    external
    override
  {}
}

// File: contracts/implementations/DonationsRouter.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@prb/math/contracts/PRBMathUD60x18.sol";
import "../interfaces/IDonationsRouter.sol";
import "../interfaces/IStakingRewards.sol";
import "../interfaces/IThinWallet.sol";
import "./Queue.sol";

contract DonationsRouter is IDonationsRouter, Ownable, Queue {
  using PRBMathUD60x18 for uint256;
  using SafeERC20 for IERC20;

  ERC20 public immutable override baseToken;
  IStakingRewards public immutable override stakingContract;

  address public immutable override walletImplementation;

  uint256 public override causeId;
  uint256 public override platformFee;

  /// Cause ID => Cause record
  mapping(uint256 => CauseRecord) public override causeRecords;

  /// Token => CauseID
  mapping(address => uint256) public override tokenCauseIds;

  /// Thin wallet salt => thin wallet address
  mapping(bytes32 => address) public override deployedWallets;
  // keccak(owner, token) => is registered
  mapping(bytes32 => bool) public isRegistered;

  constructor(
    address _baseToken,
    address _stakingContract,
    address _owner,
    address _walletImplementation
  ) {
    require(_baseToken != address(0), "invalid base token");
    require(_stakingContract != address(0), "invalid staking contract");
    require(_owner != address(0), "invalid owner");
    require(_walletImplementation != address(0), "invalid implementation");

    baseToken = ERC20(_baseToken);
    stakingContract = IStakingRewards(_stakingContract);
    walletImplementation = _walletImplementation;

    _transferOwnership(_owner);
  }

  function setPlatformFee(uint256 _fee) external override onlyOwner {
    emit UpdateFee(platformFee, _fee);
    platformFee = _fee;
  }

  function registerCause(CauseRegistrationRequest calldata _cause)
    external
    override
  {
    require(_cause.owner != address(0), "invalid owner");
    require(_cause.daoToken != address(0), "invalid token");

    bytes32 causeRegistrationHash = keccak256(
      abi.encode(_cause.owner, _cause.daoToken)
    );
    require(!isRegistered[causeRegistrationHash], "cause exists");
    isRegistered[causeRegistrationHash] = true;

    uint256 id = ++causeId; // Increments then returns, thus causeId starts at 1

    CauseRecord memory cause = CauseRecord({
      owner: _cause.owner,
      rewardPercentage: _cause.rewardPercentage,
      daoToken: _cause.daoToken,
      defaultWallet: calculateThinWallet(
        ThinWalletID({causeId: id, thinWalletId: abi.encode(id)})
      )
    });

    causeRecords[id] = cause;

    emit RegisterCause(_cause.owner, _cause.daoToken, id);

    tokenCauseIds[_cause.daoToken] = id;
    address[] memory owners = new address[](1);
    owners[0] = _cause.owner;
    _deployWallet(
      _getSalt(ThinWalletID({causeId: id, thinWalletId: abi.encode(id)})),
      owners
    );

    ERC20(_cause.daoToken).approve(address(stakingContract), type(uint256).max);
  }

  function updateCause(uint256 _causeId, CauseUpdateRequest calldata _cause)
    external
    override
  {
    require(_causeId <= causeId, "invalid cause");
    CauseRecord memory cause = causeRecords[_causeId];
    require(msg.sender == cause.owner, "not authorized");
    require(_cause.owner != address(0), "invalid owner");

    cause.owner = _cause.owner;
    cause.rewardPercentage = _cause.rewardPercentage;

    causeRecords[_causeId] = cause;

    emit UpdateCause(cause);
  }

  function calculateThinWallet(ThinWalletID memory _walletId)
    public
    view
    override
    returns (address wallet)
  {
    wallet = Clones.predictDeterministicAddress(
      walletImplementation,
      _getSalt(_walletId)
    );
  }

  function registerThinWallet(
    ThinWalletID calldata _walletId,
    address[] calldata _owners
  ) external override {
    bytes32 salt = _getSalt(_walletId);
    require(deployedWallets[salt] == address(0), "already deployed");
    require(_owners.length >= 1, "invalid owners");
    CauseRecord memory cause = causeRecords[_walletId.causeId];

    require(_walletId.causeId <= causeId, "invalid cause");
    require(msg.sender == cause.owner, "unauthorized");

    address wallet = calculateThinWallet(_walletId);

    emit RegisterWallet(wallet, _walletId);

    _deployWallet(salt, _owners);
  }

  function withdrawFromThinWallet(
    ThinWalletID calldata _walletId,
    WithdrawalRequest calldata _withdrawal,
    bytes32 _proposalId
  ) external override {
    require(_walletId.causeId <= causeId, "invalid cause");
    CauseRecord memory cause = causeRecords[_walletId.causeId];

    require(msg.sender == cause.owner, "unauthorized");
    require(_proposalId != "", "invalid proposal id");
    uint128 queueToWithdraw = getFront(_walletId.causeId);
    QueueItem memory item = getQueueItem(_walletId.causeId, queueToWithdraw);
    if (
      item.isUnclaimed &&
      item.id == keccak256(abi.encode(_walletId.causeId, _proposalId))
    ) {
      dequeue(_walletId.causeId);
    } else {
      revert("not head of queue");
    }

    bytes32 salt = _getSalt(_walletId);
    IThinWallet wallet = IThinWallet(deployedWallets[salt]);

    if (address(wallet) == address(0)) {
      address[] memory owners = new address[](1);
      owners[0] = cause.owner;
      wallet = IThinWallet(_deployWallet(salt, owners));

      emit RegisterWallet(address(wallet), _walletId);
    }

    emit WithdrawFromWallet(_walletId, _withdrawal);

    address rewardToken = address(baseToken);

    if (_withdrawal.token == rewardToken) {
      uint256 rewardAmount = _withdrawal.amount.mul(cause.rewardPercentage);
      uint256 feeAmount = _withdrawal.amount.mul(platformFee);

      IThinWallet.TokenMovement[]
        memory transfers = new IThinWallet.TokenMovement[](3);
      transfers[0] = IThinWallet.TokenMovement({
        token: rewardToken,
        recipient: owner(),
        amount: feeAmount
      });
      transfers[1] = IThinWallet.TokenMovement({
        token: rewardToken,
        recipient: _withdrawal.recipient,
        amount: _withdrawal.amount - (feeAmount + rewardAmount)
      });
      transfers[2] = IThinWallet.TokenMovement({
        token: rewardToken,
        recipient: address(this),
        amount: rewardAmount
      });

      wallet.transferERC20(transfers);

      IERC20(stakingContract.rewardToken()).safeApprove(
        address(stakingContract),
        rewardAmount
      );
      stakingContract.distributeRewards(cause.daoToken, rewardAmount);
    } else {
      IThinWallet.TokenMovement[]
        memory transfers = new IThinWallet.TokenMovement[](1);
      transfers[0] = IThinWallet.TokenMovement({
        token: rewardToken,
        recipient: _withdrawal.recipient,
        amount: _withdrawal.amount
      });
      wallet.transferERC20(transfers);
    }
  }

  function addToQueue(uint256 _causeId, bytes32 _proposalId) external {
    require(_proposalId != bytes32(0), "invalid proposal id");
    CauseRecord memory cause = causeRecords[_causeId];
    require(msg.sender == cause.owner, "unauthorized");
    bytes32 queueId = keccak256(abi.encode(_causeId, _proposalId));
    enqueue(_causeId, queueId);
  }

  function removeFromQueue(
    uint256 _causeId,
    bytes32 _proposalId,
    uint128 _index
  ) external {
    CauseRecord memory cause = causeRecords[_causeId];
    require(msg.sender == cause.owner, "unauthorized");

    bytes32 queueId = keccak256(abi.encode(_causeId, _proposalId));
    QueueItem memory item = getQueueItem(_causeId, _index);
    require(item.id == queueId, "id does not match index item");

    dequeue(_causeId, _index);
  }

  function getQueueAtIndex(uint256 _causeId, uint128 _index)
    external
    view
    returns (QueuedItem memory item)
  {
    QueueItem memory retrievedItem = getQueueItem(_causeId, _index);
    item = QueuedItem({
      next: retrievedItem.next,
      previous: retrievedItem.previous,
      id: retrievedItem.id,
      isUnclaimed: retrievedItem.isUnclaimed
    });
  }

  function getFirstInQueue(uint256 _causeId)
    external
    view
    returns (uint128 queueFront)
  {
    queueFront = getFront(_causeId);
  }

  function getLastInQueue(uint256 _causeId)
    external
    view
    returns (uint128 queueBack)
  {
    queueBack = getBack(_causeId);
  }

  /// ### Internal functions
  function _getSalt(ThinWalletID memory _walletId)
    internal
    pure
    returns (bytes32 salt)
  {
    salt = keccak256(abi.encode(_walletId));
  }

  function _deployWallet(bytes32 salt, address[] memory owners)
    internal
    returns (address wallet)
  {
    wallet = Clones.cloneDeterministic(walletImplementation, salt);

    deployedWallets[salt] = wallet;

    IThinWallet(wallet).initialize(address(this), owners);
  }
}

// File: contracts/implementations/ERC20Singleton.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "../interfaces/IERC20Singleton.sol";

contract ERC20Singleton is
  IERC20Singleton,
  Initializable,
  ERC20Upgradeable,
  OwnableUpgradeable
{
  error MaxSupplyReached();

  uint256 maxSupply;

  constructor() initializer {
    __ERC20_init("Singleton Base", "BASE");
    __Ownable_init();
    maxSupply = 1 ether;
    transferOwnership(address(1));
  }

  function initialize(
    bytes calldata _name,
    bytes calldata _symbol,
    uint256 _maxSupply,
    address _owner,
    address _preMintDestination,
    uint256 _preMintAmount
  ) external initializer {
    if (_preMintAmount > _maxSupply) {
      revert MaxSupplyReached();
    }
    if (_preMintAmount > 0) {
      _mint(_preMintDestination, _preMintAmount);
    }
    __ERC20_init(string(_name), string(_symbol));
    __Ownable_init();
    maxSupply = _maxSupply;
    transferOwnership(_owner);
  }

  function mint(address account, uint256 amount) external override onlyOwner {
    if (this.totalSupply() + amount > maxSupply) {
      revert MaxSupplyReached();
    }
    _mint(account, amount);
  }

  function burn(address account, uint256 amount) external override onlyOwner {
    _burn(account, amount);
  }
}

// File: contracts/implementations/Governor.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";

import "./ERC20Singleton.sol";
import "../interfaces/IGovernor.sol";
import "../interfaces/IModuleProxyFactory.sol";
import "../vendors/IGnosisSafe.sol";
import "@reality.eth/contracts/development/contracts/IRealityETH.sol";

contract Governor is IGovernor, Ownable, ERC721Holder {
  PublicResolver public override ensResolver;
  ENSRegistry public override ensRegistry;
  IENSRegistrar public override ensRegistrar;
  GnosisSafeProxyFactory public override gnosisFactory;
  address public override gnosisSafeSingleton;
  address public override erc20Singleton;
  uint256 public override ensDomainNFTId;
  IClearingHouseV2 public clearingHouse;
  IDonationsRouter public donationsRouter;

  constructor(ConstructorParams memory _params) {
    require(
      address(_params.ensResolver) != address(0),
      "invalid resolver address"
    );
    require(
      address(_params.ensRegistry) != address(0),
      "invalid registry address"
    );
    require(
      address(_params.ensRegistrar) != address(0),
      "invalid registrar address"
    );
    require(
      address(_params.gnosisFactory) != address(0),
      "invalid factory address"
    );
    require(
      _params.gnosisSafeSingleton != address(0),
      "invalid safe singleton address"
    );
    require(
      _params.erc20Singleton != address(0),
      "invalid token singleton address"
    );
    require(_params.parentDao != address(0), "invalid owner");
    require(
      address(_params.clearingHouse) != address(0),
      "invalid clearing house address"
    );
    require(
      address(_params.donationsRouter) != address(0),
      "invalid donations router address"
    );

    ensResolver = _params.ensResolver;
    ensRegistry = _params.ensRegistry;
    ensRegistrar = _params.ensRegistrar;
    gnosisFactory = _params.gnosisFactory;
    gnosisSafeSingleton = _params.gnosisSafeSingleton;
    erc20Singleton = _params.erc20Singleton;
    clearingHouse = _params.clearingHouse;
    donationsRouter = _params.donationsRouter;

    transferOwnership(_params.parentDao);
  }

  receive() external payable {}

  function addENSDomain(uint256 _domainNFTId) external override onlyOwner {
    require(ensDomainNFTId == 0, "ens domain already set");
    ensDomainNFTId = _domainNFTId;
    ensRegistrar.safeTransferFrom(
      address(msg.sender),
      address(this),
      _domainNFTId
    );

    ensRegistrar.reclaim(_domainNFTId, address(this));
  }

  function withdrawENSDomain(address _destination) external override onlyOwner {
    require(ensDomainNFTId > 0, "ens domain not set");
    uint256 _domainNFTId = ensDomainNFTId;
    delete ensDomainNFTId;
    ensRegistrar.safeTransferFrom(address(this), _destination, _domainNFTId);
  }

  function createChildDAO(
    Token calldata _tokenData,
    Safe calldata _safeData,
    Subdomain calldata _subdomain
  ) external override {
    require(ensDomainNFTId > 0, "ENS domain unavailable");

    // Need to disable auto staking initially
    require(_tokenData.autoStaking == false, "disable auto staking");

    /// Gnosis multi sig
    (address safe, ) = _createGnosisSafe(
      _safeData.safe,
      _safeData.zodiac,
      uint256(keccak256(abi.encodePacked(_subdomain.subdomain, address(this))))
    );

    /// Token
    address token = _createERC20Clone(
      _tokenData.tokenName,
      _tokenData.tokenSymbol,
      _tokenData.maxSupply,
      safe,
      _tokenData.mintingAmount
    );

    /// Register the token in the clearing house contract
    clearingHouse.registerChildDao(
      ERC20Singleton(token),
      _tokenData.autoStaking,
      _tokenData.kycRequired,
      _tokenData.maxSupply,
      _tokenData.maxSwap,
      _tokenData.release,
      _tokenData.exchangeRate
    );
    /// ENS Subdomain + Snapshot text record
    bytes32 node = _createENSSubdomain(
      safe,
      _subdomain.subdomain,
      _subdomain.snapshotKey,
      _subdomain.snapshotValue
    );

    emit ChildDaoCreated(safe, token, node);

    try
      donationsRouter.registerCause(
        IDonationsRouter.CauseRegistrationRequest({
          owner: address(safe),
          rewardPercentage: _tokenData.rewardPercentage,
          daoToken: address(token)
        })
      )
    {} catch (bytes memory reason) {
      emit RegisterCauseFailure(reason);
    }
  }

  function _checkZeroAddress(address _address) internal pure {
    if (_address == address(0)) {
      revert CannotBeZeroAddress();
    }
  }

  function _createGnosisSafe(
    SafeCreationParams memory safeData,
    ZodiacParams memory zodiacData,
    uint256 safeDeploymentSalt
  ) internal returns (address safe, address module) {
    address[] memory initialOwners = new address[](safeData.owners.length + 1);
    uint256 i;
    for (i = 0; i < safeData.owners.length; ++i) {
      initialOwners[i] = safeData.owners[i];
    }
    initialOwners[initialOwners.length - 1] = address(this);
    safe = address(
      gnosisFactory.createProxyWithNonce(
        gnosisSafeSingleton,
        _getSafeInitializer(
          SafeCreationParams({
            owners: initialOwners,
            threshold: 1,
            to: safeData.to,
            data: "",
            fallbackHandler: safeData.fallbackHandler,
            paymentToken: safeData.paymentToken,
            payment: safeData.payment,
            paymentReceiver: safeData.paymentReceiver
          })
        ),
        safeDeploymentSalt
      )
    );
    /// @dev The reality module requires a template. This will create, or reuse an existing one.
    uint256 templateId;
    if (bytes(zodiacData.template).length > 0) {
      templateId = IRealityETH(zodiacData.oracle).createTemplate(
        zodiacData.template
      );
    } else {
      // @dev reality template IDs start at 0;
      templateId = zodiacData.templateId;
    }

    module = IModuleProxyFactory(zodiacData.zodiacFactory).deployModule(
      zodiacData.moduleMasterCopy,
      _getZodiacInitializer(safe, templateId, zodiacData),
      uint256(keccak256(abi.encode(safeDeploymentSalt)))
    );
    /// @dev Enable the newly deployed module on our safe
    IGnosisSafe(safe).execTransaction(
      safe,
      0,
      abi.encodeWithSignature("enableModule(address)", module),
      IGnosisSafe.Operation.Call,
      0,
      0,
      0,
      address(0),
      payable(msg.sender),
      _getApprovedHashSignature()
    );
    /// @dev Remove this contract as an owner in the safe
    IGnosisSafe(safe).execTransaction(
      safe,
      0,
      abi.encodeWithSignature(
        "removeOwner(address,address,uint256)",
        initialOwners[initialOwners.length - 2],
        initialOwners[initialOwners.length - 1],
        safeData.threshold
      ),
      IGnosisSafe.Operation.Call,
      0,
      0,
      0,
      address(0),
      payable(msg.sender),
      _getApprovedHashSignature()
    );
    emit ZodiacModuleEnabled(safe, module, templateId);
  }

  function _getApprovedHashSignature()
    internal
    view
    returns (bytes memory signature)
  {
    signature = abi.encodePacked(
      bytes32(uint256(uint160(address(this)))),
      bytes32(0),
      uint8(1)
    );
  }

  function _getZodiacInitializer(
    address safe,
    uint256 templateId,
    ZodiacParams memory zodiacData
  ) internal pure returns (bytes memory initializer) {
    initializer = abi.encodeWithSignature(
      "setUp(bytes)",
      abi.encode(
        safe,
        safe,
        safe,
        zodiacData.oracle,
        zodiacData.timeout,
        zodiacData.cooldown,
        zodiacData.expiration,
        zodiacData.bond,
        templateId,
        zodiacData.arbitrator == address(0) ? safe : zodiacData.arbitrator
      )
    );
  }

  function _getSafeInitializer(SafeCreationParams memory safeData)
    internal
    pure
    returns (bytes memory initData)
  {
    initData = abi.encodeWithSignature(
      "setup(address[],uint256,address,bytes,address,address,uint256,address)",
      safeData.owners,
      safeData.threshold,
      safeData.to,
      safeData.data,
      safeData.fallbackHandler,
      safeData.paymentToken,
      safeData.payment,
      safeData.paymentReceiver
    );
  }

  function _createERC20Clone(
    bytes memory _name,
    bytes memory _symbol,
    uint256 _maxSupply,
    address _preMintDestination,
    uint256 _preMint
  ) internal returns (address token) {
    token = Clones.cloneDeterministic(
      erc20Singleton,
      keccak256(abi.encodePacked(_name, _symbol, _maxSupply))
    );
    ERC20Singleton(token).initialize(
      _name,
      _symbol,
      _maxSupply,
      address(clearingHouse),
      _preMintDestination,
      _preMint
    );
  }

  function _calculateENSNode(bytes32 baseNode, bytes32 childNode)
    internal
    pure
    returns (bytes32 ensNode)
  {
    ensNode = keccak256(abi.encodePacked(baseNode, childNode));
  }

  function _createENSSubdomain(
    address _owner,
    bytes memory _name,
    bytes memory _key,
    bytes memory _value
  ) internal returns (bytes32 childNode) {
    bytes32 labelHash = keccak256(_name);

    bytes32 ensBaseNode = ensRegistrar.baseNode();
    bytes32 parentNode = _calculateENSNode(
      ensBaseNode,
      bytes32(ensDomainNFTId)
    );
    childNode = _calculateENSNode(parentNode, labelHash);

    ensRegistry.setSubnodeRecord(
      parentNode,
      labelHash,
      address(this),
      address(ensResolver),
      3600
    );

    ensResolver.setAddr(childNode, _owner);

    ensResolver.setText(childNode, string(_key), string(_value));
  }

  function getPredictedAddresses(
    Token calldata _tokenData,
    Safe calldata _safeData,
    bytes calldata _subdomain
  )
    external
    returns (
      address token,
      address safe,
      address realityModule
    )
  {
    token = Clones.predictDeterministicAddress(
      erc20Singleton,
      keccak256(
        abi.encodePacked(
          _tokenData.tokenName,
          _tokenData.tokenSymbol,
          _tokenData.maxSupply
        )
      )
    );
    (safe, realityModule) = _createGnosisSafe(
      _safeData.safe,
      _safeData.zodiac,
      uint256(keccak256(abi.encodePacked(_subdomain, address(this))))
    );
  }

  function setENSRecord(
    bytes calldata _name,
    bytes calldata _key,
    bytes calldata _value
  ) external {
    bytes32 labelHash = keccak256(_name);

    bytes32 ensBaseNode = ensRegistrar.baseNode();
    bytes32 parentNode = _calculateENSNode(
      ensBaseNode,
      bytes32(ensDomainNFTId)
    );
    bytes32 childNode = _calculateENSNode(parentNode, labelHash);
    ensResolver.setText(childNode, string(_key), string(_value));

    emit ENSTextRecordSet(_name, _key, _value);

    require(msg.sender == ensResolver.addr(childNode), "Invalid owner");
  }

  function setEnsResolver(PublicResolver _implementation)
    external
    override
    onlyOwner
  {
    _checkZeroAddress(address(_implementation));
    ensResolver = _implementation;
  }

  function setEnsRegistry(ENSRegistry _implementation) external onlyOwner {
    _checkZeroAddress(address(_implementation));
    ensRegistry = _implementation;
  } //#

  function setEnsRegistrar(IENSRegistrar _implementation) external onlyOwner {
    _checkZeroAddress(address(_implementation));
    ensRegistrar = _implementation;
  }

  function setGnosisFactory(GnosisSafeProxyFactory _implementation)
    external
    onlyOwner
  {
    _checkZeroAddress(address(_implementation));
    gnosisFactory = _implementation;
  }

  function setGnosisSafeSingleton(address _implementation) external onlyOwner {
    _checkZeroAddress(address(_implementation));
    gnosisSafeSingleton = _implementation;
  }

  function setErc20Singleton(address _implementation) external onlyOwner {
    _checkZeroAddress(address(_implementation));
    erc20Singleton = _implementation;
  }

  function setClearingHouse(IClearingHouseV2 _implementation)
    external
    onlyOwner
  {
    _checkZeroAddress(address(_implementation));
    clearingHouse = _implementation;
  }

  function setDonationsRouter(IDonationsRouter _implementation)
    external
    onlyOwner
  {
    _checkZeroAddress(address(_implementation));
    donationsRouter = _implementation;
  }
}

// File: contracts/implementations/Queue.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

abstract contract Queue {
    struct QueueManager {
        uint128 front;
        uint128 back;
    }
    struct QueueItem {
        uint128 next; // The next item to be claimed in the queue. If this is the last item (at back of queue), next should be 0
        uint128 previous; // The previous item in the queue. If this is the first item (at the front of the queue), previous should be 0
        bytes32 id; // An unique identifier for the queue item. This will link the add to queue and withdrawal functions.
        bool isUnclaimed; // Set to true when enqueuing. We can delete the struct when dequeueing, saving some gas.
    }

    // Cause ID => queue manager
    mapping(uint256 => QueueManager) private queueManagers;

    // Queue is operating in reverse and simulates an array with a mapping for gas reasons. It's an implementation of a doubly linked list.
    // [(front) 1][2][(back) 3] (add item) => [(front) 1][2][3][(back) 4]
    // [(front) 1][2][(back) 3] (remove item) => [(front) 2][(back) 3]
    // keccak(causeID, index) => queue item
    mapping(bytes32 => QueueItem) private queueItems;

    /// @notice Adds a new item to the cause queue
    /// @dev _id should not be reused
    /// @param _causeId  The cause to queue an item for
    /// @param _id  The unique ID to use when linking enqueue and withdrawal transactions
    function enqueue(uint256 _causeId, bytes32 _id) internal {
        uint128 currentHead = getFront(_causeId);
        uint128 currentTail = getBack(_causeId);
        uint128 newTail = currentTail + 1;

        if (currentHead == 0){
            currentHead = newTail;
        }

        queueItems[keccak256(abi.encode(_causeId, newTail))] = QueueItem({
            next: 0,
            previous: currentTail,
            id: _id,
            isUnclaimed: true
        });

        if (currentTail != 0) {
            QueueItem memory item = getQueueItem(_causeId, currentTail);
            uint128 prev = item.previous;
            bytes32 id = item.id;
            
            queueItems[keccak256(abi.encode(_causeId, currentTail))] = QueueItem({
                next: newTail,
                previous: prev,
                id: id,
                isUnclaimed: true
            });
        }

        queueManagers[_causeId] = QueueManager({
            front: currentHead,
            back: newTail
        });
    }

    /// @notice Removes an item from the queue
    /// @param _causeId  the cause to dequeue an item for
    function dequeue(uint256 _causeId) internal {
        _removeFromQueue(_causeId, getFront(_causeId));
    }

    /// @notice Removes an arbitrarily placed item from the queue
    /// @param _causeId  the cause to dequeue an item for
    /// @param _index  The index of the item to dequeue. This should be the queue front, unless it's being used to remove an arbitrarily located item from the queue (ie from the middle)
    function dequeue(uint256 _causeId, uint128 _index) internal {
        _removeFromQueue(_causeId, _index);
    }

    /// @notice Removes an item from the cause queue
    /// @param _causeId  the cause to dequeue an item for
    /// @param _index  The index of the item to dequeue. This should be the queue front, unless it's being used to remove an arbitrarily located item from the queue (ie from the middle)
    function _removeFromQueue(uint256 _causeId, uint128 _index) private {
        uint128 currentHead = getFront(_causeId);
        uint128 currentTail = getBack(_causeId);

        bytes32 id = keccak256(abi.encode(_causeId, _index));

        QueueItem memory item = getQueueItem(_causeId, _index);
        uint128 next = item.next;
        uint128 previous = item.previous;
    
        QueueManager storage manager = queueManagers[_causeId];
        uint128 newPrevious = next; uint128 newNext = previous;
        if (_index == currentHead){
            newPrevious = 0;
            manager.front = next;
        }
        if(_index == currentTail){
            newNext = 0;
            manager.back = previous;
        }

        queueItems[keccak256(abi.encode(_causeId, next))].previous = newPrevious;
        queueItems[keccak256(abi.encode(_causeId, previous))].next = newNext;
    
        delete queueItems[id];
    }

    /// @notice Returns the current ID at the front of the queue
    function getFront(uint256 _causeId)
        internal
        view
        returns (uint128 queueFront)
    {
        QueueManager memory manager = queueManagers[_causeId];
        queueFront = manager.front;
    }

    /// @notice Returns the current ID at the back of the queue
    function getBack(uint256 _causeId)
        internal
        view
        returns (uint128 queueBack)
    {
        QueueManager memory manager = queueManagers[_causeId];
        queueBack = manager.back;
    }

    /// @notice Returns the queue item at a given index
    function getQueueItem(uint256 _causeId, uint128 _index)
        internal
        view
        returns (QueueItem memory item)
    {
        item = queueItems[keccak256(abi.encode(_causeId, _index))];
    }
}
// File: contracts/implementations/StakingRewards.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@prb/math/contracts/PRBMathUD60x18.sol";
import "../interfaces/IStakingRewards.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract StakingRewards is IStakingRewards, Ownable {
  using SafeERC20 for ERC20;
  using PRBMathUD60x18 for uint256;

  ERC20 public immutable override rewardToken;

  // Dao token => dao reward data
  mapping(address => RewardDistribution) public override daoRewards;
  // Dao token => user address => user stake data
  mapping(address => mapping(address => UserStake)) public override userStakes;

  uint256 public lockupPeriod;

  constructor(
    address _rewardToken,
    address _owner,
    uint256 _lockupPeriod
  ) {
    require(_rewardToken != address(0), "invalid reward token");
    rewardToken = ERC20(_rewardToken);
    lockupPeriod = _lockupPeriod;
    transferOwnership(_owner);
  }

  modifier isUnlocked(address _daoToken) {
    UserStake memory user = userStakes[_daoToken][msg.sender];
    require(
      block.timestamp >= user.timeStaked + lockupPeriod,
      "stake still locked"
    );
    _;
  }

  function setLockupPeriod(uint256 _lockupPeriod) external override onlyOwner {
    lockupPeriod = _lockupPeriod;
  }

  function stake(address _daoToken, uint256 _amount) external override {
    _processStake(msg.sender, _daoToken, _amount);
  }

  function stakeOnBehalf(
    address _user,
    address _daoToken,
    uint256 _amount
  ) external override {
    require(_user != address(0), "invalid user");
    _processStake(_user, _daoToken, _amount);
  }

  function unstake(
    address _daoToken,
    uint256 _amount,
    address _to
  ) external override isUnlocked(_daoToken) {
    require(_daoToken != address(0), "invalid token");
    require(_amount > 0, "invalid amount");
    require(_to != address(0), "invalid destination");

    RewardDistribution memory dao = daoRewards[_daoToken];
    UserStake memory user = userStakes[_daoToken][msg.sender];

    require(_amount <= user.stakedAmount, "invalid unstake amount");

    // Save their currently earned reward entitlement
    user.pendingRewards += _getRewardAmount(
      user.stakedAmount,
      dao.rewardPerToken,
      user.rewardEntry
    );

    user.stakedAmount -= _amount;
    user.rewardEntry = dao.rewardPerToken;
    dao.totalStake -= _amount;

    if (dao.totalStake == 0) {
      // Last man out the door resets the staking contract for that DAO.
      dao.rewardPerToken = 0;
    }

    daoRewards[_daoToken] = dao;
    userStakes[_daoToken][msg.sender] = user;

    emit Unstake(msg.sender, _daoToken, _amount);

    ERC20(_daoToken).safeTransfer(_to, _amount);
  }

  function claimRewards(address _daoToken, address _to) external override {
    require(_daoToken != address(0), "invalid dao token");
    require(_to != address(0), "invalid destination");

    RewardDistribution memory dao = daoRewards[_daoToken];
    UserStake memory user = userStakes[_daoToken][msg.sender];

    uint256 entitlement = _getRewardAmount(
      user.stakedAmount,
      dao.rewardPerToken,
      user.rewardEntry
    ) + user.pendingRewards;

    user.pendingRewards = 0;
    user.rewardEntry = dao.rewardPerToken;

    userStakes[_daoToken][msg.sender] = user;

    emit ClaimRewards(msg.sender, _daoToken, entitlement);

    rewardToken.safeTransfer(_to, entitlement);
  }

  function emergencyEject(address _daoToken, address _to)
    external
    override
    isUnlocked(_daoToken)
  {
    require(_daoToken != address(0), "invalid dao token");
    require(_to != address(0), "invalid destination");

    RewardDistribution memory dao = daoRewards[_daoToken];
    UserStake memory user = userStakes[_daoToken][msg.sender];

    uint256 entitlement = _getRewardAmount(
      user.stakedAmount,
      dao.rewardPerToken,
      user.rewardEntry
    ) + user.pendingRewards;

    uint256 ejectAmount = user.stakedAmount;
    user.stakedAmount = 0;
    user.rewardEntry = 0;
    user.pendingRewards = 0;
    dao.totalStake -= ejectAmount;

    if (dao.totalStake > 0) {
      // Distribute user's lost rewards to everyone else.
      dao.rewardPerToken = _calculateRewardPerToken(
        dao.rewardPerToken,
        entitlement,
        dao.totalStake
      );
    } else {
      // Last man out the door resets the dao
      dao.rewardPerToken = 0;
    }

    daoRewards[_daoToken] = dao;
    userStakes[_daoToken][msg.sender] = user;

    emit Eject(msg.sender, _daoToken, ejectAmount);

    ERC20(_daoToken).safeTransfer(_to, ejectAmount);
  }

  function distributeRewards(address _daoToken, uint256 _amount)
    external
    override
  {
    require(_daoToken != address(0), "invalid dao");
    require(_amount > 0, "invalid amount");

    RewardDistribution memory dao = daoRewards[_daoToken];

    if (dao.totalStake == 0) {
      dao.rewardPerToken += _amount;
    } else {
      dao.rewardPerToken = _calculateRewardPerToken(
        dao.rewardPerToken,
        _amount,
        dao.totalStake
      );
    }

    daoRewards[_daoToken] = dao;

    // Emit event
    emit Distribution(_daoToken, _amount);

    rewardToken.safeTransferFrom(msg.sender, address(this), _amount);
  }

  function pendingRewards(address _user, address _daoToken)
    external
    view
    override
    returns (uint256 rewardAmount)
  {
    RewardDistribution memory dao = daoRewards[_daoToken];
    UserStake memory user = userStakes[_daoToken][_user];

    rewardAmount =
      _getRewardAmount(
        user.stakedAmount,
        dao.rewardPerToken,
        user.rewardEntry
      ) +
      user.pendingRewards;
  }

  /// ### Internal functions

  /// @notice Processes the stake for both stake and stake on behalf functions
  /// @param _user The user the tokens are being staked on behalf for
  /// @param _daoToken The governance token of the dao to be staked.
  /// @param _amount The amount of governance token to be staked
  function _processStake(
    address _user,
    address _daoToken,
    uint256 _amount
  ) internal {
    require(_daoToken != address(0), "invalid token");
    require(_amount > 0, "invalid amount");

    RewardDistribution memory dao = daoRewards[_daoToken];
    UserStake memory user = userStakes[_daoToken][_user];

    if (dao.totalStake == 0) {
      // Distribute reward amount equally across the first staker's tokens
      if (dao.rewardPerToken > 0) {
        user.pendingRewards = dao.rewardPerToken;
        dao.rewardPerToken = _calculateRewardPerToken(
          0,
          dao.rewardPerToken,
          _amount
        );
      }
    } else {
      user.pendingRewards += _getRewardAmount(
        user.stakedAmount,
        dao.rewardPerToken,
        user.rewardEntry
      );
    }

    user.rewardEntry = dao.rewardPerToken;
    user.stakedAmount += _amount;
    user.timeStaked = block.timestamp;
    dao.totalStake += _amount;

    daoRewards[_daoToken] = dao;
    userStakes[_daoToken][_user] = user;

    emit Stake(_user, _daoToken, _amount);

    ERC20(_daoToken).safeTransferFrom(msg.sender, address(this), _amount);
  }

  /// @notice Calculates the actual amount of reward token that a user is entitled to
  /// @param _userStake  The number of tokens a user has currently staked
  /// @param _rewardPerToken  The current reward per token A 60.18 fixed point number
  /// @param _userRewardEntry  The reward per token the last time the user modified their stake. A 60.18 fixed point number
  function _getRewardAmount(
    uint256 _userStake,
    uint256 _rewardPerToken,
    uint256 _userRewardEntry
  ) internal pure returns (uint256 rewardAmount) {
    if (_userStake == 0 || _rewardPerToken == _userRewardEntry) return 0;
    rewardAmount = PRBMathUD60x18.toUint(
      (_userStake.mul(_rewardPerToken) - (_userStake.mul(_userRewardEntry)))
    );
  }

  /// @notice Calculates the reward per token
  /// @param _currentRewardPerToken The current reward token per staked token
  /// @param _distribution  The amount to distribute
  /// @param _totalStake  The total amount of tokens staked
  function _calculateRewardPerToken(
    uint256 _currentRewardPerToken,
    uint256 _distribution,
    uint256 _totalStake
  ) internal pure returns (uint256 rewardPerToken) {
    rewardPerToken =
      _currentRewardPerToken +
      (PRBMathUD60x18.fromUint(_distribution).div(_totalStake));
  }
}

// File: contracts/interfaces/IClearingHouseV2.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "../implementations/ERC20Singleton.sol";
import "../implementations/Governor.sol";
import "../implementations/StakingRewards.sol";
import "../implementations/DonationsRouter.sol";
import "../implementations/DaoResellQueue.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../vendors/IWETH.sol";

/// @title Earth Fund Clearing House V2
/// @author Sean Long
/// @notice Used as part of the Earth Fund ecosystem to manage purchase and resell of DAO tokens
interface IClearingHouseV2 {
  //################
  //#### STRUCTS ###

  /// @notice Used to track cause DAO information
  /// @param release Unix time of when DAO tokens are released
  /// @param maxSupply Maximum supply of the DAO. **This may be able to be removed**
  /// @param maxPerUser Max amount of token per user
  /// @param exchangeRate Buy in rate for the DAO token
  /// @param childDaoRegistry Used to check if DAO is registered (hard coded to true by setter)
  /// @param autoStaking If tokens should automatically be staked
  /// @param kycEnabled If KYC is enabled for this cause
  /// @param paused Is this cause paused
  struct CauseInformation {
    uint256 release;
    uint256 maxSupply;
    uint256 maxPerUser;
    uint256 exchangeRate;
    bool childDaoRegistry;
    bool autoStaking;
    bool kycEnabled;
    bool paused;
  }
  /// @notice Input data for swapping a token
  /// @param sellToken The token that is being sold (taken) through the swap
  /// @param buyToken The token that is being bought (given to caller) through swap
  /// @param buyAmount The amount of tokenm to buy
  /// @param sellAmount The amount of token to sell
  /// @param spender The address taht will be taking the funds from the calling account
  /// @param swapTarget The address to send the actual call to
  /// @param swapTxData The transaction data to send to the target
  struct SwapData {
    ERC20 sellToken;
    ERC20 buyToken;
    uint256 buyAmount;
    uint256 sellAmount;
    address spender;
    address payable swapTarget;
    bytes swapTxData;
  }

  struct BuyInTokenData {
    ERC20 tokenAddress;
    uint8 decimals;
  }

  //################
  //#### EVENTS ####

  /// @notice Emitted when a Child DAO is registered
  event ChildDaoRegistered(address childDaoToken);

  /// @notice Emitted when a Child DAO token is purchased
  event DaoTokenPurchased(uint256 amount, address buyer, bool autoStake);

  /// @notice Emitted when max supply is set for a Child Dao
  event MaxSupplySet(uint256 maxSupply, ERC20Singleton token);

  /// @notice Emitted when max swap (effectively max per user) is set for a Child Dao
  event MaxSwapSet(uint256 maxSwap, ERC20Singleton token);

  //################
  //#### ERRORS ####
  /// @dev Thrown if an address value is the zero address
  error CannotBeZeroAddress();

  /// @dev Thrown if block.timestamp is greater than expiry in provided message for DAO purchase
  error ApprovalExpired();

  /// @dev Thrown if: message sig already used, message hashes don't match, recoverd signer of message != owner()
  error InvalidSignature();

  /// @dev Thrown if cumulative withdrawals would exceed maxPerUser w/ KYC on OR transaction purchase amount would exceed maxPerUser w/ KYC off
  error UserAmountExceeded();

  /// @dev Thrown if the child DAO is not registered
  error ChildDaoNotRegistered();

  /// @dev Thrown if release of the Child DAO token hasn't started yet
  error ChildDaoReleaseNotStarted();

  /// @dev Thrown if an account that isn't the DAO owner tries to change DAO configuration
  error AccountNotDaoOwner();

  /// @dev Thrown if a swap through 0x fails
  error ZeroXSwapFailed();

  /// @dev Thrown if ETH transfer fails
  error EthTransferFailed();

  /// @dev Thrown if the token being swap to (bought) is not the platform buyInToken
  error WrongBuyToken();

  /// @dev Thrown if the sell token != WETH when swapping ETH to buyInTOken
  error WrongSellToken();

  /// @dev Thrown if pausable operations of a Child DAO token are called while paused
  error CausePaused();

  /// @dev Thrown if the provided swap target does not match the known target stored in state
  error WrongSwapTarget();

  /// @dev Thrown if registerChildDao is called by an account != governor stored in state
  error AccountNotGovernor();

  //###################
  //#### FUNCTIONS ####

  /// @notice Used to register a child DAO
  /// @dev Only callable by Governor. Reverts if paused
  /// @param _childDaoToken Address of the Child DAO token
  /// @param _autoStaking Auto-stasking enabled
  /// @param _kycEnabled KYC enabled
  /// @param _maxSupply Max supply for DAO token
  /// @param _maxSwap Max amount of DAO token per user or per transaction if KYC is disabled
  /// @param _release Unix time tokens should be released
  /// @param _exchangeRate Amount of buyInToken tokens => 1 DAO token
  function registerChildDao(
    ERC20Singleton _childDaoToken,
    bool _autoStaking,
    bool _kycEnabled,
    uint256 _maxSupply,
    uint256 _maxSwap,
    uint256 _release,
    uint256 _exchangeRate
  ) external;

  /// @notice Used to purchase DAO token of a cause
  /// @dev Reverts if paused
  /// @param _childDaoToken Address of the DAO token to buy
  /// @param _amount Amount of token to buy
  /// @param _KYCId KYC ID generated by the back-end
  /// @param _expiry Expiry of the signature
  /// @param _signature Signature of the above parameters
  function purchaseToken(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes memory _KYCId,
    uint256 _expiry,
    bytes memory _signature
  ) external;

  /// @notice Used to complete a 0x swap of any token to the platform buy in token and then use the swapped funds to purhcase DAO tokens
  /// @dev If the swap results in some sell tokens being left over these will be returned to the calling account
  /// @dev If the swap results in extra buy tokens these will be returned to the calling account
  /// @dev Must be called with 0x protocal quote info relevant to the swap being completed
  /// @param _childDaoToken Address of the DAO token to buy
  /// @param _amount Amount of token to buy
  /// @param _KYCId KYC ID generated by the back-end
  /// @param _expiry Expiry of the signature
  /// @param _signature Signature of the above parameters
  /// @param _swapData Swap data - see SwapData struct for further details
  function swapAndPurchaseToken(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes calldata _KYCId,
    uint256 _expiry,
    bytes memory _signature,
    SwapData calldata _swapData
  ) external;

  /// @notice Used to complete a 0x swap of ETH to platform buy in token. This is achieved by wrapping and unwrapping ETH on the way in/out
  /// @dev If the swap results in left over ETH this will be unwrapped and returned to the calling account
  /// @dev If the swap results in extra buy tokens these will be returned to the calling account
  /// @param _childDaoToken Address of the DAO token to buy
  /// @param _amount Amount of token to buy
  /// @param _KYCId KYC ID generated by the back-end
  /// @param _expiry Expiry of the signature
  /// @param _signature Signature of the above parameters
  /// @param _swapData Swap data - see SwapData struct for further details
  function swapETHAndPurchaseToken(
    ERC20Singleton _childDaoToken,
    uint256 _amount,
    bytes calldata _KYCId,
    uint256 _expiry,
    bytes memory _signature,
    SwapData calldata _swapData
  ) external payable;

  /// @notice Enable / disable auto-staking
  /// @dev Only callable by the cause owner
  /// @param _childDaoToken Address of the Child DAO token to change
  /// @param _state What to set it to
  function setAutoStake(ERC20Singleton _childDaoToken, bool _state) external;

  /// @notice Enable KYC for a cause
  /// @dev Only callable by the cause owner. Cannot be disabled
  /// @param _childDaoToken Address of the Child DAO token to change
  function enableKyc(ERC20Singleton _childDaoToken) external;

  /// @notice Set max amount of DAO token per user
  /// @dev Only callable by the cause owner
  /// @param _childDaoToken Address of the Child DAO token to change
  /// @param _max Maxmimum amount of tokens per user
  function setMaxPerUser(ERC20Singleton _childDaoToken, uint256 _max) external;

  /// @notice Set exchange rate (amount of token a user needs to swap per DAO token)
  /// @dev Only callable by the cause owner
  /// @param _childDaoToken Address of the Child DAO token to change
  /// @param _rate Exchange rate
  function setExchangeRate(ERC20Singleton _childDaoToken, uint256 _rate)
    external;

  /// @notice Pause a specific cause
  /// @dev Only callable by the cause owner
  /// @param _childDaoToken The address of the DAO token for the cause
  function pauseCause(ERC20Singleton _childDaoToken) external;

  /// @notice Unpause a specific cause
  /// @dev Only callable by the cause owner
  /// @param _childDaoToken The address of the DAO token for the cause
  function unpauseCause(ERC20Singleton _childDaoToken) external;

  /// @notice Set the donations router contract
  /// @dev Only callable by platform owner
  /// @param _implementation Contract address
  function setDonationsRouter(DonationsRouter _implementation) external;

  /// @notice Set the token the platform uses for DAO token buy in
  /// @dev Only callable by platform owner
  /// @param _implementation Contract address
  function setBuyInToken(ERC20 _implementation) external;

  /// @notice Set the staking contract
  /// @dev Only callable by platform owner
  /// @param _implementation Contract address
  function setStakingRewards(StakingRewards _implementation) external;

  /// @notice Pause the contract
  /// @dev Only callable by platform owner
  function pause() external;

  /// @notice Unpause the contract
  /// @dev Only callable by platform owner
  function unpause() external;

  /// @notice Set the 0x swap target
  /// @dev Used to prevent a user from having this contract execute calls on arbitrary contracts
  /// @param _implementation New address for the 0x exchange proxy contract
  function setSwapTarget(address _implementation) external;

  /// @notice Set the address of the governor contract
  /// @dev Only callable by platform owner
  /// @param _implementation Address of the governor implementation
  function setGovernor(address _implementation) external;

  /// @notice Sets the authenticated signer for kyc signature generation.
  /// @param _newSigner the new signer address
  function setAuthenticatedKYCSigner(address _newSigner) external;

  //################################
  //#### AUTO-GENERATED GETTERS ####

  function causeInformation(ERC20Singleton _causeToken)
    external
    view
    returns (
      uint256 release,
      uint256 maxSupply,
      uint256 maxPerUser,
      uint256 exchangeRate,
      bool childDaoRegistry,
      bool autoStaking,
      bool kycEnabled,
      bool paused
    );

  function withdrawnAmount(uint256 _causeId, bytes calldata _kycId)
    external
    view
    returns (uint256 amount);

  function donationsRouter()
    external
    view
    returns (DonationsRouter implementation);

  function staking() external view returns (StakingRewards implementation);

  function buyInToken()
    external
    view
    returns (ERC20 tokenAddress, uint8 decimals);

  function WETH() external view returns (IWETH implementaiton);

  function usedSignatures(bytes32 _message) external returns (bool used);

  function zeroXSwapTarget() external view returns (address _implementation);

  function governor() external view returns (address _implementation);
}

// File: contracts/interfaces/IDaoResellQueue.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IDaoResellQueue {
  //################
  //#### STRUCTS ####

  struct SaleItem {
    address owner;
    uint32 splitId;
    uint128 amount;
    uint128 claimThreshold;
  }

  struct PurchaseItem {
    address owner;
    uint256 split;
    uint256 amount;
  }

  struct CumulativeTotals {
    uint128 cumulativeSold;
    uint128 cumulativeDeleted;
  }

  //################
  //#### EVENTS ####

  event SaleItemQueued(address causeToken, address owner, uint256 saleItem);

  event SaleItemDequeued(address causeToken, address owner, uint128 amount);

  //################
  //#### ERRORS ####

  error CannotBeZeroAddress();

  //###################
  //#### FUNCTIONS ####
  function enqueue(address _causeToken, uint128 _amount) external;

  function dequeue(address _causeToken, uint128 _saleId) external;

  function purchaseAvailable(address _causeToken, uint256 _amount)
    external
    returns (PurchaseItem[] memory purchaseItems);

  // function setMinimumSaleAmount(address _causeToken, uint128 _amount) external;

  function forceDequeue(address _causeToken, uint128[] calldata _listingIds)
    external;

  function setSaleSplit(address _causeToken, uint256 _splitAmount) external;

  //################################
  //#### AUTO-GENERATED GETTERS ####

  function causeSplitId(address _causeToken)
    external
    view
    returns (uint32 splitId);

  function split(uint32 _splitId) external view returns (uint256 split);

  function saleItems(address _causeToken, uint256 _saleItemId)
    external
    view
    returns (
      address owner,
      uint32 splitId,
      uint128 amount,
      uint128 claimThreshold
    );

  function cumulativeTotals(address _causeToken)
    external
    view
    returns (uint128 totalSold, uint128 totalDeleted);

  function clearingHouse() external view returns (address clearingHouse);

  function saleItemCounter() external view returns (uint256 id);

  function validateProceedsClaim(address _causeToken, uint256 _saleItemId)
    external;
}

// File: contracts/interfaces/IDonationsRouter.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import "../interfaces/IStakingRewards.sol";

interface IDonationsRouter {
  /// ### Structs
  struct CauseRegistrationRequest {
    address owner;
    uint256 rewardPercentage;
    address daoToken;
  }

  struct CauseUpdateRequest {
    address owner;
    uint256 rewardPercentage;
  }

  struct CauseRecord {
    address owner;
    address defaultWallet; /// Default wallet is calculated with cause Id and thin wallet id being equal to each other
    address daoToken;
    uint256 rewardPercentage; /// A PRBMath 60.18 fixed point number. 1e16 == 1% and 1e18 == 100%
  }

  struct WithdrawalRequest {
    address token;
    address recipient;
    uint256 amount;
  }

  struct ThinWalletID {
    uint256 causeId;
    bytes thinWalletId;
  }

  struct QueuedItem {
    uint128 next; // The next item to be claimed in the queue. If this is the last item (at back of queue), next should be 0
    uint128 previous; // The previous item in the queue. If this is the first item (at the front of the queue), previous should be 0
    bytes32 id; // An unique identifier for the queue item. This will link the add to queue and withdrawal functions.
    bool isUnclaimed; // Set to true when enqueuing. We can delete the struct when dequeueing, saving some gas.
  }

  /// ### Events

  event RegisterCause(
    address indexed owner,
    address indexed daoToken,
    uint256 causeId
  );
  event RegisterWallet(address indexed walletAddress, ThinWalletID walletId);
  event WithdrawFromWallet(ThinWalletID wallet, WithdrawalRequest request);
  event UpdateCause(CauseRecord cause);
  event UpdateRewardAddress(
    address indexed oldRewardAddress,
    address indexed newAddress
  );
  event UpdateFee(uint256 oldFee, uint256 newFee);

  /// ### Functions

  /// @notice Creates a cause so that it can start using thin wallets that it controls
  /// @dev This should be an open function
  /// @param _cause  The cause to be registered
  function registerCause(CauseRegistrationRequest calldata _cause) external;

  /// @notice Updates a cause
  /// @dev Can only be called by the current owner of the cause
  /// @param _causeId  The cause to update
  /// @param _cause  The new details of the cause
  function updateCause(uint256 _causeId, CauseUpdateRequest calldata _cause)
    external;

  /// @notice Sets the address of the staking contract so rewards can be distributed
  /// @dev This should be secured
  /// @param _rewardContract  The new staking contract
  // function setRewardAddress(address _rewardContract) external;

  /// @notice Calculates the address that a given thin wallet is or will be deployed to
  /// @param _walletId  The wallet parameters to calculate the address from
  function calculateThinWallet(ThinWalletID calldata _walletId)
    external
    view
    returns (address wallet);

  /// @notice Deploys a thin wallet to the address derived from the parameters given
  /// @param _walletId  The wallet parameters
  /// @param _owners  The wallet owners to set. These accounts can transfer funds, so they should be limited
  function registerThinWallet(
    ThinWalletID calldata _walletId,
    address[] calldata _owners
  ) external;

  /// @notice Withdraws funds from the specified thin wallet
  /// @param _walletId  The wallet address parameters
  /// @param _withdrawal  An array of withdrawal requests for the wallet to process
  function withdrawFromThinWallet(
    ThinWalletID calldata _walletId,
    WithdrawalRequest calldata _withdrawal,
    bytes32 _proposalId
  ) external;

  /// @notice Allows the platform owner to set the platform fee
  /// @dev Only the platform owner should be able to call this
  /// @param _fee  The new platform fee
  function setPlatformFee(uint256 _fee) external;

  /// @notice Adds a queue which is linked to a unique identifier using the hash of combined causeID and proposalID
  /// @param _causeId The cause id
  /// @param _proposalId The proposal id
  function addToQueue(uint256 _causeId, bytes32 _proposalId) external;

  /// @notice Removes a queue arbitrarily at specified index
  /// @param _causeId The cause id
  /// @param _proposalId The proposal id
  /// @param _index The queue index
  function removeFromQueue(
    uint256 _causeId,
    bytes32 _proposalId,
    uint128 _index
  ) external;

  /// @notice Gets queue item at specific index
  /// @param _causeId The cause id
  /// @param _index The queue index
  function getQueueAtIndex(uint256 _causeId, uint128 _index)
    external
    view
    returns (QueuedItem memory item);

  /// @notice Gets first item in queue
  /// @param _causeId The cause id
  function getFirstInQueue(uint256 _causeId)
    external
    view
    returns (uint128 queueFront);

  /// @notice Gets last item in queue
  /// @param _causeId The cause id
  function getLastInQueue(uint256 _causeId)
    external
    view
    returns (uint128 queueBack);

  /// ### Autogenerated getters

  function baseToken() external view returns (ERC20 baseToken);

  function stakingContract()
    external
    view
    returns (IStakingRewards stakingContract);

  function causeId() external view returns (uint256 causeId);

  function tokenCauseIds(address token) external view returns (uint256 causeId);

  function causeRecords(uint256 causeId)
    external
    view
    returns (
      address owner,
      address defaultWallet,
      address daoToken,
      uint256 rewardPercentage
    );

  function platformFee() external view returns (uint256 fee);

  function deployedWallets(bytes32 _salt)
    external
    view
    returns (address wallet);

  function walletImplementation()
    external
    view
    returns (address walletImplementation);
}

// File: contracts/interfaces/IERC20Singleton.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface IERC20Singleton {
  function initialize(
    bytes calldata _name,
    bytes calldata _symbol,
    uint256 _maxSupply,
    address _owner,
    address _preMintDestination,
    uint256 _preMintAmount
  ) external;

  function mint(address account, uint256 amount) external;

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

// File: contracts/interfaces/IGovernor.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "@ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol";
import "@ensdomains/ens-contracts/contracts/registry/ENSRegistry.sol";
import "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "../vendors/IENSRegistrar.sol";
import "./IClearingHouseV2.sol";
import "./IDonationsRouter.sol";

interface IGovernor {
  error CannotBeZeroAddress();
  /// Structs
  struct ConstructorParams {
    PublicResolver ensResolver;
    ENSRegistry ensRegistry;
    IENSRegistrar ensRegistrar;
    GnosisSafeProxyFactory gnosisFactory;
    address gnosisSafeSingleton;
    address erc20Singleton;
    address parentDao;
    IClearingHouseV2 clearingHouse;
    IDonationsRouter donationsRouter;
  }

  struct Token {
    bytes tokenName;
    bytes tokenSymbol;
    uint256 maxSupply;
    uint256 maxSwap;
    uint256 release;
    bool autoStaking;
    bool kycRequired;
    uint256 mintingAmount;
    uint256 rewardPercentage;
    bytes KYCId;
    uint256 expiry;
    bytes signature;
    uint256 exchangeRate;
  }

  struct Safe {
    SafeCreationParams safe;
    ZodiacParams zodiac;
  }
  struct ZodiacParams {
    address zodiacFactory; // The factory to deploy from
    address moduleMasterCopy; // The module we want cloned
    address oracle; // The public reality.eth oracle contract
    uint32 timeout;
    uint32 cooldown;
    uint32 expiration;
    uint256 bond;
    uint256 templateId; // To be used to reuse a template
    string template; // If this.length > 0, it will be used to create a new template
    address arbitrator; // Should probably be kleros general court, or earthfund dao safe.
  }

  struct SafeCreationParams {
    address[] owners;
    uint256 threshold;
    address to;
    bytes data;
    address fallbackHandler;
    address paymentToken;
    uint256 payment;
    address payable paymentReceiver;
  }

  struct Subdomain {
    bytes subdomain;
    bytes snapshotKey;
    bytes snapshotValue;
  }

  /// Events
  event ChildDaoCreated(
    address indexed safe,
    address indexed token,
    bytes32 node
  );

  event ZodiacModuleEnabled(
    address indexed safe,
    address indexed module,
    uint256 oracleTemplateId
  );

  event RegisterCauseFailure(bytes failure);

  event ENSTextRecordSet(bytes subdomain, bytes key, bytes value);

  /// Functions

  /// @notice Creates the constituent components of a child dao
  /// @param _tokenData The details of the ERC20 token to create
  /// @param _safeData The details of the Gnosis safe to create
  /// @param _subdomain The details of the ens subdomain to create
  function createChildDAO(
    Token calldata _tokenData,
    Safe calldata _safeData,
    Subdomain calldata _subdomain
  ) external;

  /// @notice Transfers an ENS NFT into the contract for use with subdomains
  /// @param _domainNFTId The ENS NFT to transfer in
  function addENSDomain(uint256 _domainNFTId) external;

  /// @notice Removes the ENS domain from the contract
  /// @param _destination The account to transfer the domain to
  function withdrawENSDomain(address _destination) external;

  function setENSRecord(
    bytes calldata _name,
    bytes calldata _key,
    bytes calldata _value
  ) external;

  /// @notice Gets the predicted addresses required to create the correct snapshot k/v pair
  /// @dev This returns the address in the revert string. abi.decode(address,address,address) returns (token, safe, realityModule)
  /// @dev Call off chain using callstatic, and catching the revert string.
  /// @param _tokenData  The token creation parameters
  /// @param _safeData  The safe creation parameters
  /// @param _subdomain  The desired subdomain to be added to the earthfund ens record
  function getPredictedAddresses(
    Token calldata _tokenData,
    Safe calldata _safeData,
    bytes calldata _subdomain
  )
    external
    returns (
      address token,
      address safe,
      address realityModule
    );

  /// @notice Set ENS Resolver contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setEnsResolver(PublicResolver _implementation) external;

  /// @notice Set ENS Registry contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setEnsRegistry(ENSRegistry _implementation) external;

  /// @notice Set ENS Registrar contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setEnsRegistrar(IENSRegistrar _implementation) external;

  /// @notice Set Gnosis Safe Factory contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setGnosisFactory(GnosisSafeProxyFactory _implementation) external;

  /// @notice Set Gnosis Safe Singleton contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setGnosisSafeSingleton(address _implementation) external;

  /// @notice Set ERC20 Singleton contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setErc20Singleton(address _implementation) external;

  /// @notice Set Clearing House contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setClearingHouse(IClearingHouseV2 _implementation) external;

  /// @notice Set Donations Router  contract
  /// @dev Only callable by contract owner
  /// @param _implementation Contract address
  function setDonationsRouter(IDonationsRouter _implementation) external;

  /// @notice Autogenerated getters
  function ensResolver() external view returns (PublicResolver);

  function ensRegistry() external view returns (ENSRegistry);

  function ensRegistrar() external view returns (IENSRegistrar);

  function gnosisFactory() external view returns (GnosisSafeProxyFactory);

  function gnosisSafeSingleton() external view returns (address);

  function erc20Singleton() external view returns (address);

  function clearingHouse()
    external
    view
    returns (IClearingHouseV2 implementation);

  function donationsRouter() external view returns (IDonationsRouter);

  function ensDomainNFTId() external view returns (uint256);
}

// File: contracts/interfaces/IModuleProxyFactory.sol



// SPDX-License-Identifier: LGPL-3.0-only
// Source: https://github.com/gnosis/zodiac/blob/master/contracts/factory/ModuleProxyFactory.sol
pragma solidity 0.8.13;

interface IModuleProxyFactory {
  function deployModule(
    address masterCopy,
    bytes memory initializer,
    uint256 saltNonce
  ) external returns (address proxy);
}

// File: contracts/interfaces/IStakingRewards.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface IStakingRewards {
    /// ### Structs
    struct RewardDistribution {
        uint256 totalStake;
        uint256 rewardPerToken; // Needs to be boosted by 1e18 before storing so we don't lose precision
    }
    // If totalstake is 0, first staker gets everything. When ejecting, or withdrawing, and totalstake becomes 0, reward per token should also be set to 0.
    struct UserStake {
        uint256 stakedAmount;
        uint256 rewardEntry;
        uint256 pendingRewards;
        uint256 timeStaked;
    }

    /// ### Events
    event Stake(address indexed user, address indexed daoToken, uint256 amount);
    event Unstake(
        address indexed user,
        address indexed daoToken,
        uint256 amount
    );
    event ClaimRewards(
        address indexed user,
        address indexed daoToken,
        uint256 amount
    );
    event Distribution(address indexed daoToken, uint256 amount);
    event Eject(
        address indexed sender,
        address indexed daoToken,
        uint256 amount
    );

    /// ### Functions
    /// @notice Updates the lock up period in state
    /// @param _lockupPeriod The new 
    function setLockupPeriod(uint256 _lockupPeriod) external;

    /// @notice Stakes amount of dao token into the contract
    /// @param _daoToken The governance token of the dao to be staked.
    /// @param _amount The amount of governance token to be staked
    function stake(address _daoToken, uint256 _amount) external;

    /// @notice Stakes amount of dao token into the contract for a user
    /// @param _user The user the tokens are being staked on behalf for
    /// @param _daoToken The governance token of the dao to be staked.
    /// @param _amount The amount of governance token to be staked
    function stakeOnBehalf(address _user, address _daoToken, uint256 _amount) external;

    /// @notice Unstakes and transfers out a given amount of dao governance tokens
    /// @dev This function will also claim any outstanding rewards for the user
    /// @param _daoToken The dao governance token to unstake
    /// @param _amount The amount to remove
    /// @param _to The destination for the dao tokens and rewards
    function unstake(
        address _daoToken,
        uint256 _amount,
        address _to
    ) external;

    /// @notice Claims all outstanding rewards for the user for the specified dao
    /// @param _daoToken The dao to claim rewards for.
    /// @param _to The destination for the rewards
    function claimRewards(address _daoToken, address _to) external;

    /// @notice Withdraws the user's dao governance tokens without claiming any outstanding rewards
    /// @dev To be used as an emergency exit hatch for users. This will permanently lock any outstanding rewards for the user in the contract.
    /// @param _daoToken The dao governance token to eject.
    /// @param _to The destination wallet for the ejected tokens
    function emergencyEject(address _daoToken, address _to) external;

    /// @notice Updates the dao distribution to include an additional amount
    /// @dev This should pull tokens from the caller's account
    /// @dev This should be coded in such a way as to not require any kind of authorization other than providing an amount of reward tokens.
    /// @dev This should handle cases where there aren't any tokens staked: first staker gets everything.
    /// @param _daoToken The dao to distribute rewards to
    /// @param _amount The amount of reward token to distribute
    function distributeRewards(address _daoToken, uint256 _amount) external;

    /// @notice Returns the amount of claimable reward tokens for a user for a given dao
    /// @param _user The user who staked the tokens
    /// @param _daoToken The governance token of the dao the user has staked
    /// @return rewardAmount The amount of the reward token that the user is entitled to
    function pendingRewards(address _user, address _daoToken)
        external
        view
        returns (uint256 rewardAmount);

    /// ### Autogenerated getter functions

    function lockupPeriod() external view returns (uint256);

    function rewardToken() external view returns (ERC20 rewardToken);

    function daoRewards(address _daoToken)
        external
        view
        returns (uint256 totalStake, uint256 rewardPerToken);

    function userStakes(address _daoToken, address _user)
        external
        view
        returns (
            uint256 stakedAmount,
            uint256 rewardEntry,
            uint256 pendingRewards,
            uint256 timeStaked
        );
}

// File: contracts/interfaces/IThinWallet.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IThinWallet {
    error InvalidPermissions(address _user);
    error InvalidAddress();

    struct TokenMovement {
        address token;
        address recipient;
        uint256 amount;
    }

    struct EtherPaymentTransfer {
        address recipient;
        uint256 amount;
    }

    /// ### Events
    event TransferERC20(TokenMovement[] transfers);
    event TransferEther(EtherPaymentTransfer[] transfers);

    /// ### Functions
    /// @notice Initializes the thin wallet clone with the accounts that can control it
    /// @param _admin  This is should be set as the default admin. This will be the donation router
    /// @param _owners  The accounts that can call the transfer functions
    function initialize(address _admin, address[] calldata _owners) external;

    /// @notice Transfers amounts of an ERC20 to one or more recipients
    /// @dev If the `setApprove` field is true, the contract should approve that recipient for type(uint256).max
    /// @param _transfers  An array of transfers. Each transfer object specifies the amount and recipient to send tokens to
    function transferERC20(TokenMovement[] calldata _transfers) external;

    /// @notice Transfers amounts of ether to one or more recipeints
    /// @dev This should use address(recipient).call to transfer the ether
    /// @param _transfers  The ether transfers
    function transferEther(EtherPaymentTransfer[] calldata _transfers) external;

    /// @notice Transfers the current balance of a token to a destination without processing a split
    /// @param _token  The token to transfer
    /// @param _destination  The acccount to send the tokens to
    function emergencyEjectERC20(address _token, address _destination) external;

    /// @notice Transfers the current balance of Ether to the destination without processing a split
    /// @param _destination  The account to send the ether to.
    function emergencyEjectEth(address _destination) external;
}

// File: contracts/library/SigRecovery.sol



pragma solidity 0.8.13;

/**
 * Helper library to recover admin signatures approving certain user's
 * as KYCed in the earthfund ecosystem.
 */

library SigRecovery {
  function recoverApproval(
    bytes memory _KYCId,
    address _user,
    uint256 _causeId,
    uint256 _expiry,
    bytes memory _signature
  ) internal pure returns (address recoveredAddress, bytes32 messageHash) {
    messageHash = recreateApprovalHash(_KYCId, _user, _causeId, _expiry);

    (bytes32 r, bytes32 s, uint8 v) = _splitSignature(_signature);

    recoveredAddress = ecrecover(messageHash, v, r, s);
  }

  function recreateApprovalHash(
    bytes memory _KYCId,
    address _user,
    uint256 _causeId,
    uint256 _expiry
  ) internal pure returns (bytes32 messageHash) {
    messageHash = keccak256(
      abi.encodePacked(
        "\x19Ethereum Signed Message:\n32",
        keccak256(abi.encode(_KYCId, _user, _causeId, _expiry))
      )
    );
  }

  /// @notice Helper function for splitting the signature into its three parts; r s v.
  /// @dev This functions output is used when calling the ecrecover helper function
  /// @param sig  The signature to split
  /// @return r Output from the ECDSA signature
  /// @return s The other output from the ECDSA signature
  /// @return v The signature recovery ID
  function _splitSignature(bytes memory sig)
    private
    pure
    returns (
      bytes32 r,
      bytes32 s,
      uint8 v
    )
  {
    require(sig.length == 65, "Sig: Invalid signature length");

    assembly {
      // First 32 bytes holds the signature length, skips first 32 bytes as that is the prefix
      r := mload(add(sig, 32))
      // Gets the following 32 bytes of the signature
      s := mload(add(sig, 64))
      // Get the final byte (first byte of the next 32 bytes)
      v := byte(0, mload(add(sig, 96)))
    }
  }
}

// File: contracts/vendors/IENSRegistrar.sol



import "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface IENSRegistrar is IERC721 {
    function reclaim(uint256 id, address owner) external;

    function ens() external returns (address ens);

    function baseNode() external view returns (bytes32 base);
}

// File: contracts/vendors/IGnosisSafe.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IGnosisSafe {
  enum Operation {
    Call,
    DelegateCall
  }

  function getThreshold() external view returns (uint256);

  function isOwner(address owner) external view returns (bool);

  /// @dev Returns array of owners.
  /// @return Array of Safe owners.
  function getOwners() external view returns (address[] memory);

  function isModuleEnabled(address module) external view returns (bool);

  /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
  ///      Note: The fees are always transferred, even if the user transaction fails.
  /// @param to Destination address of Safe transaction.
  /// @param value Ether value of Safe transaction.
  /// @param data Data payload of Safe transaction.
  /// @param operation Operation type of Safe transaction.
  /// @param safeTxGas Gas that should be used for the Safe transaction.
  /// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
  /// @param gasPrice Gas price that should be used for the payment calculation.
  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
  /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
  function execTransaction(
    address to,
    uint256 value,
    bytes calldata data,
    Operation operation,
    uint256 safeTxGas,
    uint256 baseGas,
    uint256 gasPrice,
    address gasToken,
    address payable refundReceiver,
    bytes memory signatures
  ) external payable returns (bool success);

  /// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
  ///      This can only be done via a Safe transaction.
  /// @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`.
  /// @param prevOwner Owner that pointed to the owner to be removed in the linked list
  /// @param owner Owner address to be removed.
  /// @param _threshold New threshold.
  function removeOwner(
    address prevOwner,
    address owner,
    uint256 _threshold
  ) external;

  /// @dev Setup function sets initial storage of contract.
  /// @param _owners List of Safe owners.
  /// @param _threshold Number of required confirmations for a Safe transaction.
  /// @param to Contract address for optional delegate call.
  /// @param data Data payload for optional delegate call.
  /// @param fallbackHandler Handler for fallback calls to this contract
  /// @param paymentToken Token that should be used for the payment (0 is ETH)
  /// @param payment Value that should be paid
  /// @param paymentReceiver Address that should receive the payment (or 0 if tx.origin)
  function setup(
    address[] calldata _owners,
    uint256 _threshold,
    address to,
    bytes calldata data,
    address fallbackHandler,
    address paymentToken,
    uint256 payment,
    address payable paymentReceiver
  ) external;
}

// File: contracts/vendors/IWETH.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
  function deposit() external payable;

  function withdraw(uint256 amount) external;
}