APE Price: $0.69 (-2.97%)

Contract

0x000000B3F05aC48e679d211B2892BC08F02150C9

Overview

APE Balance

Apechain LogoApechain LogoApechain Logo0 APE

APE Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

2 Internal Transactions found.

Latest 2 internal transactions

Parent Transaction Hash Block From To
76586572025-01-06 19:58:4030 days ago1736193520
0x000000B3...8F02150C9
 Contract Creation0 APE
76586572025-01-06 19:58:4030 days ago1736193520  Contract Creation0 APE

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PromotionalPoolFactory

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 9999999 runs

Other Settings:
cancun EvmVersion, None license
File 1 of 37 : PromotionalPoolFactory.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "./PromotionalPoolCreationCode.sol";
import "../../DataTypes.sol";
import "../../Errors.sol";
import "../../interfaces/ITokenMasterFactory.sol";
import "@limitbreak/tm-core-lib/src/licenses/LicenseRef-PolyForm-Strict-1.0.0.sol";

/**
 * @title  PromotionalPoolFactory
 * @author Limit Break, Inc.
 * @notice Factory contract for deploying PromotionalPool contracts from TokenMasterRouter.
 * 
 * @dev    The `deployToken` function may only be called by the TokenMasterRouter contract
 *         which will supply the initial paired token value to the contract prior to deployment.
 */
contract PromotionalPoolFactory is ITokenMasterFactory {
    /// @dev The address of the TokenMasterRouter contract which is allowed to call `deployToken`.
    address private immutable ROUTER;

    /// @dev The address that contains the pool creation code.
    address public immutable CREATION_CODE;

    modifier onlyRouter() {
        if (msg.sender != ROUTER) {
            revert TokenMasterFactory__CallerMustBeRouter();
        }
        _;
    }

    /**
     * @notice Constructs the factory contract.
     * 
     * @dev    Throws when the router address is the zero address.
     * @dev    Throws when the router address does not have code.
     * 
     * @param tokenMasterRouter  The address of the TokenMaster Router contract.
     */
    constructor(address tokenMasterRouter) {
        if (tokenMasterRouter == address(0) || tokenMasterRouter.code.length == 0) {
            revert TokenMasterFactory__RouterAddressNotSet();
        }

        ROUTER = tokenMasterRouter;
        CREATION_CODE = address(new PromotionalPoolCreationCode());
    }
    /**
     * @notice Deploys a new PromotionalPool token with the provided parameters and returns the deployment address.
     * 
     * @param tokenSalt             The salt value for the contract creation.
     * @param poolParams            The parameters for the PromotionalPool pool.
     * @param pairedValueIn         The amount of paired value sent with the deployment transaction.
     * @param infrastructureFeeBPS  The infrastructure fee for the pool.
     * 
     * @return deployedAddress  The address the token contract is deployed to.
     */
    function deployToken(
        bytes32 tokenSalt,
        PoolDeploymentParameters calldata poolParams,
        uint256 pairedValueIn,
        uint256 infrastructureFeeBPS
    ) external onlyRouter returns(address deployedAddress) {
        bytes memory initCode = 
            bytes.concat(
                CREATION_CODE.code,
                abi.encode(
                    poolParams,
                    pairedValueIn,
                    infrastructureFeeBPS,
                    ROUTER
                )
            );

        assembly ("memory-safe") {
            deployedAddress := create2(0x00, add(initCode, 0x20), mload(initCode), tokenSalt)
        }
    }

    /**
     * @notice Calculates the deployment address of a PromotionalPool based on the deployment parameters.
     * 
     * @param tokenSalt             The salt value for the contract creation.
     * @param poolParams            The parameters for the PromotionalPool pool.
     * @param pairedValueIn         The amount of paired value sent with the deployment transaction.
     * @param infrastructureFeeBPS  The infrastructure fee for the pool.
     * 
     * @return deploymentAddress  Address that the contract will deploy to with the given parameters.
     */
    function computeDeploymentAddress(
        bytes32 tokenSalt,
        PoolDeploymentParameters calldata poolParams,
        uint256 pairedValueIn,
        uint256 infrastructureFeeBPS
    ) external view returns(address deploymentAddress) {
        bytes32 initCodeHash = keccak256(
            bytes.concat(
                CREATION_CODE.code,
                abi.encode(
                    poolParams,
                    pairedValueIn,
                    infrastructureFeeBPS,
                    ROUTER
                )
            )
        );

        address addressMask = ADDRESS_MASK;
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(0x40, add(ptr, 0x60))
            mstore8(ptr, 0xff)
            mstore(add(ptr, 0x01), shl(0x60, address()))
            mstore(add(ptr, 0x15), tokenSalt)
            mstore(add(ptr, 0x35), initCodeHash)
            deploymentAddress := and(addressMask, keccak256(ptr, 0x55))
        }
    }
}

File 2 of 37 : LicenseRef-PolyForm-Strict-1.0.0.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity ^0.8.0;

/*
# PolyForm Strict License 1.0.0

<https://polyformproject.org/licenses/strict/1.0.0>

## Acceptance

In order to get any license under these terms, you must agree
to them as both strict obligations and conditions to all
your licenses.

## Copyright License

The licensor grants you a copyright license for the software
to do everything you might do with the software that would
otherwise infringe the licensor's copyright in it for any
permitted purpose, other than distributing the software or
making changes or new works based on the software.

## Patent License

The licensor grants you a patent license for the software that
covers patent claims the licensor can license, or becomes able
to license, that you would infringe by using the software.

## Noncommercial Purposes

Any noncommercial purpose is a permitted purpose.

## Personal Uses

Personal use for research, experiment, and testing for
the benefit of public knowledge, personal study, private
entertainment, hobby projects, amateur pursuits, or religious
observance, without any anticipated commercial application,
is use for a permitted purpose.

## Noncommercial Organizations

Use by any charitable organization, educational institution,
public research organization, public safety or health
organization, environmental protection organization,
or government institution is use for a permitted purpose
regardless of the source of funding or obligations resulting
from the funding.

## Fair Use

You may have "fair use" rights for the software under the
law. These terms do not limit them.

## No Other Rights

These terms do not allow you to sublicense or transfer any of
your licenses to anyone else, or prevent the licensor from
granting licenses to anyone else.  These terms do not imply
any other licenses.

## Patent Defense

If you make any written claim that the software infringes or
contributes to infringement of any patent, your patent license
for the software granted under these terms ends immediately. If
your company makes such a claim, your patent license ends
immediately for work on behalf of your company.

## Violations

The first time you are notified in writing that you have
violated any of these terms, or done anything with the software
not covered by your licenses, your licenses can nonetheless
continue if you come into full compliance with these terms,
and take practical steps to correct past violations, within
32 days of receiving notice.  Otherwise, all your licenses
end immediately.

## No Liability

***As far as the law allows, the software comes as is, without
any warranty or condition, and the licensor will not be liable
to you for any damages arising out of these terms or the use
or nature of the software, under any kind of legal claim.***

## Definitions

The **licensor** is the individual or entity offering these
terms, and the **software** is the software the licensor makes
available under these terms.

**You** refers to the individual or entity agreeing to these
terms.

**Your company** is any legal entity, sole proprietorship,
or other kind of organization that you work for, plus all
organizations that have control over, are under the control of,
or are under common control with that organization.  **Control**
means ownership of substantially all the assets of an entity,
or the power to direct its management and policies by vote,
contract, or otherwise.  Control can be direct or indirect.

**Your licenses** are all the licenses granted to you for the
software under these terms.

**Use** means anything you do with the software requiring one
of your licenses.
*/

File 3 of 37 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.4;

import "./IERC20.sol";
import "./IERC20Metadata.sol";
import "./IERC20Errors.sol";
import "./StorageERC20.sol";
import "../../utils/token/TransferHooks.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}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * 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.
 */
abstract contract ERC20 is TransferHooks, IERC20, IERC20Metadata, IERC20Errors {
    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        StorageERC20.data().name = name_;
        StorageERC20.data().symbol = symbol_;
    }

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

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return StorageERC20.data().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 default value returned by this function, unless
     * it's 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 returns (uint8) {
        return 18;
    }

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` 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 value) public virtual returns (bool) {
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }

        StorageERC20.data().allowances[_getAllowanceKey(msg.sender, spender)] = value;
        emit Approval(msg.sender, spender, value);

        return true;
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _validateTransfer(msg.sender, msg.sender, to, 0, value, 0);
        
        uint256 fromBalance = StorageERC20.data().balances[msg.sender];
        if (fromBalance < value) {
            revert ERC20InsufficientBalance(msg.sender, fromBalance, value);
        }
        unchecked {
            // Overflow not possible: value <= fromBalance <= totalSupply.
            StorageERC20.data().balances[msg.sender] = fromBalance - value;

            // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
            StorageERC20.data().balances[to] += value;
        }

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

        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 `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }

        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }

        uint256 currentAllowance = allowance(from, msg.sender);

        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(msg.sender, currentAllowance, value);
            }
    
            unchecked {
                StorageERC20.data().allowances[_getAllowanceKey(from, msg.sender)] = currentAllowance - value;
            }
        }

        _validateTransfer(msg.sender, from, to, 0, value, 0);

        uint256 fromBalance = StorageERC20.data().balances[from];
        if (fromBalance < value) {
            revert ERC20InsufficientBalance(from, fromBalance, value);
        }

        unchecked {
            // Overflow not possible: value <= fromBalance <= totalSupply.
            StorageERC20.data().balances[from] = fromBalance - value;

            // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
            StorageERC20.data().balances[to] += value;
        }

        emit Transfer(from, to, value);

        return true;
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     */
    function _mint(address to, uint256 value) internal {
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }

        _validateMint(msg.sender, to, 0, value, msg.value);

        // Overflow check required: The rest of the code assumes that totalSupply never overflows
        StorageERC20.data().totalSupply += value;

        unchecked {
            // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
            StorageERC20.data().balances[to] += value;
        }

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

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     */
    function _burn(address from, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        
        _validateBurn(msg.sender, from, 0, value, msg.value);

        uint256 fromBalance = StorageERC20.data().balances[from];
        if (fromBalance < value) {
            revert ERC20InsufficientBalance(from, fromBalance, value);
        }
        
        unchecked {
            // Overflow not possible: value <= fromBalance <= totalSupply.
            StorageERC20.data().balances[from] = fromBalance - value;

            // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
            StorageERC20.data().totalSupply -= value;
        }

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

    function _getAllowanceKey(address owner, address spender) internal pure returns (bytes32 key) {
        assembly {
            mstore(0x00, owner)
            mstore(0x20, spender)
            key := keccak256(0x00, 0x40)
        }
    }
}

File 4 of 37 : ERC20C.sol
pragma solidity ^0.8.4;

import "./ERC20.sol";
import "../../utils/token/AutomaticValidatorTransferApproval.sol";
import "../../utils/token/CreatorTokenBase.sol";
import "../../utils/introspection/ERC165.sol";
import "../../utils/token/Constants.sol";

/**
 * @title ERC20CBase
 * @author Limit Break, Inc.
 * @notice Extends OpenZeppelin's ERC20 implementation with Creator Token functionality, which
 *         allows the contract owner to update the transfer validation logic by managing a security policy in
 *         an external transfer validation security policy registry.  See {CreatorTokenTransferValidator}.
 */
abstract contract ERC20CBase is ERC165, ERC20, CreatorTokenBase, AutomaticValidatorTransferApproval {
    constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) { }

    /**
     * @notice Overrides behavior of allowance such that if a spender is not explicitly approved,
     *         the contract owner can optionally auto-approve the 20-C transfer validator for transfers.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256 _allowance) {
        _allowance = super.allowance(owner, spender);

        if (_allowance == 0) {
            if (storageAutomaticValidatorTransferApproval().autoApproveTransfersFromValidator) {
                if (spender == address(getTransferValidator())) {
                    _allowance = type(uint256).max;
                }
            }
        }
    }

    /**
     * @notice Indicates whether the contract implements the specified interface.
     * @dev Overrides supportsInterface in ERC165.
     * @param interfaceId The interface id
     * @return true if the contract implements the specified interface, false otherwise
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return 
        interfaceId == type(IERC20).interfaceId || 
        interfaceId == type(IERC20Metadata).interfaceId || 
        interfaceId == type(ICreatorToken).interfaceId || 
        interfaceId == type(ICreatorTokenLegacy).interfaceId || 
        super.supportsInterface(interfaceId);
    }

    /**
     * @notice Returns the function selector for the transfer validator's validation function to be called 
     * @notice for transaction simulation. 
     */
    function getTransferValidationFunction() external pure returns (bytes4 functionSignature, bool isViewFunction) {
        functionSignature = bytes4(keccak256("validateTransfer(address,address,address,uint256,uint256)"));
        isViewFunction = false;
    }

    function _validateTransfer(
        address caller, 
        address from, 
        address to, 
        uint256 tokenId, 
        uint256 amount, 
        uint256 value
    ) internal virtual override {
        _preValidateTransfer(caller, from, to, tokenId, amount, value);
    }

    function _tokenType() internal pure override returns(uint16) {
        return uint16(TOKEN_TYPE_ERC20);
    }
}

/**
 * @title ERC20C
 * @author Limit Break, Inc.
 * @notice Extends OpenZeppelin's ERC20 implementation with Creator Token functionality, which
 *         allows the contract owner to update the transfer validation logic by managing a security policy in
 *         an external transfer validation security policy registry.  See {CreatorTokenTransferValidator}.
 */
abstract contract ERC20C is ERC20CBase {
    constructor(string memory name_, string memory symbol_) ERC20CBase(name_, symbol_) { }
}

/**
 * @title ERC20CInitializable
 * @author Limit Break, Inc.
 * @notice Initializable implementation of ERC20C to allow for EIP-1167 proxy clones.
 */
abstract contract ERC20CInitializable is ERC20CBase {
    error ERC20Initializable__AlreadyInitializedERC20();

    constructor() ERC20CBase("", "") { }

    function initializeERC20(string memory name_, string memory symbol_) public virtual {
        _requireCallerIsContractOwner();

        if(StorageERC20Initializable.data().erc20Initialized) {
            revert ERC20Initializable__AlreadyInitializedERC20();
        }

        StorageERC20Initializable.data().erc20Initialized = true;

        StorageERC20.data().name = name_;
        StorageERC20.data().symbol = symbol_;

        _emitDefaultTransferValidator();
        _registerTokenType(getTransferValidator());
    }
}

File 5 of 37 : IERC20.sol
pragma solidity ^0.8.4;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev 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);
}

File 6 of 37 : IERC20Errors.sol
pragma solidity ^0.8.4;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

File 7 of 37 : IERC20Metadata.sol
pragma solidity ^0.8.4;

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 8 of 37 : StorageERC20.sol
pragma solidity ^0.8.4;

library StorageERC20 {
    bytes32 private constant DATA_STORAGE_SLOT = keccak256("storage.ERC20");

    struct Data {
        uint256 totalSupply;

        string name;
        string symbol;

        mapping(address => uint256) balances;
        mapping (bytes32 => uint256) allowances;
    }

    function data() internal pure returns (Data storage ptr) {
        bytes32 slot = DATA_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }
}

library StorageERC20Initializable {
    bytes32 private constant DATA_STORAGE_SLOT = keccak256("storage.ERC20.Initializable");

    struct Data {
       bool erc20Initialized;
    }

    function data() internal pure returns (Data storage ptr) {
        bytes32 slot = DATA_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }
}

File 9 of 37 : SafeERC20.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

library SafeERC20 {
    /**
     * @dev A gas efficient, and fallback-safe method to transfer ERC20 tokens owned by the contract.
     * 
     * @param tokenAddress  The address of the token to transfer.
     * @param to            The address to transfer tokens to.
     * @param amount        The amount of tokens to transfer.
     * 
     * @return isError      True if there was an error transferring, false if the call was successful.
     */
    function safeTransfer(
        address tokenAddress,
        address to,
        uint256 amount
    ) internal returns(bool isError) {
        assembly {
            function _callTransfer(_tokenAddress, _to, _amount) -> _isError {
                let ptr := mload(0x40)
                mstore(0x40, add(ptr, 0x60))
                mstore(ptr, 0xa9059cbb)
                mstore(add(0x20, ptr), _to)
                mstore(add(0x40, ptr), _amount)
                if call(gas(), _tokenAddress, 0, add(ptr, 0x1C), 0x44, 0x00, 0x00) {
                    if lt(returndatasize(), 0x20) {
                        _isError := iszero(extcodesize(_tokenAddress))
                        leave
                    }
                    returndatacopy(0x00, 0x00, 0x20)
                    _isError := iszero(mload(0x00))
                    leave
                }
                _isError := true
            }
            isError := _callTransfer(tokenAddress, to, amount)
        }
    }

    /**
     * @dev A gas efficient, and fallback-safe method to transfer ERC20 tokens owned by another address.
     * 
     * @param tokenAddress  The address of the token to transfer.
     * @param from          The address to transfer tokens from.
     * @param to            The address to transfer tokens to.
     * @param amount        The amount of tokens to transfer.
     * 
     * @return isError      True if there was an error transferring, false if the call was successful.
     */
    function safeTransferFrom(
        address tokenAddress,
        address from,
        address to,
        uint256 amount
    ) internal returns(bool isError) {
        assembly {
            function _callTransferFrom(_tokenAddress, _from, _to, _amount) -> _isError {
                let ptr := mload(0x40)
                mstore(0x40, add(ptr, 0x80))
                mstore(ptr, 0x23b872dd)
                mstore(add(0x20, ptr), _from)
                mstore(add(0x40, ptr), _to)
                mstore(add(0x60, ptr), _amount)
                if call(gas(), _tokenAddress, 0, add(ptr, 0x1C), 0x64, 0x00, 0x00) {
                    if lt(returndatasize(), 0x20) {
                        _isError := iszero(extcodesize(_tokenAddress))
                        leave
                    }
                    returndatacopy(0x00, 0x00, 0x20)
                    _isError := iszero(mload(0x00))
                    leave
                }
                _isError := true
            }
            isError := _callTransferFrom(tokenAddress, from, to, amount)
        }
    }

    /**
     * @dev A gas efficient, and fallback-safe method to set approval on ERC20 tokens.
     * 
     * @param tokenAddress  The address of the token to transfer.
     * @param spender       The address to allow to spend tokens.
     * @param allowance     The amount of tokens to allow `spender` to transfer.
     * 
     * @return isError      True if there was an error setting allowance, false if the call was successful.
     */
    function safeApprove(
        address tokenAddress,
        address spender,
        uint256 allowance
    ) internal returns(bool isError) {
        assembly {
            function _callApprove(_tokenAddress, _spender, _allowance) -> _isError {
                let ptr := mload(0x40)
                mstore(0x40, add(ptr, 0x60))
                mstore(ptr, 0x095ea7b3)
                mstore(add(0x20, ptr), _spender)
                mstore(add(0x40, ptr), _allowance)
                if call(gas(), _tokenAddress, 0, add(ptr, 0x1C), 0x44, 0x00, 0x00) {
                    if lt(returndatasize(), 0x20) {
                        _isError := iszero(extcodesize(_tokenAddress))
                        leave
                    }
                    returndatacopy(0x00, 0x00, 0x20)
                    _isError := iszero(mload(0x00))
                    leave
                }
                _isError := true
            }
            isError := _callApprove(tokenAddress, spender, allowance)
        }
    }

    /**
     * @dev A gas efficient, and fallback-safe method to set approval on ERC20 tokens.
     * @dev If the initial approve fails, it will retry setting the allowance to zero and then
     * @dev to the new allowance.
     * 
     * @param tokenAddress  The address of the token to transfer.
     * @param spender       The address to allow to spend tokens.
     * @param allowance     The amount of tokens to allow `spender` to transfer.
     * 
     * @return isError      True if there was an error setting allowance, false if the call was successful.
     */
    function safeApproveWithRetryAfterZero(
        address tokenAddress,
        address spender,
        uint256 allowance
    ) internal returns(bool isError) {
        assembly {
            function _callApprove(_ptr, _tokenAddress, _spender, _allowance) -> _isError {
                mstore(add(0x40, _ptr), _allowance)
                if call(gas(), _tokenAddress, 0, add(_ptr, 0x1C), 0x44, 0x00, 0x00) {
                    if lt(returndatasize(), 0x20) {
                        _isError := iszero(extcodesize(_tokenAddress))
                        leave
                    }
                    returndatacopy(0x00, 0x00, 0x20)
                    _isError := iszero(mload(0x00))
                    leave
                }
                _isError := true
            }

            let ptr := mload(0x40)
            mstore(0x40, add(ptr, 0x60))
            mstore(ptr, 0x095ea7b3)
            mstore(add(0x20, ptr), spender)

            isError := _callApprove(ptr, tokenAddress, spender, allowance)
            if isError {
                pop(_callApprove(ptr, tokenAddress, spender, 0x00))
                isError := _callApprove(ptr, tokenAddress, spender, allowance)
            }
        }
    }
}

File 10 of 37 : IOwnableAccessControl.sol
pragma solidity ^0.8.4;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IOwnableAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

File 11 of 37 : Ownable.sol
pragma solidity ^0.8.4;

abstract contract Ownable {
    struct StorageOwnable {
        address owner;
    }

    bytes32 private constant OWNABLE_STORAGE_SLOT = keccak256("storage.Ownable");
    
    function storageOwnable() internal pure returns (StorageOwnable storage ptr) {
        bytes32 slot = OWNABLE_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != msg.sender) {
            revert OwnableUnauthorizedAccount(msg.sender);
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling 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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _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 = storageOwnable().owner;
        storageOwnable().owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 12 of 37 : Ownable2Step.sol
pragma solidity ^0.8.4;

import {Ownable} from "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This extension of the {Ownable} contract includes a two-step mechanism to transfer
 * ownership, where the new owner must call {acceptOwnership} in order to replace the
 * old one. This can help prevent common mistakes, such as transfers of ownership to
 * incorrect accounts, or to contracts that are unable to interact with the
 * permission system.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    struct StorageOwnable2Step {
        address pendingOwner;
    }

    bytes32 private constant OWNABLE_2STEP_STORAGE_SLOT = keccak256("storage.Ownable.2Step");
    
    function storageOwnable2Step() internal pure returns (StorageOwnable2Step storage ptr) {
        bytes32 slot = OWNABLE_2STEP_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        storageOwnable2Step().pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete storageOwnable2Step().pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = msg.sender;
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

File 13 of 37 : OwnableAccessControl.sol
pragma solidity ^0.8.4;

import "./IOwnableAccessControl.sol";
import "./OwnablePermissions.sol";

abstract contract OwnableAccessControl is OwnablePermissions, IOwnableAccessControl {
    struct RoleData {
        mapping(address account => bool) hasRole;
    }

    struct StorageOwnableAccessControl {
        mapping(bytes32 role => RoleData) roles;
    }

    bytes32 private constant OWNABLE_ACCESS_CONTROL_STORAGE_SLOT = keccak256("storage.OwnableAccessControl");
    
    function storageOwnableAccessControl() internal pure returns (StorageOwnableAccessControl storage ptr) {
        bytes32 slot = OWNABLE_ACCESS_CONTROL_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual {
        _requireCallerIsContractOwner();
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual {
        _requireCallerIsContractOwner();
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != msg.sender) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return storageOwnableAccessControl().roles[role].hasRole[account];
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            storageOwnableAccessControl().roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, msg.sender);
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            storageOwnableAccessControl().roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, msg.sender);
            return true;
        } else {
            return false;
        }
    }

        /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `msg.sender`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, msg.sender);
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }
}

File 14 of 37 : OwnablePermissions.sol
pragma solidity ^0.8.4;

abstract contract OwnablePermissions {
    function _requireCallerIsContractOwner() internal view virtual;
}

File 15 of 37 : ERC165.sol
pragma solidity ^0.8.4;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 16 of 37 : IERC165.sol
pragma solidity ^0.8.4;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * 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[ERC 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 17 of 37 : EnumerableSet.sol
pragma solidity ^0.8.4;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 18 of 37 : AutomaticValidatorTransferApproval.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../access/OwnablePermissions.sol";

/**
 * @title AutomaticValidatorTransferApproval
 * @author Limit Break, Inc.
 * @notice Base contract mix-in that provides boilerplate code giving the contract owner the
 *         option to automatically approve a 721-C transfer validator implementation for transfers.
 */
abstract contract AutomaticValidatorTransferApproval is OwnablePermissions {
    struct StorageAutomaticValidatorTransferApproval {
        bool autoApproveTransfersFromValidator;
    }

    bytes32 private constant AUTOMATIC_VALIDATOR_TRANSFER_APPROVAL_STORAGE_SLOT = keccak256("storage.AutomaticValidatorTransferApproval");
    
    function storageAutomaticValidatorTransferApproval() internal pure returns (StorageAutomaticValidatorTransferApproval storage ptr) {
        bytes32 slot = AUTOMATIC_VALIDATOR_TRANSFER_APPROVAL_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }

    /// @dev Emitted when the automatic approval flag is modified by the creator.
    event AutomaticApprovalOfTransferValidatorSet(bool autoApproved);

    /**
     * @notice Sets if the transfer validator is automatically approved as an operator for all token owners.
     * 
     * @dev    Throws when the caller is not the contract owner.
     * 
     * @param autoApprove If true, the collection's transfer validator will be automatically approved to
     *                    transfer holder's tokens.
     */
    function setAutomaticApprovalOfTransfersFromValidator(bool autoApprove) external {
        _requireCallerIsContractOwner();
        storageAutomaticValidatorTransferApproval().autoApproveTransfersFromValidator = autoApprove;
        emit AutomaticApprovalOfTransferValidatorSet(autoApprove);
    }

    function autoApproveTransfersFromValidator() public view returns (bool) {
        return storageAutomaticValidatorTransferApproval().autoApproveTransfersFromValidator;
    }
}

File 19 of 37 : Constants.sol
pragma solidity ^0.8.4;

/// @dev Constant value representing the ERC721 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC721 = 721;
/// @dev Constant value representing the ERC1155 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC1155 = 1155;
/// @dev Constant value representing the ERC20 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC20 = 20;

File 20 of 37 : CreatorTokenBase.sol
pragma solidity ^0.8.4;

import "./ICreatorToken.sol";
import "./ICreatorTokenLegacy.sol";
import "./ITransferValidator.sol";
import "./ITransferValidatorSetTokenType.sol";
import "./TransferValidation.sol";
import "../access/OwnablePermissions.sol";

/**
 * @title CreatorTokenBase
 * @author Limit Break, Inc.
 * @notice CreatorTokenBaseV3 is an abstract contract that provides basic functionality for managing token 
 * transfer policies through an implementation of ICreatorTokenTransferValidator/ICreatorTokenTransferValidatorV2/ICreatorTokenTransferValidatorV3. 
 * This contract is intended to be used as a base for creator-specific token contracts, enabling customizable transfer 
 * restrictions and security policies.
 *
 * <h4>Features:</h4>
 * <ul>Ownable: This contract can have an owner who can set and update the transfer validator.</ul>
 * <ul>TransferValidation: Implements the basic token transfer validation interface.</ul>
 *
 * <h4>Benefits:</h4>
 * <ul>Provides a flexible and modular way to implement custom token transfer restrictions and security policies.</ul>
 * <ul>Allows creators to enforce policies such as account and codehash blacklists, whitelists, and graylists.</ul>
 * <ul>Can be easily integrated into other token contracts as a base contract.</ul>
 *
 * <h4>Intended Usage:</h4>
 * <ul>Use as a base contract for creator token implementations that require advanced transfer restrictions and 
 *   security policies.</ul>
 * <ul>Set and update the ICreatorTokenTransferValidator implementation contract to enforce desired policies for the 
 *   creator token.</ul>
 *
 * <h4>Compatibility:</h4>
 * <ul>Backward and Forward Compatible - V1/V2/V3 Creator Token Base will work with V1/V2/V3 Transfer Validators.</ul>
 */
abstract contract CreatorTokenBase is OwnablePermissions, TransferValidation, ICreatorToken {
    struct StorageCreatorToken {
        bool isValidatorInitialized;
        address transferValidator;
    }

    bytes32 private constant CREATOR_TOKEN_STORAGE_SLOT = keccak256("storage.CreatorToken");
    
    function storageCreatorToken() internal pure returns (StorageCreatorToken storage ptr) {
        bytes32 slot = CREATOR_TOKEN_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }

    /// @dev Thrown when setting a transfer validator address that has no deployed code.
    error CreatorTokenBase__InvalidTransferValidatorContract();

    /// @dev The default transfer validator that will be used if no transfer validator has been set by the creator.
    address private immutable DEFAULT_TRANSFER_VALIDATOR;

    constructor(address defaultTransferValidator_) {
        DEFAULT_TRANSFER_VALIDATOR = defaultTransferValidator_;
        _emitDefaultTransferValidator();
        _registerTokenType(DEFAULT_TRANSFER_VALIDATOR);
    }

    /**
     * @notice Sets the transfer validator for the token contract.
     *
     * @dev    Throws when provided validator contract is not the zero address and does not have code.
     * @dev    Throws when the caller is not the contract owner.
     *
     * @dev    <h4>Postconditions:</h4>
     *         1. The transferValidator address is updated.
     *         2. The `TransferValidatorUpdated` event is emitted.
     *
     * @param transferValidator_ The address of the transfer validator contract.
     */
    function setTransferValidator(address transferValidator_) public {
        _requireCallerIsContractOwner();

        bool isValidTransferValidator = transferValidator_.code.length > 0;

        if(transferValidator_ != address(0) && !isValidTransferValidator) {
            revert CreatorTokenBase__InvalidTransferValidatorContract();
        }

        emit TransferValidatorUpdated(address(getTransferValidator()), transferValidator_);

        storageCreatorToken().isValidatorInitialized = true;
        storageCreatorToken().transferValidator = transferValidator_;

        _registerTokenType(transferValidator_);
    }

    /**
     * @notice Returns the default transfer validator contract address for this token contract.
     *         This validator will be used if no transfer validator has been set by the creator.
     */
    function getDefaultTransferValidator() public virtual view returns (address) {
        return DEFAULT_TRANSFER_VALIDATOR;
    }

    /**
     * @notice Returns the transfer validator contract address for this token contract.
     */
    function getTransferValidator() public view override returns (address validator) {
        validator = storageCreatorToken().transferValidator;

        if (validator == address(0)) {
            if (!storageCreatorToken().isValidatorInitialized) {
                validator = getDefaultTransferValidator();
            }
        }
    }

    /**
     * @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
     *      Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
     *      and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
     *
     * @dev Be aware that if the msg.sender is the transfer validator, the transfer is automatically permitted, as the
     *      transfer validator is expected to pre-validate the transfer.
     *
     * @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
     *      set to a non-zero address.
     *
     * @param caller  The address of the caller.
     * @param from    The address of the sender.
     * @param to      The address of the receiver.
     * @param tokenId The token id being transferred.
     */
    function _preValidateTransfer(
        address caller, 
        address from, 
        address to, 
        uint256 tokenId, 
        uint256 /*value*/) internal virtual override {
        address validator = getTransferValidator();

        if (validator != address(0)) {
            if (msg.sender == validator) {
                return;
            }

            ITransferValidator(validator).validateTransfer(caller, from, to, tokenId);
        }
    }

    /**
     * @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
     *      Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
     *      and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
     *
     * @dev Be aware that if the msg.sender is the transfer validator, the transfer is automatically permitted, as the
     *      transfer validator is expected to pre-validate the transfer.
     * 
     * @dev Used for ERC20 and ERC1155 token transfers which have an amount value to validate in the transfer validator.
     * @dev The `tokenId` for ERC20 tokens should be set to `0`.
     *
     * @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
     *      set to a non-zero address.
     *
     * @param caller  The address of the caller.
     * @param from    The address of the sender.
     * @param to      The address of the receiver.
     * @param tokenId The token id being transferred.
     * @param amount  The amount of token being transferred.
     */
    function _preValidateTransfer(
        address caller, 
        address from, 
        address to, 
        uint256 tokenId, 
        uint256 amount,
        uint256 /*value*/) internal virtual override {
        address validator = getTransferValidator();

        if (validator != address(0)) {
            if (msg.sender == validator) {
                return;
            }

            ITransferValidator(validator).validateTransfer(caller, from, to, tokenId, amount);
        }
    }

    function _tokenType() internal virtual pure returns(uint16);

    function _registerTokenType(address validator) internal {
        if (validator != address(0)) {
            uint256 validatorCodeSize;
            assembly {
                validatorCodeSize := extcodesize(validator)
            }
            if(validatorCodeSize > 0) {
                try ITransferValidatorSetTokenType(validator).setTokenTypeOfCollection(address(this), _tokenType()) {
                } catch { }
            }
        }
    }

    /**
     * @dev  Used during contract deployment for constructable and cloneable creator tokens
     * @dev  to emit the `TransferValidatorUpdated` event signaling the validator for the contract
     * @dev  is the default transfer validator.
     */
    function _emitDefaultTransferValidator() internal {
        emit TransferValidatorUpdated(address(0), getDefaultTransferValidator());
    }
}

File 21 of 37 : ICreatorToken.sol
pragma solidity ^0.8.4;

interface ICreatorToken {
    event TransferValidatorUpdated(address oldValidator, address newValidator);
    function getTransferValidator() external view returns (address validator);
    function setTransferValidator(address validator) external;
    function getTransferValidationFunction() external view returns (bytes4 functionSignature, bool isViewFunction);
}

File 22 of 37 : ICreatorTokenLegacy.sol
pragma solidity ^0.8.4;

interface ICreatorTokenLegacy {
    event TransferValidatorUpdated(address oldValidator, address newValidator);
    function getTransferValidator() external view returns (address validator);
    function setTransferValidator(address validator) external;
}

File 23 of 37 : ITransferValidator.sol
pragma solidity ^0.8.4;

interface ITransferValidator {
    function applyCollectionTransferPolicy(address caller, address from, address to) external view;
    function validateTransfer(address caller, address from, address to) external view;
    function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;
    function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;

    function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external;
    function afterAuthorizedTransfer(address token, uint256 tokenId) external;
    function beforeAuthorizedTransfer(address operator, address token) external;
    function afterAuthorizedTransfer(address token) external;
    function beforeAuthorizedTransfer(address token, uint256 tokenId) external;
    function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external;
    function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external;
}

File 24 of 37 : ITransferValidatorSetTokenType.sol
pragma solidity ^0.8.4;

interface ITransferValidatorSetTokenType {
    function setTokenTypeOfCollection(address collection, uint16 tokenType) external;
}

File 25 of 37 : TransferHooks.sol
pragma solidity ^0.8.4;

abstract contract TransferHooks {
    
    /*************************************************************************/
    /*                      Transfers Without Amounts                        */
    /*************************************************************************/

    /// @dev Optional validation hook that fires before a mint
    function _validateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a burn
    function _validateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a transfer
    function _validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}

    /*************************************************************************/
    /*                         Transfers With Amounts                        */
    /*************************************************************************/

    /// @dev Optional validation hook that fires before a mint
    function _validateMint(address caller, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a burn
    function _validateBurn(address caller, address from, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a transfer
    function _validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
}

File 26 of 37 : TransferValidation.sol
pragma solidity ^0.8.4;

/**
 * @title TransferValidation
 * @author Limit Break, Inc.
 * @notice A mix-in that can be combined with ERC-721 contracts to provide more granular hooks.
 * Openzeppelin's ERC721 contract only provides hooks for before and after transfer.  This allows
 * developers to validate or customize transfers within the context of a mint, a burn, or a transfer.
 */
abstract contract TransferValidation {
    
    /// @dev Thrown when the from and to address are both the zero address.
    error ShouldNotMintToBurnAddress();

    /*************************************************************************/
    /*                      Transfers Without Amounts                        */
    /*************************************************************************/

    /// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
    function _validateBeforeTransfer(address from, address to, uint256 tokenId) internal virtual {
        bool fromZeroAddress = from == address(0);
        bool toZeroAddress = to == address(0);

        if(fromZeroAddress && toZeroAddress) {
            revert ShouldNotMintToBurnAddress();
        } else if(fromZeroAddress) {
            _preValidateMint(msg.sender, to, tokenId, msg.value);
        } else if(toZeroAddress) {
            _preValidateBurn(msg.sender, from, tokenId, msg.value);
        } else {
            _preValidateTransfer(msg.sender, from, to, tokenId, msg.value);
        }
    }

    /// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
    function _validateAfterTransfer(address from, address to, uint256 tokenId) internal virtual {
        bool fromZeroAddress = from == address(0);
        bool toZeroAddress = to == address(0);

        if(fromZeroAddress && toZeroAddress) {
            revert ShouldNotMintToBurnAddress();
        } else if(fromZeroAddress) {
            _postValidateMint(msg.sender, to, tokenId, msg.value);
        } else if(toZeroAddress) {
            _postValidateBurn(msg.sender, from, tokenId, msg.value);
        } else {
            _postValidateTransfer(msg.sender, from, to, tokenId, msg.value);
        }
    }

    /// @dev Optional validation hook that fires before a mint
    function _preValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a mint
    function _postValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a burn
    function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a burn
    function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a transfer
    function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a transfer
    function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}

    /*************************************************************************/
    /*                         Transfers With Amounts                        */
    /*************************************************************************/

    /// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
    function _validateBeforeTransfer(address from, address to, uint256 tokenId, uint256 amount) internal virtual {
        bool fromZeroAddress = from == address(0);
        bool toZeroAddress = to == address(0);

        if(fromZeroAddress && toZeroAddress) {
            revert ShouldNotMintToBurnAddress();
        } else if(fromZeroAddress) {
            _preValidateMint(msg.sender, to, tokenId, amount, msg.value);
        } else if(toZeroAddress) {
            _preValidateBurn(msg.sender, from, tokenId, amount, msg.value);
        } else {
            _preValidateTransfer(msg.sender, from, to, tokenId, amount, msg.value);
        }
    }

    /// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
    function _validateAfterTransfer(address from, address to, uint256 tokenId, uint256 amount) internal virtual {
        bool fromZeroAddress = from == address(0);
        bool toZeroAddress = to == address(0);

        if(fromZeroAddress && toZeroAddress) {
            revert ShouldNotMintToBurnAddress();
        } else if(fromZeroAddress) {
            _postValidateMint(msg.sender, to, tokenId, amount, msg.value);
        } else if(toZeroAddress) {
            _postValidateBurn(msg.sender, from, tokenId, amount, msg.value);
        } else {
            _postValidateTransfer(msg.sender, from, to, tokenId, amount, msg.value);
        }
    }

    /// @dev Optional validation hook that fires before a mint
    function _preValidateMint(address caller, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a mint
    function _postValidateMint(address caller, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a burn
    function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a burn
    function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a transfer
    function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a transfer
    function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
}

File 27 of 37 : Constants.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

/// @dev Constant value for BPS where 1 BPS is 0.01%
uint16 constant BPS = 100_00;
/// @dev Constant value for zero.
uint256 constant ZERO = 0;
/// @dev Constant value for one.
uint256 constant ONE = 1;
// Target supply baseline is a uint48 multiplied by 10 ^ scale factor
// Limiting scale factor to 62 prevents an overflow of expected supply 
// since type(uint48).max * 10**62 equals 2.81e76.
// To overflow with target supply growth rate the current timestamp would need to exceed
// the baseline timestamp by 3.5e40 years.
uint16 constant MAX_BASELINE_SCALE_FACTOR = 62;

/// @dev Pausable flag to pause buys in a TokenMaster token.
uint256 constant PAUSE_FLAG_BUYS = 1 << 0;
/// @dev Pausable flag to pause sells in a TokenMaster token.
uint256 constant PAUSE_FLAG_SELLS = 1 << 1;
/// @dev Pausable flag to pause spends in a TokenMaster token.
uint256 constant PAUSE_FLAG_SPENDS = 1 << 2;

/// @dev EIP-712 typehash for deployments that require signature validation.
bytes32 constant DEPLOYMENT_TYPEHASH = keccak256("DeploymentParameters(address tokenFactory,bytes32 tokenSalt,address tokenAddress,bool blockTransactionsFromUntrustedChannels,bool restrictPairingToLists)");
/// @dev EIP-712 typehash for advanced buy orders.
bytes32 constant BUY_TYPEHASH = keccak256("BuyTokenMasterToken(bytes32 creatorBuyIdentifier,address tokenMasterToken,address tokenMasterOracle,address baseToken,uint256 baseValue,uint256 maxPerWallet,uint256 maxTotal,uint256 expiration,address hook,address cosigner)");
/// @dev EIP-712 typehash for advanced sell orders.
bytes32 constant SELL_TYPEHASH = keccak256("SellTokenMasterToken(bytes32 creatorSellIdentifier,address tokenMasterToken,address tokenMasterOracle,address baseToken,uint256 baseValue,uint256 maxPerWallet,uint256 maxTotal,uint256 expiration,address hook,address cosigner)");
/// @dev EIP-712 typehash for spend orders.
bytes32 constant SPEND_TYPEHASH = keccak256("SpendTokenMasterToken(bytes32 creatorSpendIdentifier,address tokenMasterToken,address tokenMasterOracle,address baseToken,uint256 baseValue,uint256 maxPerWallet,uint256 maxTotal,uint256 expiration,address hook,address cosigner)");
/// @dev EIP-712 tyephash for cosignatures.
bytes32 constant COSIGNATURE_TYPEHASH = keccak256("Cosignature(uint8 v,bytes32 r,bytes32 s,uint256 expiration,address executor)");
/// @dev EIP-712 typehash for advanced PermitC transfers.
bytes32 constant PERMITTED_TRANSFER_ADDITIONAL_DATA_BUY_TYPEHASH = keccak256("PermitTransferFromWithAdditionalData(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 expiration,uint256 masterNonce,AdvancedBuyOrder advancedBuyOrder)AdvancedBuyOrder(address tokenMasterToken,uint256 tokensToBuy,uint256 pairedValueIn,bytes32 creatorBuyIdentifier,address hook,uint8 buyOrderSignatureV,bytes32 buyOrderSignatureR,bytes32 buyOrderSignatureS)");
/// @dev EIP-712 typehash for the advanced data struct in PermitC advanced transfers.
bytes32 constant PERMITTED_TRANSFER_BUY_TYPEHASH = keccak256("AdvancedBuyOrder(address tokenMasterToken,uint256 tokensToBuy,uint256 pairedValueIn,bytes32 creatorBuyIdentifier,address hook,uint8 buyOrderSignatureV,bytes32 buyOrderSignatureR,bytes32 buyOrderSignatureS)");

/// @dev Role constant for roles in a token to grant an address the ability to manage orders.
bytes32 constant ORDER_MANAGER_ROLE = bytes32(bytes4(keccak256("ORDER_MANAGER")));

/// @dev Base amount of calldata expected for a buy order when not being called by a trusted forwarder.
// | 4        | 96       | = 100 bytes
// | selector | buyOrder |
uint256 constant BASE_MSG_LENGTH_BUY_ORDER = 100;
/// @dev Base amount of calldata expected for an advanced buy order when not being called by a trusted forwarder.
// | 4        | 96       | 32                 | 32                    | 640         | 192            | = 996 bytes
// | selector | buyOrder | signedOrder Offset | permitTransfer Offset | signedOrder | permitTransfer |
uint256 constant BASE_MSG_LENGTH_BUY_ORDER_ADVANCED = 996;
/// @dev Base amount of calldata expected for a sell order when not being called by a trusted forwarder.
// | 4        | 96        | = 100 bytes
// | selector | sellOrder |
uint256 constant BASE_MSG_LENGTH_SELL_ORDER = 100;
/// @dev Base amount of calldata expected for an advanced sell order when not being called by a trusted forwarder.
// | 4        | 96        | 32                 | 640         | = 772 bytes
// | selector | sellOrder | signedOrder Offset | signedOrder |
uint256 constant BASE_MSG_LENGTH_SELL_ORDER_ADVANCED = 772;
/// @dev Base amount of calldata expected for a spend order when not being called by a trusted forwarder.
// | 4        | 96         | 32                 | 640         | = 772 bytes
// | selector | spendOrder | signedOrder Offset | signedOrder |
uint256 constant BASE_MSG_LENGTH_SPEND_ORDER = 772;
/// @dev Base amount of calldata expected for a token deployment when not being called by a trusted forwarder.
// | 4        | 32                         | 96        | 672                  | = 996 bytes
// | selector | deploymentParmeters Offset | signature | deploymentParameters |
uint256 constant BASE_MSG_LENGTH_DEPLOY_TOKEN = 804;

/// @dev Token setting flag to indicate a token was deployed by TokenMaster.
uint8 constant FLAG_DEPLOYED_BY_TOKENMASTER = 1 << 0;
/// @dev Token setting flag to block transactions from untrusted channels.
uint8 constant FLAG_BLOCK_TRANSACTIONS_FROM_UNTRUSTED_CHANNELS = 1 << 1;
/// @dev Token setting flag to restrict pairing of the token to only allowed addresses.
uint8 constant FLAG_RESTRICT_PAIRING_TO_LISTS = 1 << 2;

/// @dev Base role constant for the TokenMaster Admin in the Role Server.
bytes32 constant TOKENMASTER_ADMIN_BASE_ROLE = keccak256("TOKENMASTER_ADMIN_ROLE");
/// @dev Base role constant for the TokenMaster Deployment Signer in the Role Server.
bytes32 constant TOKENMASTER_SIGNER_BASE_ROLE = keccak256("TOKENMASTER_SIGNER_ROLE");
/// @dev Base role constant for the TokenMaster Fee Receiver in the Role Server.
bytes32 constant TOKENMASTER_FEE_RECEIVER_BASE_ROLE = keccak256("TOKENMASTER_FEE_RECEIVER");
/// @dev Base role constant for the TokenMaster Fee Collector in the Role Server.
bytes32 constant TOKENMASTER_FEE_COLLECTOR_BASE_ROLE = keccak256("TOKENMASTER_FEE_COLLECTOR");

/// @dev Transaction type value passed to a TokenMasterOracle when the transaction being executed is a buy.
uint256 constant ORACLE_BUY_TRANSACTION_TYPE = 0;
/// @dev Transaction type value passed to a TokenMasterOracle when the transaction being executed is a sell.
uint256 constant ORACLE_SELL_TRANSACTION_TYPE = 1;
/// @dev Transaction type value passed to a TokenMasterOracle when the transaction being executed is a spend.
uint256 constant ORACLE_SPEND_TRANSACTION_TYPE = 2;

/// @dev Constant value for the maximum address value for masking in factories.
address constant ADDRESS_MASK = address(type(uint160).max);

File 28 of 37 : DataTypes.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "@limitbreak/tm-core-lib/src/utils/structs/EnumerableSet.sol";

/**
 * @dev This struct defines storage for token settings.
 * 
 * @dev **flags**: Bit packed flags for token settings defined in constants.
 * @dev **spacer**: Unused
 * @dev **partnerFeeRecipient**: Address of the partner fee recipient for a token.
 * @dev **proposedPartnerFeeRecipient**: Address proposed by the current partner fee recipient to be the new fee recipient.
 * @dev **orderSigners**: Enumerable list of addresses that are allowed order signers.
 * @dev **trustedChannels**: Enumerable list of channels that are allowed for token transactions.
 * @dev **allowedPairToDeployers**: Enumberable list of deployers that are allowed to deploy tokens paired with the token.
 * @dev **allowedPairToTokens**: Enumberable list of tokens that are allowed to deploy as paired with the token.
 */
struct TokenSettings {
    uint8 flags;
    uint248 spacer;
    address partnerFeeRecipient;
    address proposedPartnerFeeRecipient;
    EnumerableSet.AddressSet orderSigners;
    EnumerableSet.AddressSet trustedChannels;
    EnumerableSet.AddressSet allowedPairToDeployers;
    EnumerableSet.AddressSet allowedPairToTokens;
}

/**
 * @dev This struct defines parameters used by the TokenMasterRouter and factories for deploying tokens.
 * 
 * @dev **tokenFactory**: The token factory to use to deploy a specific pool type.
 * @dev **tokenSalt**: The salt value to use when deploying the token to control the deterministic address.
 * @dev **tokenAddress**: The deterministic address of the token that will be deployed.
 * @dev **blockTransactionsFromUntrustedChannels**: Initial setting for blocking transactions from untrusted channels.
 * @dev **restrictPairingToLists**: Initial setting for restricting pairing of the new token with other tokens.
 * @dev **poolParams**: The parameters that will be sent during token contract construction.
 * @dev **maxInfrastructureFeeBPS**: The maximum infrastructure fee that is allowed without reverting the deployment.
 */
struct DeploymentParameters {
    address tokenFactory;
    bytes32 tokenSalt;
    address tokenAddress;
    bool blockTransactionsFromUntrustedChannels;
    bool restrictPairingToLists;
    PoolDeploymentParameters poolParams;
    uint16 maxInfrastructureFeeBPS;
}

/**
 * @dev This struct defines parameters that are sent by token factories to create a token contract.
 * 
 * @dev **name**: The name of the token.
 * @dev **symbol**: The symbol of the token.
 * @dev **decimals**: The number of decimals of the token.
 * @dev **initialOwner**: Address to set as the initial owner of the token.
 * @dev **pairedToken**: Address of the token to pair with the new token, for native token use `address(0)`.
 * @dev **initialPairedTokenToDeposit**: Amount of paired token to deposit to the new token pool.
 * @dev **encodedInitializationArgs**: Bytes array of ABI encoded initialization arguments to allow new pool types 
 * @dev with different types of constructor arguments that are decoded during deployment.
 * @dev **defaultTransferValidator**: Address of the initial transfer validator for a token.
 * @dev **useRouterForPairedTransfers**: If true, the pool will default to allowing the router to transfer paired tokens
 * @dev during operations that require the paired token to transfer from the pool. This is useful when pairing with
 * @dev ERC20C tokens that utilize the default operator whitelist which includes the TokenMasterRouter but does not
 * @dev include individual token pools.
 * @dev **partnerFeeRecipient**: The address that will receive partner fee shares.
 * @dev **partnerFeeBPS**: The fee rate in BPS for partner fees.
 */
struct PoolDeploymentParameters {
    string name;
    string symbol;
    uint8 tokenDecimals;
    address initialOwner;
    address pairedToken;
    uint256 initialPairedTokenToDeposit;
    bytes encodedInitializationArgs;
    address defaultTransferValidator;
    bool useRouterForPairedTransfers;
    address partnerFeeRecipient;
    uint256 partnerFeeBPS;
}

/**
 * @dev This struct defines storage for tracking advanced orders.
 * 
 * @dev **orderDisabled**: True if the order has been disabled by the creator.
 * @dev **orderTotal**: The total amount executed by all users on the order.
 * @dev **orderTotalPerWallet**: The total amount per wallet executed on the order.
 */
struct OrderTracking {
    bool orderDisabled;
    uint256 orderTotal;
    mapping (address => uint256) orderTotalPerWallet;
}

/**
 * @dev This struct defines buy order base parameters.
 * 
 * @dev **tokenMasterToken**: The address of the TokenMaster token to buy.
 * @dev **tokensToBuy**: The amount of tokens to buy.
 * @dev **pairedValueIn**: The amount of paired tokens to transfer in to the token contract for the purchase.
 */
struct BuyOrder {
    address tokenMasterToken;
    uint256 tokensToBuy;
    uint256 pairedValueIn;
}

/**
 * @dev This struct defines a permit transfer parameters.
 * 
 * @dev **permitProcessor**: The address of the PermitC-compliant permit processor to use for the transfer.
 * @dev **nonce**: The permit nonce to use for the permit transfer signature validation.
 * @dev **permitAmount**: The amount that the permit was signed for.
 * @dev **expiration**: The time, in seconds since the Unix epoch, that the permit will expire.
 * @dev **signedPermit**: The permit signature bytes authorizing the transfer.
 */
struct PermitTransfer {
    address permitProcessor;
    uint256 nonce;
    uint256 permitAmount;
    uint256 expiration;
    bytes signedPermit;
}

/**
 * @dev This struct defines sell order base parameters.
 * 
 * @dev **tokenMasterToken**: The address of the TokenMaster token to sell.
 * @dev **tokensToSell**: The amount of tokens to sell.
 * @dev **minimumOut**: The minimum output of paired tokens to be received by the seller without the transaction reverting.
 */
struct SellOrder {
    address tokenMasterToken;
    uint256 tokensToSell;
    uint256 minimumOut;
}

/**
 * @dev This struct defines spend order base parameters.
 * 
 * @dev **tokenMasterToken**: The address of the TokenMaster token to spend.
 * @dev **multiplier**: The multiplier of the signed spend order's `baseValue`, adjusted by an oracle if specified, to be spent.
 * @dev **maxAmountToSpend**: The maximum amount the spender will spend on the order without the transaction reverting.
 */
struct SpendOrder {
    address tokenMasterToken;
    uint256 multiplier;
    uint256 maxAmountToSpend;
}

/**
 * @dev This struct defines advanced order execution parameters.
 * 
 * @dev **creatorIdentifier**: A value specified by the creator to identify the order for any onchain or offchain benefits
 * @dev to the order executor for executing the order.
 * @dev **tokenMasterOracle**: An address for an onchain oracle that can adjust the `baseValue` for an advanced order.
 * @dev **baseToken**: An address for a token to base the `baseValue` on when adjusting value through a TokenMaster Oracle.
 * @dev **baseValue**: The amount of token required for the order to be executed.
 * @dev If `tokenMasterOracle` is set to `address(0)`, the `baseToken` will not be utilized and the advanced order will 
 * @dev execute with `baseValue` being the amount of the TokenMaster token to be required for the order.
 * @dev **maxPerWallet**: The maximum amount per wallet that can be executed on the order. For buy and sell advanced orders
 * @dev this amount is in the TokenMaster token amount, for spend orders it is multipliers.
 * @dev **maxPerWallet**: The maximum amount for all wallets that can be executed on the order. For buy and sell advanced orders
 * @dev this amount is in the TokenMaster token amount, for spend orders it is multipliers.
 * @dev **expiration**: The time, in seconds since the Unix epoch, that the order will expire.
 * @dev **hook**: An address for an onchain hook for an order to execute after the buy, sell or spend is executed.
 * @dev **signature**: The signature from an allowed order signer to authorize the order.
 * @dev **cosignature**: The cosignature from the cosigner specified by the order signer.
 * @dev **hookExtraData**: Extra data to send with the call to the onchain hook contract.
 * @dev **oracleExtraData**: Extra data to send with the call to the oracle contract.
 */
struct SignedOrder {
    bytes32 creatorIdentifier;
    address tokenMasterOracle;
    address baseToken;
    uint256 baseValue;
    uint256 maxPerWallet;
    uint256 maxTotal;
    uint256 expiration;
    address hook;
    SignatureECDSA signature;
    Cosignature cosignature;
    bytes hookExtraData;
    bytes oracleExtraData;
}

/**
 * @dev The `v`, `r`, and `s` components of an ECDSA signature.  For more information
 *      [refer to this article](https://medium.com/mycrypto/the-magic-of-digital-signatures-on-ethereum-98fe184dc9c7).
 */
struct SignatureECDSA {
    uint256 v;
    bytes32 r;
    bytes32 s;
}

/**
 * @dev This struct defines the cosignature for verifying an order that is a cosigned order.
 *
 * @dev **signer**: The address that signed the cosigned order. This must match the cosigner that is part of the order signature.
 * @dev **expiration**: The time, in seconds since the Unix epoch, that the cosignature will expire.
 * @dev The `v`, `r`, and `s` components of an ECDSA signature.  For more information
 *      [refer to this article](https://medium.com/mycrypto/the-magic-of-digital-signatures-on-ethereum-98fe184dc9c7).
 */
struct Cosignature {
    address signer;
    uint256 expiration;
    uint256 v;
    bytes32 r;
    bytes32 s;
}

File 29 of 37 : Errors.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

/// @dev Thrown when a signature `v` value exceeds 255.
error Error__InvalidSignatureV();

// TokenMaster Errors
/// @dev Thrown when a deployment call is made to a token factory and it does not originate from the TokenMasterRouter.
error TokenMasterFactory__CallerMustBeRouter();
/// @dev Thrown when deploying a token factory and the TokenMasterRouter address is not set in the factory configuration contract.
error TokenMasterFactory__RouterAddressNotSet();

/// @dev Thrown when the amount of tokens to be spent exceeds the maximum amount specified by the spender.
error TokenMasterRouter__AmountToSpendExceedsMax();
/// @dev Thrown when calldata length does not match the expected calldata length.
error TokenMasterRouter__BadCalldataLength();
/// @dev Thrown when a caller is attempting to execute a permissioned function they are not permitted for.
error TokenMasterRouter__CallerNotAllowed();
/// @dev Thrown when the block time is after the expiration of a cosignature.
error TokenMasterRouter__CosignatureExpired();
/// @dev Thrown when a cosigner is specified on an advanced order and the cosignature is invalid.
error TokenMasterRouter__CosignatureInvalid();
/// @dev Thrown when the deterministic address specified in deployment parameters does not match the address returned from a token factory.
error TokenMasterRouter__DeployedTokenAddressMismatch();
/// @dev Thrown when a new TokenMaster token deployment expects to deposit native funds to the pool and the funds fail to transfer.
error TokenMasterRouter__FailedToDepositInitialPairedFunds();
/// @dev Thrown when the paired token value fails to transfer to or from a pool.
error TokenMasterRouter__FailedToTransferPairedToken();
/// @dev Thrown when deployment signing is enabled and the supplied signature is invalid.
error TokenMasterRouter__InvalidDeploymentSignature();
/// @dev Thrown when the TokenMaster admin sets the infrastructure fee greater than 10_000 or during deployment when the current fee exceeds the max specified fee.
error TokenMasterRouter__InvalidInfrastructureFeeBPS();
/// @dev Thrown when a TokenMaster token owner attempts to accept the zero address as the new partner fee recipient.
error TokenMasterRouter__InvalidRecipient();
/// @dev Thrown when the message value for a deployment pairing with native tokens does not match the specified initial paired token to deposit.
error TokenMasterRouter__InvalidMessageValue();
/// @dev Thrown when transferring ERC20 tokens to a pool and native value is sent with the call to the router.
error TokenMasterRouter__NativeValueNotAllowedOnERC20();
/// @dev Thrown when an advanced order has expired.
error TokenMasterRouter__OrderExpired();
/// @dev Thrown when an advanced order has been disabled.
error TokenMasterRouter__OrderDisabled();
/// @dev Thrown when the amount of tokens being bought or sold on an advanced order does not meet the order's minimum amount.
error TokenMasterRouter__OrderDoesNotMeetMinimum();
/// @dev Thrown when the cumulative amount being bought, sold or spent on an advanced order exceeds the order's maximum total.
error TokenMasterRouter__OrderMaxTotalExceeded();
/// @dev Thrown when the cumulative amount being bought, sold or spent on an advanced order by a user exceeds the order's maximum for one user.
error TokenMasterRouter__OrderMaxPerWalletExceeded();
/// @dev Thrown when the supplied signature for an advanced order recovers to an address that is not authorized as an order signer.
error TokenMasterRouter__OrderSignerUnauthorized();
/// @dev Thrown when pairing with a token that has enabled pairing restrictions and the deployer or token are not specified as allowed.
error TokenMasterRouter__PairedTokenPairingRestricted();
/// @dev Thrown when attempting to use PermitC transfers with a token that is paired with the chain native token.
error TokenMasterRouter__PermitNotCompatibleWithNativeValue();
/// @dev Thrown when the permit transfer fails to execute the transfer of tokens to the pool.
error TokenMasterRouter__PermitTransferFailed();
/// @dev Thrown when deploying a token with a token factory specified that is not allowed by TokenMasterRouter.
error TokenMasterRouter__TokenFactoryNotAllowed();
/// @dev Thrown when attempting to buy, sell or spend a token that was not deployed with TokenMaster.
error TokenMasterRouter__TokenNotDeployedByTokenMaster();
/// @dev Thrown when a token has disabled transactions from untrusted channels and the call originates from a caller that is not trusted.
error TokenMasterRouter__TransactionOriginatedFromUntrustedChannel();

/// @dev Thrown when an address other than the router calls a function in a token that must be called by the router.
error TokenMasterERC20__CallerMustBeRouter();
/// @dev Thrown when attempting to withdraw an unrelated token from the pool and the address specified is the paired token.
error TokenMasterERC20__CannotWithdrawPairedToken();
/// @dev Thrown when attempting to withdraw an unrelated ERC20 token from the pool and the transfer fails.
error TokenMasterERC20__ERC20TransferFailed();
/// @dev Thrown when attempting to reset the token approval for the router to transfer paired tokens and the approval fails.
error TokenMasterERC20__FailedToSetApproval();
/// @dev Thrown when attempting to forfeit claimable emissions in an amount greater than the current claimable amount.
error TokenMasterERC20__ForfeitAmountGreaterThanClaimable();
/// @dev Thrown when the initial paired amount supplied is zero.
error TokenMasterERC20__InitialPairedDepositCannotBeZero();
/// @dev Thrown when the initial supply amount specified is zero.
error TokenMasterERC20__InitialSupplyCannotBeZero();
/// @dev Thrown when the amount of value supplied for a purchase of tokens is insufficient for the cost.
error TokenMasterERC20__InsufficientBuyInput();
/// @dev Thrown when the output value of paired tokens does not meet the sellers supplied minimum output.
error TokenMasterERC20__InsufficientSellOutput();
/// @dev Thrown when attempting to deploy or purchase an exceptionally large quantity of tokens that could destablize the token.
error TokenMasterERC20__InvalidPairedValues();
/// @dev Thrown when parameters that are being set are not within a valid range.
error TokenMasterERC20__InvalidParameters();
/// @dev Thrown when a transfer of native token value fails to execute.
error TokenMasterERC20__NativeTransferFailed();
/// @dev Thrown when adjusting the hard cap on claimable emissions and the value supplied is greater than the current cap.
error TokenMasterERC20__NewHardCapGreaterThanCurrent();
/// @dev Thrown when attempting to renounce ownership of a token contract.
error TokenMasterERC20__RenounceNotAllowed();
/// @dev Thrown when attempting to withdraw or transfer a creator share to market in an amount greater than the current creator share.
error TokenMasterERC20__WithdrawOrTransferAmountGreaterThanShare();

/// @dev Thrown when calling a function in a token contract that is not supported by the pool type.
error TokenMasterERC20__OperationNotSupportedByPool();
/// @dev Thrown when deploying a pool and an insufficient amount of value is provided for the initial supply requested.
error TokenMasterERC20__InsufficientSeedFunding();
/// @dev Thrown when multiple arrays are expected to be of equal lengths and their lengths are not equal.
error TokenMasterERC20__ArrayLengthMismatch();

File 30 of 37 : IMinterBurnerRolePool.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

/**
 * @title  IMinterBurnerRolePool
 * @author Limit Break, Inc.
 * @notice Interface definition for pools that implement external minting and burning functions.
 */
interface IMinterBurnerRolePool {
    function mint(address to, uint256 amount) external;
    function mintBatch(address[] calldata toAddresses, uint256[] calldata amounts) external;
    function burn(address from, uint256 amount) external;
}

File 31 of 37 : ITokenMasterERC20C.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

/**
 * @title  ITokenMasterERC20C
 * @author Limit Break, Inc.
 * @notice Interface that must be implemented by token contracts that will be
 * @notice deployed through TokenMasterRouter.
 */
interface ITokenMasterERC20C {

    /// @dev Emitted when a creator withdraws their share or when fees are withdrawn.
    event CreatorShareWithdrawn(address to, uint256 withdrawAmount, uint256 infrastructureAmount, uint256 partnerAmount);

    /// @dev Emitted when a creator transfers a portion of their share to the market bonded value. Infrastructure and partner amounts are transferred to their respective receivers.
    event CreatorShareTransferredToMarket(address to, uint256 transferAmount, uint256 infrastructureAmount, uint256 partnerAmount);
    
    function PAIRED_TOKEN() external view returns(address);
    function buyTokens(
        address buyer,
        uint256 pairedTokenIn,
        uint256 pooledTokenToBuy
    ) external payable returns(uint256 totalCost, uint256 refundByRouterAmount);
    function sellTokens(
        address seller,
        uint256 pooledTokenToSell,
        uint256 pairedTokenMinimumOut
    ) external returns (address pairedToken, uint256 pairedValueToSeller, uint256 transferByRouterAmount);
    function spendTokens(address spender, uint256 pooledTokenToSpend) external;
    function withdrawCreatorShare(
        address withdrawTo,
        uint256 withdrawAmount,
        address infrastructureFeeRecipient,
        address partnerFeeRecipient
    ) external returns (
        address pairedToken,
        uint256 transferByRouterAmountCreator,
        uint256 transferByRouterAmountInfrastructure,
        uint256 transferByRouterAmountPartner
    );
    function transferCreatorShareToMarket(
        uint256 transferAmount,
        address infrastructureFeeRecipient,
        address partnerFeeRecipient
    ) external returns(address pairedToken, uint256 transferByRouterAmountInfrastructure, uint256 transferByRouterAmountPartner);
    function withdrawFees(
        address infrastructureFeeRecipient,
        address partnerFeeRecipient
    ) external returns (
        address pairedToken,
        uint256 transferByRouterAmountInfrastructure,
        uint256 transferByRouterAmountPartner
    );
    function withdrawUnrelatedToken(address tokenAddress, address withdrawTo, uint256 withdrawAmount) external;
    function resetPairedTokenApproval() external;
    function pairedTokenShares() external view returns(uint256 marketShare, uint256 creatorShare, uint256 infrastructureShare, uint256 partnerShare);
}

File 32 of 37 : ITokenMasterFactory.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "../DataTypes.sol";

/**
 * @title  ITokenMasterFactory
 * @author Limit Break, Inc.
 * @notice Interface that must be implemented by contracts that are factories
 * @notice for tokens that will be deployed through TokenMasterRouter.
 */
interface ITokenMasterFactory {
    function deployToken(
        bytes32 tokenSalt,
        PoolDeploymentParameters calldata poolParams,
        uint256 pairedValueIn,
        uint256 infrastructureFeeBPS
    ) external returns(address deployedAddress);

    function computeDeploymentAddress(
        bytes32 tokenSalt,
        PoolDeploymentParameters calldata poolParams,
        uint256 pairedValueIn,
        uint256 infrastructureFeeBPS
    ) external view returns(address deploymentAddress);
}

File 33 of 37 : BondedPool.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "../Constants.sol";
import "../DataTypes.sol";
import "../Errors.sol";

import "../interfaces/ITokenMasterERC20C.sol";

import "@limitbreak/tm-core-lib/src/token/erc20/ERC20C.sol";
import "@limitbreak/tm-core-lib/src/token/erc20/utils/SafeERC20.sol";
import "@limitbreak/tm-core-lib/src/utils/access/Ownable2Step.sol";
import "@limitbreak/tm-core-lib/src/utils/access/OwnableAccessControl.sol";

/**
 * @title  BondedPool
 * @author Limit Break, Inc.
 * @notice The BondedPool contract implements common code for all tokens deployed by TokenMaster.
 * 
 * @dev    Specific pool implementations must implement additional functionality to comply with 
 * @dev    ITokenMasterERC20C and may extend or override BondedPool logic.
 */
abstract contract BondedPool is Ownable2Step, OwnableAccessControl, ERC20C, ITokenMasterERC20C {

    /// @dev The number of decimals for the ERC20 token.
    uint8 internal immutable DECIMALS;
    /// @dev Address of the token paired with the pool.
    address public immutable PAIRED_TOKEN;
    /// @dev Address of the TokenMasterRouter.
    address internal immutable ROUTER;
    /// @dev Infrastructure fee rate in BPS.
    uint16 internal immutable INFRASTRUCTURE_FEE_BPS;
    /// @dev Partner fee rate in BPS.
    uint16 internal immutable PARTNER_FEE_BPS;

    /// @dev Amount of creator share that has already paid infra/partner fees.
    uint256 internal creatorShareFeesPaidAdjustment;

    /// @dev Internal function pointer to the shares function, set during contract construction to either ERC20 or native token version.
    function() internal view returns(uint256,uint256,uint256) internal immutable _creatorPairedTokenShare;
    /// @dev Internal function pointer to the paired token transfer function, set during contract construction for ERC20 or native transfers.
    function(address,uint256) internal returns(uint256) internal immutable _transferPairedToken;

    constructor(
        PoolDeploymentParameters memory deploymentParams,
        uint256 infrastructureFeeBPS,
        address router
    )
    ERC20C(deploymentParams.name, deploymentParams.symbol)
    Ownable(deploymentParams.initialOwner)
    CreatorTokenBase(deploymentParams.defaultTransferValidator) {

        if (
            infrastructureFeeBPS > BPS || 
            (
                deploymentParams.partnerFeeBPS > 0 && 
                (deploymentParams.partnerFeeRecipient == address(0) || deploymentParams.partnerFeeBPS > BPS)
            )
        ) {
            revert TokenMasterERC20__InvalidParameters();
        }

        DECIMALS = deploymentParams.tokenDecimals;
        PAIRED_TOKEN = deploymentParams.pairedToken;
        ROUTER = router;
        INFRASTRUCTURE_FEE_BPS = uint16(infrastructureFeeBPS);
        PARTNER_FEE_BPS = uint16(deploymentParams.partnerFeeBPS);

        if (deploymentParams.pairedToken == address(0)) {
            if (deploymentParams.useRouterForPairedTransfers) {
                revert TokenMasterERC20__InvalidParameters();
            }
            _creatorPairedTokenShare = _creatorPairedTokenShareNative;
            _transferPairedToken = _transferPairedTokenNative;
        } else {
            _creatorPairedTokenShare = _creatorPairedTokenShareERC20;
            if (deploymentParams.useRouterForPairedTransfers) {
                _transferPairedToken = _transferPairedTokenERC20RouterTransfer;
                SafeERC20.safeApprove(PAIRED_TOKEN, ROUTER, type(uint256).max);
            } else {
                _transferPairedToken = _transferPairedTokenERC20PoolTransfer;
            }
        }
    }

    /*************************************************************************/
    /*                           CREATOR FUNCTIONS                           */
    /*************************************************************************/

    /**
     * @notice  Withdraws an amount of creator share to an address specified by the creator.
     * 
     * @dev     The entire infrastructure and partner shares will be withdrawn when this function is called.
     * @dev     When the paired token is ERC20 and tokens fail to transfer by the contract, the transfer
     * @dev     will fall back to attempting to transfer by the router contract.
     * 
     * @dev     Throws when the caller is not the TokenMasterRouter.
     * @dev     Throws when the amount to withdraw exceeds the creator share.
     * @dev     Throws when the paired token is native and a share transfer fails.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Withdraw amount has been transferred to the withdraw to address.
     * @dev    2. Partner fees have been transferred to the partner fee receiver.
     * @dev    3. Infrastructure fees have been transferred to the infrastructure fee receiver.
     * @dev    4. A `CreatorShareWithdrawn` event has been emitted.
     * 
     * @param withdrawTo                  Address to withdraw creator share to.
     * @param withdrawAmount              Amount to withdraw from creator share.
     * @param infrastructureFeeRecipient  Address of the infra fee receipient.
     * @param partnerFeeRecipient         Address of the partner fee recipient.
     * 
     * @return pairedToken                           Address of the paired token for router transfers.
     * @return transferByRouterAmountCreator         Amount of paired token to transfer to creator by router.
     * @return transferByRouterAmountInfrastructure  Amount of paired token to transfer to infra by router.
     * @return transferByRouterAmountPartner         Amount of paired token to transfer to partner by router.
     */
    function withdrawCreatorShare(
        address withdrawTo,
        uint256 withdrawAmount,
        address infrastructureFeeRecipient,
        address partnerFeeRecipient
    ) external  virtual  returns (
        address pairedToken,
        uint256 transferByRouterAmountCreator,
        uint256 transferByRouterAmountInfrastructure,
        uint256 transferByRouterAmountPartner
    ) {
        _callerIsRouter();

        (
            uint256 _creatorShare,
            uint256 _infrastructureShare,
            uint256 _partnerShare
        ) = _creatorPairedTokenShare();

        if (withdrawAmount > _creatorShare) {
            revert TokenMasterERC20__WithdrawOrTransferAmountGreaterThanShare();
        } else if (withdrawAmount < _creatorShare) {
            unchecked {
                // Entire infrastructure/partner fee is withdrawn even when creator withdraws a partial amount
                // set creator share fee paid adjustment to the amount remaining to account for the
                // fees already being paid on that portion.
                creatorShareFeesPaidAdjustment = _creatorShare - withdrawAmount;
            }
        } else {
            // Creator is withdrawing their entire share, reset creator share fee paid adjustment to zero
            creatorShareFeesPaidAdjustment = 0;
        }
        
        pairedToken = PAIRED_TOKEN;
        transferByRouterAmountCreator = _transferPairedToken(withdrawTo, withdrawAmount);
        transferByRouterAmountInfrastructure = _transferPairedToken(infrastructureFeeRecipient, _infrastructureShare);
        transferByRouterAmountPartner = _transferPairedToken(partnerFeeRecipient, _partnerShare);

        emit CreatorShareWithdrawn(withdrawTo, withdrawAmount, _infrastructureShare, _partnerShare);
    }

    /**
     * @notice  Transfers an amount of creator share to the market. This function is disabled by 
     * @notice  default and must be overriden by a pool implementation to be utilized.
     * 
     * @dev     Throws when not implemented by a pool implementation.
     */
    function transferCreatorShareToMarket(
        uint256 /*transferAmount*/,
        address /*infrastructureFeeRecipient*/,
        address /*partnerFeeRecipient*/
    ) external virtual returns (
        address /*pairedToken*/,
        uint256 /*transferByRouterAmountInfrastructure*/,
        uint256 /*transferByRouterAmountPartner*/
    ) {
        revert TokenMasterERC20__OperationNotSupportedByPool();
    }

    /**
     * @notice  Withdraws partner and infrastructure fees for the pool.
     * 
     * @dev     When the paired token is ERC20 and tokens fail to transfer by the contract, the transfer
     * @dev     will fall back to attempting to transfer by the router contract.
     * 
     * @dev     Throws when the caller is not the TokenMasterRouter.
     * @dev     Throws when the paired token is native and a share transfer fails.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Partner fees have been transferred to the partner fee receiver.
     * @dev    2. Infrastructure fees have been transferred to the infrastructure fee receiver.
     * @dev    3. A `CreatorShareWithdrawn` event has been emitted.
     * 
     * @param infrastructureFeeRecipient  Address of the infra fee receipient.
     * @param partnerFeeRecipient         Address of the partner fee recipient.
     * 
     * @return pairedToken                           Address of the paired token for router transfers.
     * @return transferByRouterAmountInfrastructure  Amount of paired token to transfer to infra by router.
     * @return transferByRouterAmountPartner         Amount of paired token to transfer to partner by router.
     */
    function withdrawFees(
        address infrastructureFeeRecipient,
        address partnerFeeRecipient
    ) external virtual returns (
        address pairedToken,
        uint256 transferByRouterAmountInfrastructure,
        uint256 transferByRouterAmountPartner
    ) {
        _callerIsRouter();

        (
            uint256 _creatorShare,
            uint256 _infrastructureShare,
            uint256 _partnerShare
        ) = _creatorPairedTokenShare();

        // Entire infrastructure/partner fee is withdrawn set creator share fee paid
        // adjustment to the total creator share to account for fees being paid
        creatorShareFeesPaidAdjustment = _creatorShare;

        pairedToken = PAIRED_TOKEN;
        transferByRouterAmountInfrastructure = _transferPairedToken(infrastructureFeeRecipient, _infrastructureShare);
        transferByRouterAmountPartner = _transferPairedToken(partnerFeeRecipient, _partnerShare);
        emit CreatorShareWithdrawn(address(this), 0, _infrastructureShare, _partnerShare);
    }

    /**
     * @notice  Withdraws an unrelated ERC20 or native token from the pool.
     * 
     * @dev     Throws when the caller is not the owner of the pool.
     * @dev     Throws when the token is the paired token.
     * @dev     Throws when the token fails to transfer.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. The unrelated token has been transferred to the withdraw to address.
     * 
     * @param tokenAddress    Address of the token to withdraw from the pool
     * @param withdrawTo      Adress to withdraw the token to.
     * @param withdrawAmount  Amount of the token to withdraw.
     */
    function withdrawUnrelatedToken(
        address tokenAddress, 
        address withdrawTo, 
        uint256 withdrawAmount
    ) external virtual onlyOwner {
        if (tokenAddress == PAIRED_TOKEN) {
            revert TokenMasterERC20__CannotWithdrawPairedToken();
        }
        if (tokenAddress == address(0)) {
            (bool success, ) = withdrawTo.call{value: withdrawAmount}("");
            if (!success) {
                revert TokenMasterERC20__NativeTransferFailed();
            }
        } else {
            bool isError = SafeERC20.safeTransfer(tokenAddress, withdrawTo, withdrawAmount);
            if (isError) {
                revert TokenMasterERC20__ERC20TransferFailed();
            }
        }
    }

    /*************************************************************************/
    /*                            ROUTER FUNCTIONS                           */
    /*************************************************************************/

    /**
     * @notice  Sets a max approval for the TokenMasterRouter to transfer the paired token from the pool.
     * 
     * @dev     Called by router when the current approval is insufficient for an attempted transfer.
     * @dev     This may be caused by a token that is initially set to handle transfers by the pool but has to
     * @dev     fall back to router transfers due to ERC20C whitelist transfer restrictions or when a paired token
     * @dev     does not treat `type(uint256).max` as an unlimited approval and the volume of transfers has diminished
     * @dev     the approved amount to the point where it needs to be updated.
     * 
     * @dev     Throws when the caller is not the TokenMasterRouter.
     * @dev     Throws when the approval call fails to reset the approval.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. The paired token approval for the router has been set to max approval.
     */
    function resetPairedTokenApproval() external {
        _callerIsRouter();
        bool isError = SafeERC20.safeApproveWithRetryAfterZero(PAIRED_TOKEN, ROUTER, type(uint256).max);
        if (isError) {
            revert TokenMasterERC20__FailedToSetApproval();
        }
    }

    /*************************************************************************/
    /*                             VIEW FUNCTIONS                            */
    /*************************************************************************/

    /**
     * @notice  Returns the current share splits for the paired token.
     * 
     * @return marketShare          Amount of paired token bonded to market value.
     * @return creatorShare         Amount of paired token allocated to the creator.
     * @return infrastructureShare  Amount of paired token allocated to infrastructure fees.
     * @return partnerShare         Amount of paired token allocated to partner fees.
     */
    function pairedTokenShares() external view returns(
        uint256 marketShare,
        uint256 creatorShare,
        uint256 infrastructureShare,
        uint256 partnerShare
    ) {
        marketShare = _bondedMarketValue();
        (creatorShare,infrastructureShare,partnerShare) = _creatorPairedTokenShare();
    }

    /*************************************************************************/
    /*                           INTERNAL FUNCTIONS                          */
    /*************************************************************************/

    /**
     * @dev  Returns the current bonded market value.
     * @dev  This function must be implemented by each pool type based on the conditions that determine
     * @dev  bonded value in the pool.
     */
    function _bondedMarketValue() internal virtual view returns(uint256);

    /**
     * @dev  Internal function assigned to `_creatorPairedTokenShare` function pointer when the paired
     * @dev  token is an ERC20 token.
     * 
     * @dev  Returns the token shares based on the balance of the ERC20 paired token minus
     * @dev  the bonded market value.
     * 
     * @return creatorShare         Amount of paired token allocated to the creator.
     * @return infrastructureShare  Amount of paired token allocated to infrastructure fees.
     * @return partnerShare         Amount of paired token allocated to partner fees.
     */
    function _creatorPairedTokenShareERC20() private view returns(
        uint256 creatorShare,
        uint256 infrastructureShare,
        uint256 partnerShare
    ) {
        (creatorShare, infrastructureShare, partnerShare) = _calculateShareSplit(
            IERC20(PAIRED_TOKEN).balanceOf(address(this)) - _bondedMarketValue()
        );
    }

    /**
     * @dev  Internal function assigned to `_creatorPairedTokenShare` function pointer when the paired
     * @dev  token is the native token.
     * 
     * @dev  Returns the token shares based on the pool's native token balance minus
     * @dev  the bonded market value.
     * 
     * @return creatorShare         Amount of paired token allocated to the creator.
     * @return infrastructureShare  Amount of paired token allocated to infrastructure fees.
     * @return partnerShare         Amount of paired token allocated to partner fees.
     */
    function _creatorPairedTokenShareNative() private view returns(
        uint256 creatorShare,
        uint256 infrastructureShare,
        uint256 partnerShare
    ) {
        (creatorShare, infrastructureShare, partnerShare) = _calculateShareSplit(
            address(this).balance - _bondedMarketValue()
        );
    }

    /**
     * @dev  Calculates the split of non-bonded value between the creator, infrastructure and partner.
     * @dev  Adjusts for the amount of creator share that has already paid infrastructure and partner
     * @dev  fees.
     * 
     * @param totalShare  Total amount of value to split between creator, infra, and partner.
     * 
     * @return creatorShare         Amount of paired token allocated to the creator.
     * @return infrastructureShare  Amount of paired token allocated to infrastructure fees.
     * @return partnerShare         Amount of paired token allocated to partner fees.
     */
    function _calculateShareSplit(uint256 totalShare) private view returns (
        uint256 creatorShare,
        uint256 infrastructureShare,
        uint256 partnerShare
    ) {
        unchecked {
            uint256 _creatorShareFeesPaidAdjustment = creatorShareFeesPaidAdjustment;
            totalShare -= creatorShareFeesPaidAdjustment;
            infrastructureShare = totalShare * INFRASTRUCTURE_FEE_BPS / BPS;
            totalShare -= infrastructureShare;
            partnerShare = totalShare * PARTNER_FEE_BPS / BPS;
            creatorShare = totalShare - partnerShare + _creatorShareFeesPaidAdjustment;
        }
    }

    /**
     * @dev  Internal function assigned to the `_transferPairedToken` function pointer when paired with
     * @dev  an ERC20 token and deployment parameters specify that tokens are to be transferred by the router.
     * 
     * @dev  This function simply returns the transfer amount parameter as the transfer by router amount 
     * @dev  to inform the router that it will be required to transfer the tokens.
     */
    function _transferPairedTokenERC20RouterTransfer(address, uint256 amount) private pure returns (uint256 transferByRouterAmount) {
        transferByRouterAmount = amount;
    }

    /**
     * @dev  Intern function assigned to the `_transferPairedToken` function pointer when paired with
     * @dev  an ERC20 token and deployment parameters specify that tokens are to be transferred by the pool.
     * 
     * @dev  This function will attempt to transfer tokens, if the transfer fails it will fall back to assigning 
     * @dev  the transfer amount to transfer by router amount to inform the router that it will be required to
     * @dev  transfer the tokens.
     * 
     * @param to      Address to transfer the paired tokens to.
     * @param amount  Amount of paired tokens to transfer.
     */
    function _transferPairedTokenERC20PoolTransfer(address to, uint256 amount) private returns (uint256 transferByRouterAmount) {
        if (amount == 0) return 0;

        bool isError = SafeERC20.safeTransfer(PAIRED_TOKEN, to, amount);
        if (isError) {
            // fallback to transfer by router
            transferByRouterAmount = amount;
        }
    }

    /**
     * @dev  Intern function assigned to the `_transferPairedToken` function pointer when paired with
     * @dev  the native token.
     * 
     * @dev  Throws when the native token transfer fails.
     * 
     * @param to      Address to transfer the paired tokens to.
     * @param amount  Amount of paired tokens to transfer.
     */
    function _transferPairedTokenNative(address to, uint256 amount) private returns (uint256 transferByRouterAmount) {
        if (amount == 0) return 0;

        (bool success, ) = to.call{value: amount}("");
        if (!success) {
            revert TokenMasterERC20__NativeTransferFailed();
        }
    }

    /**
     * @dev  Throws when the caller is not the TokenMasterRouter.
     */
    function _callerIsRouter() internal view {
        if (msg.sender != ROUTER) {
            revert TokenMasterERC20__CallerMustBeRouter();
        }
    }

    /*************************************************************************/
    /*                             TOKEN FUNCTIONS                           */
    /*************************************************************************/

    function decimals() public view override returns (uint8) {
        return DECIMALS;
    }

    /**
     * @notice Indicates whether the contract implements the specified interface.
     * @dev Overrides supportsInterface in ERC165.
     * @param interfaceId The interface id
     * @return true if the contract implements the specified interface, false otherwise
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return 
        interfaceId == type(ITokenMasterERC20C).interfaceId || 
        _supportsInterfaceExtended(interfaceId) ||
        super.supportsInterface(interfaceId);
    }

    /**
     * @dev  Implements ERC20C transfer validation logic for token mints.
     */
    function _validateMint(address /*caller*/, address to, uint256 tokenId, uint256 amount, uint256 value) internal override {
        _preValidateTransfer(ROUTER, address(0), to, tokenId, amount, value);
    }

    /**
     * @dev  Implements ERC20C transfer validation logic for token burns.
     */
    function _validateBurn(address /*caller*/, address from, uint256 tokenId, uint256 amount, uint256 value) internal override {
        _preValidateTransfer(ROUTER, from, address(0), tokenId, amount, value);
    }

    /**
     * @dev  Overrides the ownable renounce ownership function to avoid inadvertent ownership renouncing as TokenMaster
     * @dev  tokens should generally not be unowned in order to manage features like order signing in TokenMasterRouter.
     * @dev  Creators should weigh all implications of their token being unowned before implementing any sort of 
     * @dev  ownership receiver to transfer the ownership to that would be the equivalent of burning ownership.
     */
    function renounceOwnership() public pure override {
        revert TokenMasterERC20__RenounceNotAllowed();
    }

    /**
     * @dev  Internal override of `_requireCallerIsContractOwner` to check the caller is the owner for OwnablePermissions.
     */
    function _requireCallerIsContractOwner() internal view override {
        _checkOwner();
    }

    /**
     * @dev  Internal function that may be overriden to extend the ERC165 interface support check in
     * @dev  pool implementations that have additional interfaces that are supported.
     */
    function _supportsInterfaceExtended(bytes4 /*interfaceId*/) internal virtual view returns (bool) {
        return false;
    }
}

File 34 of 37 : DataTypes.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

/**
 * @dev This struct defines parameters used for promotional pool initialization.
 * 
 * @dev **initialSupplyRecipient**: Address to receive the initial supply amount.
 * @dev **initialSupplyAmount**: Initial amount of supply to mint to the initial supply recipient.
 * @dev **initialBuyParameters**: Initial settings for buy parameters.
 */
struct PromotionalPoolInitializationParameters {
    address initialSupplyRecipient;
    uint256 initialSupplyAmount;
    PromotionalPoolBuyParameters initialBuyParameters;
}

/**
 * @dev This struct defines parameters used for promotional pool buys.
 * 
 * @dev **buyCostPairedTokenNumerator**: The numerator for the ratio of paired token to pooled tokens when buying.
 * @dev **buyCostPoolTokenDenominator**: The denominator for the ratio of paired token to pooled tokens when buying.
 */
struct PromotionalPoolBuyParameters {
    uint96 buyCostPairedTokenNumerator;
    uint96 buyCostPoolTokenDenominator;
}

File 35 of 37 : IPromotionalPool.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "./DataTypes.sol";
import "../../interfaces/ITokenMasterERC20C.sol";

/**
 * @title  IPromotionalPool
 * @author Limit Break, Inc.
 * @notice Interface definition for a PromotionalPool contract.
 */
interface IPromotionalPool is ITokenMasterERC20C {
    event BuyParametersUpdated();

    function setBuyParameters(PromotionalPoolBuyParameters calldata _buyParameters) external;
    function getBuyParameters() external view returns(PromotionalPoolBuyParameters memory);
}

File 36 of 37 : PromotionalPool.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "./IPromotionalPool.sol";
import "./DataTypes.sol";
import "../../Constants.sol";
import "../../DataTypes.sol";
import "../../Errors.sol";

import "../BondedPool.sol";
import "../../interfaces/IMinterBurnerRolePool.sol";

import "@limitbreak/tm-core-lib/src/token/erc20/ERC20C.sol";
import "@limitbreak/tm-core-lib/src/token/erc20/utils/SafeERC20.sol";
import "@limitbreak/tm-core-lib/src/utils/access/Ownable2Step.sol";
import "@limitbreak/tm-core-lib/src/utils/access/OwnableAccessControl.sol";
import "@limitbreak/tm-core-lib/src/licenses/LicenseRef-PolyForm-Strict-1.0.0.sol";

/**
 * @title  PromotionalPool
 * @author Limit Break, Inc.
 * @notice The PromotionalPool contract is a TokenMaster pool token that is designed for
 *         valueless promotional tokens that a creator has full control over minting and 
 *         burning with no constraints related to bonded token value while still allowing
 *         full use of TokenMaster for deploying and executing spend orders.
 * 
 * @dev    <h4>Features</h4>
 *         - ERC20C token with full creator controls.
 *         - Deployed through TokenMasterRouter.
 *         - Zero bonded market value for promotional tokens.
 *         - Creator specified buy rate with a paired value token if creator wishes to
 *             allow purchases of the promotional token by participants.
 *         - Creator can grant minting and burning roles to external addresses to mint
 *             and burn tokens to and from any account.
 */
contract PromotionalPool is BondedPool, IPromotionalPool, IMinterBurnerRolePool {

    /// @dev Role constant for assigning an address to the minter role.
    bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE");
    /// @dev Role constant for assigning an address to the burner role.
    bytes32 private constant BURNER_ROLE = keccak256("BURNER_ROLE");

    /// @dev Parameters that are applied to buys.
    PromotionalPoolBuyParameters private buyParameters;

    constructor(
        PoolDeploymentParameters memory deploymentParams,
        uint256 pairedValueIn,
        uint256 infrastructureFeeBPS,
        address router
    ) BondedPool(deploymentParams, infrastructureFeeBPS, router) {
        if (pairedValueIn > 0) {
            revert TokenMasterERC20__InvalidPairedValues();
        }

        PromotionalPoolInitializationParameters memory initializationParameters = 
            abi.decode(deploymentParams.encodedInitializationArgs, (PromotionalPoolInitializationParameters));

        _setBuyParameters(initializationParameters.initialBuyParameters);
        _mint(initializationParameters.initialSupplyRecipient, initializationParameters.initialSupplyAmount);
    }

    /*************************************************************************/
    /*                    DISTRIBUTION (MINT/BURN) FUNCTIONS                 */
    /*************************************************************************/

    /**
     * @notice  Mints an amount of tokens to an address.
     * 
     * @dev     Throws when the caller does not have the minter role.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Tokens have been minted to the address.
     * 
     * @param to      Address to mint tokens to.
     * @param amount  Amount of tokens to mint.
     */
    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    /**
     * @notice  Mints tokens to specified addresses in specified amounts.
     * 
     * @dev     Throws when the caller does not have the minter role.
     * @dev     Throws when the array lengths of the to addresses and amounts do not match.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Tokens have been minted to each address specified.
     * 
     * @param toAddresses  Addresses to mint tokens to.
     * @param amounts      Amounts of tokens to mint.
     */
    function mintBatch(address[] calldata toAddresses, uint256[] calldata amounts) external onlyRole(MINTER_ROLE) {
        if (toAddresses.length != amounts.length) {
            revert TokenMasterERC20__ArrayLengthMismatch();
        }

        for (uint256 i = 0; i < toAddresses.length; ++i) {
            _mint(toAddresses[i], amounts[i]);
        }
    }

    /**
     * @notice  Burns an amount of tokens from an address.
     * 
     * @dev     Throws when the caller does not have the burner role.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Tokens have been burned from the address.
     * 
     * @param from    Address to burn tokens from.
     * @param amount  Amount of tokens to burn.
     */
    function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) {
        _burn(from, amount);
    }

    /*************************************************************************/
    /*                              POOL FUNCTIONS                           */
    /*************************************************************************/

    /**
     * @notice  Executes a buy of tokens.
     * 
     * @dev     Throws when the caller is not the TokenMasterRouter.
     * @dev     Throws when the amount of value in is insufficient for the tokens being bought.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Purchased tokens have been minted to the buyer.
     * 
     * @param buyer          Address of the buyer. 
     * @param pairedTokenIn  Amount of paired token transferred in for the buy.
     * @param tokensToBuy    Amount of tokens being bought.
     * 
     * @return totalCost             Total amount of buy cost including fees. 
     * @return refundByRouterAmount  Amount of paired token to be refunded by the router.
     */
    function buyTokens(
        address buyer,
        uint256 pairedTokenIn,
        uint256 tokensToBuy
    ) external payable returns (uint256 totalCost, uint256 refundByRouterAmount) {
        _callerIsRouter();
        
        (uint96 buyCostPairedTokenNumerator, uint96 buyCostPoolTokenDenominator) = _loadBuyParameters();
        totalCost = tokensToBuy * buyCostPairedTokenNumerator / buyCostPoolTokenDenominator;

        unchecked {
            uint256 refundAmount = pairedTokenIn - totalCost;
            if (refundAmount > pairedTokenIn) {
                revert TokenMasterERC20__InsufficientBuyInput();
            }

            _mint(buyer, tokensToBuy);
            refundByRouterAmount = _transferPairedToken(buyer, refundAmount);
        }
    }

    /**
     * @notice  Selling of tokens is unsupported in PromotionalPool as there is no bonded market value.
     */
    function sellTokens(
        address /*seller*/,
        uint256 /*tokensToSell*/,
        uint256 /*pairedTokenMinimumOut*/
    ) external pure returns (address /*pairedToken*/, uint256 /*pairedValueToSeller*/, uint256 /*transferByRouterAmount*/) {
        revert TokenMasterERC20__OperationNotSupportedByPool();
    }

    /**
     * @notice Executes a spend of tokens.
     * 
     * @dev     Throws when the caller is not the TokenMasterRouter.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Spent tokens have been burned from the spender.
     * 
     * @param spender        Address of the spender.
     * @param tokensToSpend  Amount of tokens being spent.
     */
    function spendTokens(address spender, uint256 tokensToSpend) external {
        _callerIsRouter();
        _burn(spender, tokensToSpend);
    }

    /*************************************************************************/
    /*                           CREATOR FUNCTIONS                           */
    /*************************************************************************/

    /**
     * @notice  Sets the parameters for buy orders.
     * 
     * @dev     Throws when the caller is not the owner.
     * @dev     Throws when the settings are invalid.
     * 
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Buy parameters have been stored.
     * @dev    2. A `BuyParametersUpdated` event has been emitted.
     * 
     * @param _buyParameters  The parameters to set for buy orders.
     */
    function setBuyParameters(PromotionalPoolBuyParameters calldata _buyParameters) external onlyOwner {
        _setBuyParameters(_buyParameters);

        emit BuyParametersUpdated();
    }

    /*************************************************************************/
    /*                             VIEW FUNCTIONS                            */
    /*************************************************************************/

    /**
     * @notice  Returns the current settings for buy orders.
     * 
     * @return _buyParameters  The current buy parameters.
     */
    function getBuyParameters() external view returns(PromotionalPoolBuyParameters memory _buyParameters) {
        _buyParameters = buyParameters;
    }

    /*************************************************************************/
    /*                           INTERNAL FUNCTIONS                          */
    /*************************************************************************/

    /**
     * @dev  Returns zero as there is no bonded market value for promotional pools.
     */
    function _bondedMarketValue() internal virtual view override returns(uint256) {
        // Bonded market value in promotional pools is always zero
        return 0;
    }

    /**
     * @dev  Validates the buy parameters being set.
     * 
     * @dev  Throws when a setting is invalid.
     * 
     * @param _buyParameters  The parameters to set for buy orders.
     */
    function _setBuyParameters(PromotionalPoolBuyParameters memory _buyParameters) internal {
        if (_buyParameters.buyCostPoolTokenDenominator == 0) {
            revert TokenMasterERC20__InvalidParameters();
        }

        buyParameters = _buyParameters;
    }

    /**
     * @dev  Loads buy parameters from storage onto stack for executing a buy.
     * 
     * @return buyCostPairedTokenNumerator  The numerator for the ratio of paired token to pool token as an additional buy fee.
     * @return buyCostPoolTokenDenominator  The denominator for the ratio of paired token to pool token as an additional buy fee.
     */
    function _loadBuyParameters() internal view returns(
        uint96 buyCostPairedTokenNumerator,
        uint96 buyCostPoolTokenDenominator
    ) {
        buyCostPairedTokenNumerator = buyParameters.buyCostPairedTokenNumerator;
        buyCostPoolTokenDenominator = buyParameters.buyCostPoolTokenDenominator;
    }

    /**
     * @dev  Extends the ERC165 interface support check with additional interfaces supported by PromotionalPool.
     * 
     * @param interfaceId The interface id
     */
    function _supportsInterfaceExtended(bytes4 interfaceId) internal virtual view override returns (bool) {
        return 
        interfaceId == type(IPromotionalPool).interfaceId ||
        interfaceId == type(IMinterBurnerRolePool).interfaceId;
    }
}

File 37 of 37 : PromotionalPoolCreationCode.sol
//SPDX-License-Identifier: LicenseRef-PolyForm-Strict-1.0.0
pragma solidity 0.8.24;

import "./PromotionalPool.sol";
import "@limitbreak/tm-core-lib/src/licenses/LicenseRef-PolyForm-Strict-1.0.0.sol";

/**
 * @title  PromotionalPoolCreationCode
 * @author Limit Break, Inc.
 * @notice Stores the init code for a TokenMaster Promotional Pool to use in factory deployments.
 */
contract PromotionalPoolCreationCode {

    constructor() {
        bytes memory creationCode = type(PromotionalPool).creationCode;
        assembly ("memory-safe") {
            return(add(0x20, creationCode), mload(creationCode))
        }
    }
}

Settings
{
  "remappings": [
    "@limitbreak/tm-core-lib/=lib/tm-core-lib/",
    "@limitbreak/permit-c/=lib/PermitC/src/",
    "@limitbreak/trusted-forwarder/=lib/TrustedForwarder/src/",
    "@limitbreak/tm-role-server/=lib/tm-role-server/src/",
    "@limitbreak/creator-token-transfer-validator/=lib/creator-token-transfer-validator/src/",
    "@openzeppelin/=lib/TrustedForwarder/lib/openzeppelin-contracts/",
    "@rari-capital/solmate/=lib/PermitC/lib/solmate/",
    "PermitC/=lib/PermitC/",
    "TrustedForwarder/=lib/TrustedForwarder/",
    "creator-token-transfer-validator/=lib/creator-token-transfer-validator/src/",
    "ds-test/=lib/tm-role-server/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/TrustedForwarder/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-metering/=lib/PermitC/lib/forge-gas-metering/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/TrustedForwarder/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/TrustedForwarder/lib/openzeppelin-contracts/contracts/",
    "solady/=lib/PermitC/lib/forge-gas-metering/lib/solady/",
    "solmate/=lib/PermitC/lib/solmate/src/",
    "tm-core-lib/=lib/tm-core-lib/src/",
    "tm-role-server/=lib/tm-role-server/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 9999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"tokenMasterRouter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"TokenMasterFactory__CallerMustBeRouter","type":"error"},{"inputs":[],"name":"TokenMasterFactory__RouterAddressNotSet","type":"error"},{"inputs":[],"name":"CREATION_CODE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenSalt","type":"bytes32"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"address","name":"pairedToken","type":"address"},{"internalType":"uint256","name":"initialPairedTokenToDeposit","type":"uint256"},{"internalType":"bytes","name":"encodedInitializationArgs","type":"bytes"},{"internalType":"address","name":"defaultTransferValidator","type":"address"},{"internalType":"bool","name":"useRouterForPairedTransfers","type":"bool"},{"internalType":"address","name":"partnerFeeRecipient","type":"address"},{"internalType":"uint256","name":"partnerFeeBPS","type":"uint256"}],"internalType":"struct PoolDeploymentParameters","name":"poolParams","type":"tuple"},{"internalType":"uint256","name":"pairedValueIn","type":"uint256"},{"internalType":"uint256","name":"infrastructureFeeBPS","type":"uint256"}],"name":"computeDeploymentAddress","outputs":[{"internalType":"address","name":"deploymentAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenSalt","type":"bytes32"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"address","name":"pairedToken","type":"address"},{"internalType":"uint256","name":"initialPairedTokenToDeposit","type":"uint256"},{"internalType":"bytes","name":"encodedInitializationArgs","type":"bytes"},{"internalType":"address","name":"defaultTransferValidator","type":"address"},{"internalType":"bool","name":"useRouterForPairedTransfers","type":"bool"},{"internalType":"address","name":"partnerFeeRecipient","type":"address"},{"internalType":"uint256","name":"partnerFeeBPS","type":"uint256"}],"internalType":"struct PoolDeploymentParameters","name":"poolParams","type":"tuple"},{"internalType":"uint256","name":"pairedValueIn","type":"uint256"},{"internalType":"uint256","name":"infrastructureFeeBPS","type":"uint256"}],"name":"deployToken","outputs":[{"internalType":"address","name":"deployedAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"}]

60c060405234801561000f575f80fd5b506040516148f43803806148f483398101604081905261002e916100bc565b6001600160a01b038116158061004c57506001600160a01b0381163b155b1561006a57604051636834030560e01b815260040160405180910390fd5b6001600160a01b038116608052604051610083906100af565b604051809103905ff08015801561009c573d5f803e3d5ffd5b506001600160a01b031660a052506100e9565b6140fb806107f983390190565b5f602082840312156100cc575f80fd5b81516001600160a01b03811681146100e2575f80fd5b9392505050565b60805160a0516106d56101245f395f818160970152818161012c015261021701525f818160d201528181610181015261026c01526106d55ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806303c268e0146100435780638d33f2bf1461007f578063bcc11f2514610092575b5f80fd5b61005661005136600461035b565b6100b9565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61005661008d36600461035b565b610213565b6100567f000000000000000000000000000000000000000000000000000000000000000081565b5f3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610129576040517f66bc0c3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16803b806020016040519081016040528181525f908060200190933c8585857f00000000000000000000000000000000000000000000000000000000000000006040516020016101b394939291906104a7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526101ef9291602001610683565b6040516020818303038152906040529050858151602083015ff59695505050505050565b5f807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16803b806020016040519081016040528181525f908060200190933c8585857f000000000000000000000000000000000000000000000000000000000000000060405160200161029e94939291906104a7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526102da9291602001610683565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828252805160209091012060608301909152915073ffffffffffffffffffffffffffffffffffffffff9060ff81533060601b60018201528760158201528260358201526055812082169350505050949350505050565b5f805f806080858703121561036e575f80fd5b84359350602085013567ffffffffffffffff81111561038b575f80fd5b8501610160818803121561039d575f80fd5b93969395505050506040820135916060013590565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126103e5575f80fd5b830160208101925035905067ffffffffffffffff811115610404575f80fd5b803603821315610412575f80fd5b9250929050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b803560ff81168114610470575f80fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610470575f80fd5b80358015158114610470575f80fd5b608081525f6104b686876103b2565b6101608060808601526104ce6101e086018385610419565b92506104dd60208a018a6103b2565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80808786030160a0880152610515858584610419565b945061052360408c01610460565b60ff811660c0890152935061053a60608c01610475565b73ffffffffffffffffffffffffffffffffffffffff811660e0890152935061056460808c01610475565b9350610100915061058c8288018573ffffffffffffffffffffffffffffffffffffffff169052565b610120935060a08b0135848801526105a760c08c018c6103b2565b610140838a890301818b01526105be888385610419565b97506105cc60e08f01610475565b73ffffffffffffffffffffffffffffffffffffffff81168b88015293506105f4858f01610498565b8015156101808c0152955061060a878f01610475565b73ffffffffffffffffffffffffffffffffffffffff9081166101a08c01529d01356101c08a01525050505060208501979097525060408301949094525093166060909301929092525090565b5f81515f5b81811015610675576020818501810151868301520161065b565b505f93019283525090919050565b5f6106976106918386610656565b84610656565b94935050505056fea26469706673582212203a49e81ca72b627730638f87be20959c387a2cf3e04ca478cf3f48b597f9905e64736f6c63430008180033608060405234801561000f575f80fd5b505f604051806020016100219061003f565b6020820181038252601f19601f820116604052509050805181602001f35b6140ae8061004d8339019056fe61018060405234801562000011575f80fd5b50604051620040ae380380620040ae833981016040819052620000349162000a9c565b838282825f0151836020015181818660e00151828289606001515f6001600160a01b0316816001600160a01b0316036200008857604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6200009381620002ff565b507f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a7620000c1838262000c90565b507f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a8620000ef828262000c90565b5050506001600160a01b038116608052620001096200033c565b60805162000117906200038d565b505050505061271061ffff168211806200015e57505f8361014001511180156200015e57506101208301516001600160a01b031615806200015e5750610140830151612710105b156200017d57604051635f45b1dd60e01b815260040160405180910390fd5b604083015160ff1660a0526080830180516001600160a01b0390811660c05282811660e05261ffff808516610100526101408601511661012052905116620002185782610100015115620001e457604051635f45b1dd60e01b815260040160405180910390fd5b6200040b602090811b62001d03176001600160401b03908116610140526200045c90911b62001d8f1716610160526200028b565b6001600160401b0362001e3b620004e860201b1716610140526101008301511562000271576001600160401b0362001ef86200056960201b17166101605260c05160e0516200026a91905f196200056e565b506200028b565b6001600160401b0362001efd620005ec60201b1716610160525b505083159050620002af5760405163957985f360e01b815260040160405180910390fd5b5f8460c00151806020019051810190620002ca919062000d6f565b9050620002e181604001516200062460201b60201c565b80516020820151620002f4919062000692565b505050505062000e7e565b7fdcb758a6418d1b58c02fbe3eaed41774ca05a21b93617786a39e380345abe85f80546001600160a01b0319169055620003398162000776565b50565b7fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac5f6200036860805190565b604080516001600160a01b0393841681529290911660208301520160405180910390a1565b6001600160a01b038116156200033957803b801562000407576040805163fb2de5d760e01b81523060048201526014602482015290516001600160a01b0384169163fb2de5d7916044808301925f92919082900301818387803b158015620003f3575f80fd5b505af192505050801562000405575060015b505b5050565b5f8080620004506200041e824762000e3a565b5f54610100516101205161271061ffff9283169484900394850281900494859003929091168202049081900390910192565b91959094509092509050565b5f815f036200046d57505f620004e2565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114620004b8576040519150601f19603f3d011682016040523d82523d5f602084013e620004bd565b606091505b5050905080620004e057604051632f755b2f60e21b815260040160405180910390fd5b505b92915050565b5f8080620004508160c0516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801562000537573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200055d919062000e50565b6200041e919062000e3a565b919050565b5f620005d7565b5f6040516060810160405263095ea7b381528381602001528481604001525f806044601c84015f875af115620005ca5760203d1015620005ba575050803b15620005d0565b60205f803e50505f5115620005d0565b50600190505b9392505050565b620005e482848662000575565b949350505050565b5f815f03620005fd57505f620004e2565b5f6200061360c0518585620007e660201b60201c565b90508015620004e057509092915050565b80602001516001600160601b03165f036200065257604051635f45b1dd60e01b815260040160405180910390fd5b8051600180546020909301516001600160601b039081166c01000000000000000000000000026001600160c01b0319909416921691909117919091179055565b6001600160a01b038216620006bd5760405163ec442f0560e01b81525f60048201526024016200007f565b620006cc33835f84346200083f565b807f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a680545f90620006ff90849062000e68565b90915550506001600160a01b0382165f8181527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a960209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b7f904c57a1db75b8be294e842878a18b28975f03a20160582f7aee6f3a5261820a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f62000832565b5f6040516060810160405263a9059cbb81528381602001528481604001525f806044601c84015f875af115620005ca5760203d1015620005ba575050803b15620005d0565b620005e4828486620007ed565b60e05162000852905f8686868662000859565b5050505050565b5f620008646200090f565b90506001600160a01b0381161562000905576001600160a01b03811633036200088e575062000907565b604051631854b24160e01b81526001600160a01b038881166004830152878116602483015286811660448301526064820186905260848201859052821690631854b2419060a4015f604051808303815f87803b158015620008ed575f80fd5b505af115801562000900573d5f803e3d5ffd5b505050505b505b505050505050565b7f0245c7c302b31cdd9b77ef0ec6005b18f1d75c8b2d92ba7e892ce1855da2c6155461010090046001600160a01b03168062000974577f0245c7c302b31cdd9b77ef0ec6005b18f1d75c8b2d92ba7e892ce1855da2c6155460ff166200097457506080515b90565b634e487b7160e01b5f52604160045260245ffd5b60405161016081016001600160401b0381118282101715620009b157620009b162000977565b60405290565b5f82601f830112620009c7575f80fd5b81516001600160401b0380821115620009e457620009e462000977565b604051601f8301601f19908116603f0116810190828211818310171562000a0f5762000a0f62000977565b816040528381526020925086602085880101111562000a2c575f80fd5b5f91505b8382101562000a4f578582018301518183018401529082019062000a30565b5f602085830101528094505050505092915050565b805160ff8116811462000569575f80fd5b80516001600160a01b038116811462000569575f80fd5b8051801515811462000569575f80fd5b5f805f806080858703121562000ab0575f80fd5b84516001600160401b038082111562000ac7575f80fd5b90860190610160828903121562000adc575f80fd5b62000ae66200098b565b82518281111562000af5575f80fd5b62000b038a828601620009b7565b82525060208301518281111562000b18575f80fd5b62000b268a828601620009b7565b60208301525062000b3a6040840162000a64565b604082015262000b4d6060840162000a75565b606082015262000b606080840162000a75565b608082015260a083015160a082015260c08301518281111562000b81575f80fd5b62000b8f8a828601620009b7565b60c08301525062000ba360e0840162000a75565b60e0820152610100915062000bba82840162000a8c565b82820152610120915062000bd082840162000a75565b8282015261014091508183015182820152809650505050602085015192506040850151915062000c036060860162000a75565b905092959194509250565b600181811c9082168062000c2357607f821691505b60208210810362000c4257634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200040557805f5260205f20601f840160051c8101602085101562000c6f5750805b601f840160051c820191505b8181101562000852575f815560010162000c7b565b81516001600160401b0381111562000cac5762000cac62000977565b62000cc48162000cbd845462000c0e565b8462000c48565b602080601f83116001811462000cfa575f841562000ce25750858301515b5f19600386901b1c1916600185901b17855562000907565b5f85815260208120601f198616915b8281101562000d2a5788860151825594840194600190910190840162000d09565b508582101562000d4857878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b80516001600160601b038116811462000569575f80fd5b5f818303608081121562000d81575f80fd5b60408051606081016001600160401b03808211838310171562000da85762000da862000977565b81845262000db68762000a75565b83526020870151602084015283603f198601121562000dd3575f80fd5b835194508385019150848210818311171562000df35762000df362000977565b50825262000e0385830162000d58565b835262000e136060860162000d58565b6020840152908101919091529392505050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115620004e257620004e262000e26565b5f6020828403121562000e61575f80fd5b5051919050565b80820180821115620004e257620004e262000e26565b60805160a05160c05160e0516101005161012051610140516101605161315d62000f515f395f81816118110152818161184101528181611871015281816119c601528181611ba50152611bd001525f818161176a01528181611b020152611b4401525f611d1a01525f611d4101525f8181611075015281816124a301528181612b3b0152612b7001525f81816107f5015281816110540152818161127d015281816117e901528181611b7101528181611e700152611f1201525f61042b01525f818161066e0152610bdf015261315d5ff3fe6080604052600436106102ab575f3560e01c80638582446711610165578063c1345fcc116100c6578063e30c39781161007c578063f0a18cce11610062578063f0a18cce14610901578063f255527814610935578063f2fde38b14610954575f80fd5b8063e30c3978146108d3578063e4e57b9e146108e7575f80fd5b8063d2395dcd116100ac578063d2395dcd1461086d578063d547741f14610895578063dd62ed3e146108b4575f80fd5b8063c1345fcc146107e4578063c747300314610817575f80fd5b80639d57fc511161011b5780639e05d240116101015780639e05d24014610787578063a9059cbb146107a6578063a9fc664e146107c5575f80fd5b80639d57fc51146107495780639dc29fac14610768575f80fd5b806391d148541161014b57806391d14854146106a657806395d89b411461071657806398f059271461072a575f80fd5b806385824467146106605780638da5cb5b14610692575f80fd5b80633b83daf71161020f57806370a08231116101c557806379ba5097116101ab57806379ba50971461061957806379e32c7b1461062d5780637c88e3d914610641575f80fd5b806370a08231146105a5578063715018a614610605575f80fd5b8063570b586b116101f5578063570b586b146104e45780635dec8ae9146105505780636221d13c1461056f575f80fd5b80633b83daf71461047457806340c10f19146104c5575f80fd5b806318160ddd116102645780632f2ff15d1161024a5780632f2ff15d146103f7578063313ce5671461041857806336568abe14610455575f80fd5b806318160ddd1461039b57806323b872dd146103d8575f80fd5b8063095ea7b311610294578063095ea7b314610304578063098144d4146103235780630d705df61461035c575f80fd5b806301ffc9a7146102af57806306fdde03146102e3575b5f80fd5b3480156102ba575f80fd5b506102ce6102c9366004612c2e565b610973565b60405190151581526020015b60405180910390f35b3480156102ee575f80fd5b506102f76109dd565b6040516102da9190612c6d565b34801561030f575f80fd5b506102ce61031e366004612cfa565b610a8f565b34801561032e575f80fd5b50610337610b71565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102da565b348015610367575f80fd5b50604080517f1854b2410000000000000000000000000000000000000000000000000000000081525f6020820152016102da565b3480156103a6575f80fd5b507f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a6545b6040519081526020016102da565b3480156103e3575f80fd5b506102ce6103f2366004612d22565b610c02565b348015610402575f80fd5b50610416610411366004612d5b565b610eaa565b005b348015610423575f80fd5b5060405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016102da565b348015610460575f80fd5b5061041661046f366004612d5b565b610ec1565b34801561047f575f80fd5b5061049361048e366004612d85565b610f1a565b6040805173ffffffffffffffffffffffffffffffffffffffff90941684526020840192909252908201526060016102da565b3480156104d0575f80fd5b506104166104df366004612cfa565b610f4f565b3480156104ef575f80fd5b506040805180820182525f808252602091820152815180830183526001546bffffffffffffffffffffffff8082168084526c0100000000000000000000000090920481169284019283528451918252915190911691810191909152016102da565b34801561055b575f80fd5b5061041661056a366004612cfa565b610f83565b34801561057a575f80fd5b507f3d3215932a537a36aee89357cefc1143ff7a3e73b8133e12aa974fe0ec972ed35460ff166102ce565b3480156105b0575f80fd5b506103ca6105bf366004612dbe565b73ffffffffffffffffffffffffffffffffffffffff165f9081527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a9602052604090205490565b348015610610575f80fd5b50610416610f99565b348015610624575f80fd5b50610416610fcb565b348015610638575f80fd5b50610416611046565b34801561064c575f80fd5b5061041661065b366004612e1f565b6110f4565b34801561066b575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610337565b34801561069d575f80fd5b506103376111b9565b3480156106b1575f80fd5b506102ce6106c0366004612d5b565b5f9182527f605b91b3ea139b87df3e6c49663a6d47de4209dcae24ef36e54b0f4298aa8ba26020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610721575f80fd5b506102f76111f8565b348015610735575f80fd5b50610416610744366004612e86565b611229565b348015610754575f80fd5b50610416610763366004612d22565b611273565b348015610773575f80fd5b50610416610782366004612cfa565b6113fd565b348015610792575f80fd5b506104166107a1366004612e9c565b611431565b3480156107b1575f80fd5b506102ce6107c0366004612cfa565b6114bd565b3480156107d0575f80fd5b506104166107df366004612dbe565b611628565b3480156107ef575f80fd5b506103377f000000000000000000000000000000000000000000000000000000000000000081565b348015610822575f80fd5b50610836610831366004612ebb565b611756565b6040805173ffffffffffffffffffffffffffffffffffffffff909516855260208501939093529183015260608201526080016102da565b61088061087b366004612f05565b611907565b604080519283526020830191909152016102da565b3480156108a0575f80fd5b506104166108af366004612d5b565b6119fa565b3480156108bf575f80fd5b506103ca6108ce366004612f35565b611a02565b3480156108de575f80fd5b50610337611acd565b3480156108f2575f80fd5b5061049361048e366004612f05565b34801561090c575f80fd5b50610915611af4565b6040805194855260208501939093529183015260608201526080016102da565b348015610940575f80fd5b5061049361094f366004612f35565b611b31565b34801561095f575f80fd5b5061041661096e366004612dbe565b611c4b565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167fb080171e0000000000000000000000000000000000000000000000000000000014806109c857506109c882611f48565b806109d757506109d782611fdf565b92915050565b60607f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a66001018054610a0e90612f5d565b80601f0160208091040260200160405190810160405280929190818152602001828054610a3a90612f5d565b8015610a855780601f10610a5c57610100808354040283529160200191610a85565b820191905f5260205f20905b815481529060010190602001808311610a6857829003601f168201915b5050505050905090565b5f73ffffffffffffffffffffffffffffffffffffffff8316610ae4576040517f94280d620000000000000000000000000000000000000000000000000000000081525f60048201526024015b60405180910390fd5b335f8181526020858152604080832083527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24aa825291829020859055905184815273ffffffffffffffffffffffffffffffffffffffff861692917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350600192915050565b7f0245c7c302b31cdd9b77ef0ec6005b18f1d75c8b2d92ba7e892ce1855da2c61554610100900473ffffffffffffffffffffffffffffffffffffffff1680610bff577f0245c7c302b31cdd9b77ef0ec6005b18f1d75c8b2d92ba7e892ce1855da2c6155460ff16610bff57507f00000000000000000000000000000000000000000000000000000000000000005b90565b5f73ffffffffffffffffffffffffffffffffffffffff8416610c52576040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081525f6004820152602401610adb565b73ffffffffffffffffffffffffffffffffffffffff8316610ca1576040517fec442f050000000000000000000000000000000000000000000000000000000081525f6004820152602401610adb565b5f610cac8533611a02565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610d5c5782811015610d1f576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081523360048201526024810182905260448101849052606401610adb565b5f858152336020908152604080832083527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24aa909152902083820390555b610d6a3386865f875f612159565b73ffffffffffffffffffffffffffffffffffffffff85165f9081527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a9602052604090205483811015610e0e576040517fe450d38c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024810182905260448101859052606401610adb565b73ffffffffffffffffffffffffffffffffffffffff8681165f8181527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a960209081526040808320898703905593891680835291849020805489019055925187815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36001925050505b9392505050565b610eb2612167565b610ebc8282612171565b505050565b73ffffffffffffffffffffffffffffffffffffffff81163314610f10576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ebc8282612275565b5f805f6040517f59e9b70500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610f7981612370565b610ebc838361237a565b610f8b61248b565b610f9582826124fa565b5050565b6040517fe8b2c19900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3380610fd5611acd565b73ffffffffffffffffffffffffffffffffffffffff161461103a576040517f118cdaa700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610adb565b611043816126a7565b50565b61104e61248b565b5f6110ba7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6126f7565b90508015611043576040517fec57151a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a661111e81612370565b838214611157576040517fe10d9a0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b848110156111b1576111a986868381811061117657611176612fa8565b905060200201602081019061118b9190612dbe565b85858481811061119d5761119d612fa8565b9050602002013561237a565b600101611159565b505050505050565b5f7f904c57a1db75b8be294e842878a18b28975f03a20160582f7aee6f3a5261820a5b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a66002018054610a0e90612f5d565b611231612790565b61124861124336839003830183612ff0565b6127e8565b6040517f8b587e6dfb669691e88c128eac97bdeb65393078c31a9d5296a2b05f4fb248c0905f90a150565b61127b612790565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611300576040517f31025ce400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166113b7575f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f8114611371576040519150601f19603f3d011682016040523d82523d5f602084013e611376565b606091505b50509050806113b1576040517fbdd56cbc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b5f6113c3848484612890565b905080156113b1576040517f65571b1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84861142781612370565b610ebc83836124fa565b611439612167565b7f3d3215932a537a36aee89357cefc1143ff7a3e73b8133e12aa974fe0ec972ed380548215157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116811790915560408051918252517f6787c7f9a80aa0f5ceddab2c54f1f5169c0b88e75dd5e19d5e858a64144c7dbc9181900360200190a150565b5f73ffffffffffffffffffffffffffffffffffffffff831661150d576040517fec442f050000000000000000000000000000000000000000000000000000000081525f6004820152602401610adb565b61151b3333855f865f612159565b335f9081527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a9602052604090205482811015611593576040517fe450d38c0000000000000000000000000000000000000000000000000000000081523360048201526024810182905260448101849052606401610adb565b335f8181527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a960209081526040808320878603905573ffffffffffffffffffffffffffffffffffffffff881680845292819020805488019055518681529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35060019392505050565b611630612167565b73ffffffffffffffffffffffffffffffffffffffff8116803b15159015801590611658575080155b1561168f576040517f32483afb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac6116b8610b71565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291851660208301520160405180910390a17f0245c7c302b31cdd9b77ef0ec6005b18f1d75c8b2d92ba7e892ce1855da2c61580547fffffffffffffffffffffff0000000000000000000000000000000000000000001661010073ffffffffffffffffffffffffffffffffffffffff851602176001179055610f95826128f2565b5f805f8061176261248b565b5f805f6117917f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b925092509250828a11156117d1576040517ffb0290f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828a10156117e3578983035f556117e7565b5f80555b7f000000000000000000000000000000000000000000000000000000000000000096506118388b8b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b955061186889837f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b945061189888827f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b6040805173ffffffffffffffffffffffffffffffffffffffff8e168152602081018d9052908101849052606081018390529094507f9f1b9b8ffe273590f8b41198a55c84aca0d8c47e580d8832b1086e9b9e28511d9060800160405180910390a1505050945094509450949050565b5f8061191161248b565b5f8061193f6001546bffffffffffffffffffffffff808216926c010000000000000000000000009092041690565b91509150806bffffffffffffffffffffffff16826bffffffffffffffffffffffff168661196c919061309b565b61197691906130b2565b9350838603868111156119b5576040517f0be124cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119bf888761237a565b6119ed88827f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b9350505050935093915050565b610f10612167565b5f8281526020828152604080832083527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24aa909152812054908190036109d7577f3d3215932a537a36aee89357cefc1143ff7a3e73b8133e12aa974fe0ec972ed35460ff16156109d757611a73610b71565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109d757507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92915050565b5f7fdcb758a6418d1b58c02fbe3eaed41774ca05a21b93617786a39e380345abe85f6111dc565b5f808080611b2463ffffffff7f000000000000000000000000000000000000000000000000000000000000000016565b9596919590945092509050565b5f805f611b3c61248b565b5f805f611b6b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f8390557f0000000000000000000000000000000000000000000000000000000000000000985091945092509050611bc7888363ffffffff7f000000000000000000000000000000000000000000000000000000000000000016565b9450611bf787827f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b604080513081525f6020820152908101849052606081018390529094507f9f1b9b8ffe273590f8b41198a55c84aca0d8c47e580d8832b1086e9b9e28511d9060800160405180910390a15050509250925092565b611c53612790565b807fdcb758a6418d1b58c02fbe3eaed41774ca05a21b93617786a39e380345abe85f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9283161790558116611cbe6111b9565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f8080611d83611d1382476130ea565b5f546127107f000000000000000000000000000000000000000000000000000000000000000061ffff9081167f0000000000000000000000000000000000000000000000000000000000000000919091169383900393840282900493849003908102919091049081900390910192565b91959094509092509050565b5f815f03611d9e57505f6109d7565b5f8373ffffffffffffffffffffffffffffffffffffffff16836040515f6040518083038185875af1925050503d805f8114611df4576040519150601f19603f3d011682016040523d82523d5f602084013e611df9565b606091505b5050905080611e34576040517fbdd56cbc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b5f8080611d83816040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611eca573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eee91906130fd565b611d1391906130ea565b919050565b5f815f03611f0c57505f6109d7565b5f611f387f00000000000000000000000000000000000000000000000000000000000000008585612890565b90508015611e3457509092915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167fcffb014c0000000000000000000000000000000000000000000000000000000014806109d757507fffffffff0000000000000000000000000000000000000000000000000000000082167fa18b736c000000000000000000000000000000000000000000000000000000001492915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f36372b0700000000000000000000000000000000000000000000000000000000148061207157507fffffffff0000000000000000000000000000000000000000000000000000000082167fa219a02500000000000000000000000000000000000000000000000000000000145b806120bd57507fffffffff0000000000000000000000000000000000000000000000000000000082167fad0d7f6c00000000000000000000000000000000000000000000000000000000145b8061210957507fffffffff0000000000000000000000000000000000000000000000000000000082167fa07d229a00000000000000000000000000000000000000000000000000000000145b806109d757507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146109d7565b6111b18686868686866129a2565b61216f612790565b565b5f8281527f605b91b3ea139b87df3e6c49663a6d47de4209dcae24ef36e54b0f4298aa8ba26020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1661226e575f8381527f605b91b3ea139b87df3e6c49663a6d47de4209dcae24ef36e54b0f4298aa8ba26020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016109d7565b505f6109d7565b5f8281527f605b91b3ea139b87df3e6c49663a6d47de4209dcae24ef36e54b0f4298aa8ba26020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff161561226e575f8381527f605b91b3ea139b87df3e6c49663a6d47de4209dcae24ef36e54b0f4298aa8ba26020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016109d7565b6110438133612a90565b73ffffffffffffffffffffffffffffffffffffffff82166123c9576040517fec442f050000000000000000000000000000000000000000000000000000000081525f6004820152602401610adb565b6123d633835f8434612b36565b807f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a680545f90612407908490613114565b909155505073ffffffffffffffffffffffffffffffffffffffff82165f8181527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a960209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461216f576040517f9dbf33fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216612549576040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081525f6004820152602401610adb565b61255633835f8434612b6b565b73ffffffffffffffffffffffffffffffffffffffff82165f9081527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a96020526040902054818110156125fa576040517fe450d38c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024810182905260448101839052606401610adb565b73ffffffffffffffffffffffffffffffffffffffff83165f8181527f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a96020908152604080832086860390557f20ac81eee32ebb0ce1cc28571a398ee55251facc4c67435df5fad5c48f3a24a680548790039055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3505050565b7fdcb758a6418d1b58c02fbe3eaed41774ca05a21b93617786a39e380345abe85f80547fffffffffffffffffffffffff000000000000000000000000000000000000000016905561104381612b99565b5f612741565b8381604001525f805f6044601c85015f875af1156127355760203d10156127275750813b15612739565b60205f803e505f5115612739565b5060015b949350505050565b6040516060810160405263095ea7b38152838160200152612764838587846126fd565b91508115612788576127785f8587846126fd565b50612785838587846126fd565b91505b509392505050565b336127996111b9565b73ffffffffffffffffffffffffffffffffffffffff161461216f576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610adb565b80602001516bffffffffffffffffffffffff165f03612833576040517f5f45b1dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600180546020909301516bffffffffffffffffffffffff9081166c01000000000000000000000000027fffffffffffffffff000000000000000000000000000000000000000000000000909416921691909117919091179055565b5f6128e7565b5f6040516060810160405263a9059cbb81528381602001528481604001525f806044601c84015f875af1156127355760203d10156128d8575050803b15610ea3565b60205f803e50505f5115610ea3565b612739828486612896565b73ffffffffffffffffffffffffffffffffffffffff81161561104357803b8015610f9557604080517ffb2de5d700000000000000000000000000000000000000000000000000000000815230600482015260146024820152905173ffffffffffffffffffffffffffffffffffffffff84169163fb2de5d7916044808301925f92919082900301818387803b158015612988575f80fd5b505af1925050508015612999575060015b15610f95575050565b5f6129ab610b71565b905073ffffffffffffffffffffffffffffffffffffffff811615612a875773ffffffffffffffffffffffffffffffffffffffff811633036129ec57506111b1565b6040517f1854b24100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152878116602483015286811660448301526064820186905260848201859052821690631854b2419060a4015f604051808303815f87803b158015612a70575f80fd5b505af1158015612a82573d5f803e3d5ffd5b505050505b50505050505050565b5f8281527f605b91b3ea139b87df3e6c49663a6d47de4209dcae24ef36e54b0f4298aa8ba26020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610f95576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610adb565b612b647f00000000000000000000000000000000000000000000000000000000000000005f868686866129a2565b5050505050565b612b647f0000000000000000000000000000000000000000000000000000000000000000855f8686866129a2565b7f904c57a1db75b8be294e842878a18b28975f03a20160582f7aee6f3a5261820a805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f60208284031215612c3e575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610ea3575f80fd5b5f602080835283518060208501525f5b81811015612c9957858101830151858201604001528201612c7d565b505f6040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611ef8575f80fd5b5f8060408385031215612d0b575f80fd5b612d1483612cd7565b946020939093013593505050565b5f805f60608486031215612d34575f80fd5b612d3d84612cd7565b9250612d4b60208501612cd7565b9150604084013590509250925092565b5f8060408385031215612d6c575f80fd5b82359150612d7c60208401612cd7565b90509250929050565b5f805f60608486031215612d97575f80fd5b83359250612da760208501612cd7565b9150612db560408501612cd7565b90509250925092565b5f60208284031215612dce575f80fd5b610ea382612cd7565b5f8083601f840112612de7575f80fd5b50813567ffffffffffffffff811115612dfe575f80fd5b6020830191508360208260051b8501011115612e18575f80fd5b9250929050565b5f805f8060408587031215612e32575f80fd5b843567ffffffffffffffff80821115612e49575f80fd5b612e5588838901612dd7565b90965094506020870135915080821115612e6d575f80fd5b50612e7a87828801612dd7565b95989497509550505050565b5f60408284031215612e96575f80fd5b50919050565b5f60208284031215612eac575f80fd5b81358015158114610ea3575f80fd5b5f805f8060808587031215612ece575f80fd5b612ed785612cd7565b935060208501359250612eec60408601612cd7565b9150612efa60608601612cd7565b905092959194509250565b5f805f60608486031215612f17575f80fd5b612f2084612cd7565b95602085013595506040909401359392505050565b5f8060408385031215612f46575f80fd5b612f4f83612cd7565b9150612d7c60208401612cd7565b600181811c90821680612f7157607f821691505b602082108103612e96577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b80356bffffffffffffffffffffffff81168114611ef8575f80fd5b5f60408284031215613000575f80fd5b6040516040810181811067ffffffffffffffff82111715613048577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60405261305483612fd5565b815261306260208401612fd5565b60208201529392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820281158282048414176109d7576109d761306e565b5f826130e5577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b818103818111156109d7576109d761306e565b5f6020828403121561310d575f80fd5b5051919050565b808201808211156109d7576109d761306e56fea264697066735822122079606c70b7faee423a9bdcf49f90e34b9aaab8243779f741d4c52b6cd67bed8464736f6c634300081800330000000000000000000000000e00009d00d1000069ed00a908e00081f5006008

Deployed Bytecode

0x608060405234801561000f575f80fd5b506004361061003f575f3560e01c806303c268e0146100435780638d33f2bf1461007f578063bcc11f2514610092575b5f80fd5b61005661005136600461035b565b6100b9565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61005661008d36600461035b565b610213565b6100567f0000000000000000000000002b916962a73b76b415b811dc46519e59cebb01f381565b5f3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000e00009d00d1000069ed00a908e00081f50060081614610129576040517f66bc0c3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f0000000000000000000000002b916962a73b76b415b811dc46519e59cebb01f373ffffffffffffffffffffffffffffffffffffffff16803b806020016040519081016040528181525f908060200190933c8585857f0000000000000000000000000e00009d00d1000069ed00a908e00081f50060086040516020016101b394939291906104a7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526101ef9291602001610683565b6040516020818303038152906040529050858151602083015ff59695505050505050565b5f807f0000000000000000000000002b916962a73b76b415b811dc46519e59cebb01f373ffffffffffffffffffffffffffffffffffffffff16803b806020016040519081016040528181525f908060200190933c8585857f0000000000000000000000000e00009d00d1000069ed00a908e00081f500600860405160200161029e94939291906104a7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526102da9291602001610683565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828252805160209091012060608301909152915073ffffffffffffffffffffffffffffffffffffffff9060ff81533060601b60018201528760158201528260358201526055812082169350505050949350505050565b5f805f806080858703121561036e575f80fd5b84359350602085013567ffffffffffffffff81111561038b575f80fd5b8501610160818803121561039d575f80fd5b93969395505050506040820135916060013590565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126103e5575f80fd5b830160208101925035905067ffffffffffffffff811115610404575f80fd5b803603821315610412575f80fd5b9250929050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b803560ff81168114610470575f80fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610470575f80fd5b80358015158114610470575f80fd5b608081525f6104b686876103b2565b6101608060808601526104ce6101e086018385610419565b92506104dd60208a018a6103b2565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80808786030160a0880152610515858584610419565b945061052360408c01610460565b60ff811660c0890152935061053a60608c01610475565b73ffffffffffffffffffffffffffffffffffffffff811660e0890152935061056460808c01610475565b9350610100915061058c8288018573ffffffffffffffffffffffffffffffffffffffff169052565b610120935060a08b0135848801526105a760c08c018c6103b2565b610140838a890301818b01526105be888385610419565b97506105cc60e08f01610475565b73ffffffffffffffffffffffffffffffffffffffff81168b88015293506105f4858f01610498565b8015156101808c0152955061060a878f01610475565b73ffffffffffffffffffffffffffffffffffffffff9081166101a08c01529d01356101c08a01525050505060208501979097525060408301949094525093166060909301929092525090565b5f81515f5b81811015610675576020818501810151868301520161065b565b505f93019283525090919050565b5f6106976106918386610656565b84610656565b94935050505056fea26469706673582212203a49e81ca72b627730638f87be20959c387a2cf3e04ca478cf3f48b597f9905e64736f6c63430008180033

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

0000000000000000000000000e00009d00d1000069ed00a908e00081f5006008

-----Decoded View---------------
Arg [0] : tokenMasterRouter (address): 0x0E00009d00d1000069ed00A908e00081F5006008

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000000e00009d00d1000069ed00a908e00081f5006008


Deployed Bytecode Sourcemap

677:3855:36:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2304:669;;;;;;:::i;:::-;;:::i;:::-;;;936:42:37;924:55;;;906:74;;894:2;879:18;2304:669:36;;;;;;;3552:978;;;;;;:::i;:::-;;:::i;939:38::-;;;;;2304:669;2509:23;1020:10;:20;1034:6;1020:20;;1016:98;;1063:40;;;;;;;;;;;;;;1016:98;2544:21:::1;2611:13;:18;;;;;;;;;;;;;;;;;;;;;;;;;2679:10;2711:13;2746:20;2788:6;2647:165;;;;;;;;;;;:::i;:::-;;::::0;;;;;::::1;::::0;;;;;;;2581:245:::1;::::0;;2647:165:::1;2581:245;;:::i;:::-;;;;;;;;;;;;;2544:282;;2947:9;2936:8;2930:15;2923:4;2913:8;2909:19;2903:4;2895:62;2876:81:::0;2304:669;-1:-1:-1;;;;;;2304:669:36:o;3552:978::-;3764:25;3801:20;3877:13;:18;;;;;;;;;;;;;;;;;;;;;;;;;3945:10;3977:13;4012:20;4054:6;3913:165;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;3847:245;;;3913:165;3847:245;;:::i;:::-;;;;;;;;;;;;;;3824:278;;3847:245;3824:278;;;;4253:4;4244:14;;4231:28;;;3824:278;-1:-1:-1;6976:17:25;;4285:4:36;3847:245;4272:18;4336:9;4330:4;4326:20;4319:4;4314:3;4310:14;4303:44;4383:9;4376:4;4371:3;4367:14;4360:33;4429:12;4422:4;4417:3;4413:14;4406:36;4508:4;4503:3;4493:20;4480:11;4476:38;4455:59;;;4182:342;;3552:978;;;;;;:::o;14:609:37:-;144:6;152;160;168;221:3;209:9;200:7;196:23;192:33;189:53;;;238:1;235;228:12;189:53;274:9;261:23;251:33;;335:2;324:9;320:18;307:32;362:18;354:6;351:30;348:50;;;394:1;391;384:12;348:50;417:22;;473:3;455:16;;;451:26;448:46;;;490:1;487;480:12;448:46;14:609;;513:2;;-1:-1:-1;;;;562:2:37;547:18;;534:32;;613:2;598:18;585:32;;14:609::o;991:560::-;1050:5;1057:6;1117:3;1104:17;1199:66;1188:8;1172:14;1168:29;1164:102;1144:18;1140:127;1130:155;;1281:1;1278;1271:12;1130:155;1309:33;;1413:4;1400:18;;;-1:-1:-1;1361:21:37;;-1:-1:-1;1441:18:37;1430:30;;1427:50;;;1473:1;1470;1463:12;1427:50;1520:6;1504:14;1500:27;1493:5;1489:39;1486:59;;;1541:1;1538;1531:12;1486:59;991:560;;;;;:::o;1556:326::-;1645:6;1640:3;1633:19;1697:6;1690:5;1683:4;1678:3;1674:14;1661:43;;1749:1;1742:4;1733:6;1728:3;1724:16;1720:27;1713:38;1615:3;1871:4;1801:66;1796:2;1788:6;1784:15;1780:88;1775:3;1771:98;1767:109;1760:116;;1556:326;;;;:::o;1887:156::-;1953:20;;2013:4;2002:16;;1992:27;;1982:55;;2033:1;2030;2023:12;1982:55;1887:156;;;:::o;2128:196::-;2196:20;;2256:42;2245:54;;2235:65;;2225:93;;2314:1;2311;2304:12;2329:160;2394:20;;2450:13;;2443:21;2433:32;;2423:60;;2479:1;2476;2469:12;2590:2315;2889:3;2878:9;2871:22;2852:4;2936:47;2976:6;2968;2936:47;:::i;:::-;3002:6;3045:2;3039:3;3028:9;3024:19;3017:31;3071:75;3141:3;3130:9;3126:19;3112:12;3098;3071:75;:::i;:::-;3057:89;;3193:58;3245:4;3237:6;3233:17;3225:6;3193:58;:::i;:::-;3155:96;;3270:66;3401:2;3389:9;3381:6;3377:22;3373:31;3367:3;3356:9;3352:19;3345:60;3428:66;3487:6;3471:14;3455;3428:66;:::i;:::-;3414:80;;3525:35;3554:4;3546:6;3542:17;3525:35;:::i;:::-;2115:4;2104:16;;3617:3;3602:19;;2092:29;3503:57;-1:-1:-1;3653:37:37;3684:4;3676:6;3672:17;3653:37;:::i;:::-;705:42;694:54;;3749:3;3734:19;;682:67;3631:59;-1:-1:-1;3785:36:37;3816:3;3808:6;3804:16;3785:36;:::i;:::-;3763:58;;3840:3;3830:13;;3852:54;3902:2;3891:9;3887:18;3871:14;705:42;694:54;682:67;;628:127;3852:54;3925:3;3915:13;;3989:3;3981:6;3977:16;3964:30;3959:2;3948:9;3944:18;3937:58;4042:57;4094:3;4086:6;4082:16;4074:6;4042:57;:::i;:::-;4118:3;4185:2;4173:9;4165:6;4161:22;4157:31;4152:2;4141:9;4137:18;4130:59;4212:66;4271:6;4255:14;4239;4212:66;:::i;:::-;4198:80;;4309:36;4340:3;4332:6;4328:16;4309:36;:::i;:::-;705:42;694:54;;4389:18;;;682:67;4287:58;-1:-1:-1;4439:32:37;4467:2;4459:6;4455:15;4439:32;:::i;:::-;2564:13;;2557:21;4527:3;4512:19;;2545:34;4417:54;-1:-1:-1;4563:35:37;4594:2;4586:6;4582:15;4563:35;:::i;:::-;705:42;694:54;;;4657:3;4642:19;;682:67;4712:15;;4699:29;4693:3;4678:19;;4671:58;-1:-1:-1;;;;4783:4:37;4768:20;;4761:36;;;;-1:-1:-1;4828:4:37;4813:20;;4806:36;;;;-1:-1:-1;694:54:37;;4893:4;4878:20;;;682:67;;;;-1:-1:-1;4746:6:37;2590:2315::o;4910:322::-;4951:3;4989:5;4983:12;5013:1;5023:128;5037:6;5034:1;5031:13;5023:128;;;5134:4;5119:13;;;5115:24;;5109:31;5096:11;;;5089:52;5052:12;5023:128;;;-1:-1:-1;5206:1:37;5170:16;;5195:13;;;-1:-1:-1;5170:16:37;;4910:322;-1:-1:-1;4910:322:37:o;5237:261::-;5412:3;5437:55;5462:29;5487:3;5479:6;5462:29;:::i;:::-;5454:6;5437:55;:::i;:::-;5430:62;5237:261;-1:-1:-1;;;;5237:261:37:o

Swarm Source

ipfs://79606c70b7faee423a9bdcf49f90e34b9aaab8243779f741d4c52b6cd67bed84

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.