MNT Price: $0.86 (+0.92%)

Contract

0x1E1577ba4dC74007ED6e8FB3d3CE2Ce5477f531D
 

Overview

MNT Balance

Mantle Mainnet Network LogoMantle Mainnet Network LogoMantle Mainnet Network Logo0 MNT

MNT Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
874152682025-11-12 15:20:4874 days ago1762960848  Contract Creation0 MNT

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PrintrDev

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 2000 runs

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import { IPrintrGetters } from "../interfaces/printr/IPrintrGetters.sol";
import { IPrintrStorage } from "../interfaces/printr/IPrintrStorage.sol";
import { IPrintrTrading } from "../interfaces/printr/IPrintrTrading.sol";

import { IPrintrDev } from "../interfaces/telecoin/IPrintrDev.sol";
import { IPrintrTeleportingTelecoin } from "../interfaces/telecoin/IPrintrTeleportingTelecoin.sol";

/**
 * @title PrintrDev
 * @notice NFT contract that represents developer rights for PRINTR telecoins
 * @dev One NFT is minted per PRINTR telecoin to track the developer
 */
contract PrintrDev is ERC721, IPrintrDev {

    using Strings for bytes32;
    using Strings for uint256;
    using Strings for address;

    /// @notice The Printr contract that can mint NFTs
    address public immutable printr;

    /**
     * @notice Initialize the Dev NFT contract
     * @dev Sets the Printr contract address
     * @param _printr The address of the Printr contract
     */
    constructor(
        address _printr
    ) ERC721("Printr Dev Liquidity Position", "PRINTR DEV") {
        printr = _printr;
    }

    /**
     * @notice Mints a new Dev NFT representing developer rights for a PRINTR token
     * @dev Only callable by the Printr contract. The NFT token ID is derived from the telecoin ID.
     *      Each PRINTR telecoin can only have one associated Dev NFT, ensuring unique ownership.
     * @param telecoinId The unique identifier of the PRINTR telecoin (bytes32)
     * @param creator The address that will receive the minted Dev NFT
     * @return id The minted NFT token ID (uint256 representation of telecoinId)
     * @custom:throws OnlyPrintrCanMint if caller is not the Printr contract
     * @custom:throws PositionAlreadyCreated if a Dev NFT already exists for this telecoin ID
     */
    function mint(
        bytes32 telecoinId,
        address creator
    ) external returns (uint256 id) {
        if (msg.sender != printr) {
            revert OnlyPrintrCanMint();
        }

        // Use token address as NFT ID directly
        id = uint256(telecoinId);
        if (_ownerOf(id) != address(0)) {
            revert PositionAlreadyCreated();
        }

        _mint(creator, id);
    }

    /**
     * @notice Generates the metadata URI for a Dev NFT
     * @dev Creates a base64-encoded JSON metadata object containing the NFT's name, description,
     *      image URL, external URL, and comprehensive attributes array with 10 metadata fields.
     *      Function is split into helpers to avoid stack too deep with Paris EVM version.
     * @param id The NFT token ID to generate metadata for
     * @return The complete token URI as a data URL with base64-encoded JSON metadata
     * @custom:throws ERC721NonexistentToken if the token ID does not exist
     */
    function tokenURI(
        uint256 id
    ) public view virtual override(ERC721, IERC721Metadata) returns (string memory) {
        _requireOwned(id);

        address token = idToToken(id);
        bytes memory json = abi.encodePacked(
            _buildBaseMetadata(id, token), _buildTokenAttributes(token), _buildCurveAttributes(token), "]}"
        );

        return string(abi.encodePacked("data:application/json;base64,", Base64.encode(json)));
    }

    /**
     * @notice Builds the base metadata JSON (name, description, image, external_url)
     * @dev Helper function to reduce stack depth in tokenURI
     * @param id The NFT token ID
     * @param token The token address
     * @return Base metadata JSON string as bytes
     */
    function _buildBaseMetadata(
        uint256 id,
        address token
    ) internal pure returns (bytes memory) {
        return abi.encodePacked(
            '{"name":"PRINTR Dev Position #',
            id.toString(),
            '","description":"Developer rights for PRINTR telecoin"',
            ',"external_url":"https://printr.money/token/',
            token.toHexString(),
            '","image":"https://cdn.printr.money/nft/',
            id.toHexString(32),
            '.svg","attributes":['
        );
    }

    /**
     * @notice Builds token-specific attributes (address, name, symbol, decimals, creator)
     * @dev Helper function to reduce stack depth in tokenURI
     * @param token The token address
     * @return Token attributes JSON as bytes
     */
    function _buildTokenAttributes(
        address token
    ) internal view returns (bytes memory) {
        return abi.encodePacked(
            '{"trait_type":"Token Address","value":"',
            token.toHexString(),
            '"},{"trait_type":"Token Name","value":"',
            _getTokenName(token),
            '"},{"trait_type":"Token Symbol","value":"',
            _getTokenSymbol(token),
            '"},{"trait_type":"Decimals","value":"18"}',
            ',{"trait_type":"Creator","value":"',
            ownerOf(uint256(IPrintrTeleportingTelecoin(token).telecoinId())).toHexString(),
            '"}'
        );
    }

    /**
     * @notice Builds curve-specific attributes (price, supply, status, base pair)
     * @dev Helper function to reduce stack depth in tokenURI. Fetches curve data internally.
     * @param token The token address
     * @return Curve attributes JSON as bytes
     */
    function _buildCurveAttributes(
        address token
    ) internal view returns (bytes memory) {
        IPrintrStorage.CurveInfo memory curve;
        try IPrintrGetters(printr).getCurve(token) returns (IPrintrStorage.CurveInfo memory c) {
            curve = c;
        } catch {
            // curve remains empty (basePair == address(0))
        }

        uint256 totalSupply = _getTotalSupply(token);

        return abi.encodePacked(
            ',{"trait_type":"Current Price","value":"',
            _getCurrentPrice(curve, totalSupply),
            '","display_type":"number"}',
            ',{"trait_type":"Chain Supply","value":"',
            totalSupply.toString(),
            '","display_type":"number"},{"trait_type":"Global Supply","value":"',
            curve.maxTokenSupply.toString(),
            '","display_type":"number"},{"trait_type":"Status","value":"',
            _getCurveStatus(curve.completionThreshold, totalSupply),
            '"},{"trait_type":"Base Pair Symbol","value":"',
            _getBasePairSymbol(curve.basePair),
            '"},{"trait_type":"Base Pair Address","value":"',
            curve.basePair.toHexString(),
            '"}'
        );
    }

    /**
     * @notice Safely retrieves the token name
     * @dev Returns "Unknown Token" if the name cannot be retrieved
     * @param token The token address
     * @return The token name or fallback value
     */
    function _getTokenName(
        address token
    ) internal view returns (string memory) {
        try IERC20Metadata(token).name() returns (string memory name) {
            return bytes(name).length > 0 ? name : "Unknown Token";
        } catch {
            return "Unknown Token";
        }
    }

    /**
     * @notice Safely retrieves the token symbol
     * @dev Returns "???" if the symbol cannot be retrieved
     * @param token The token address
     * @return The token symbol or fallback value
     */
    function _getTokenSymbol(
        address token
    ) internal view returns (string memory) {
        try IERC20Metadata(token).symbol() returns (string memory symbol) {
            return bytes(symbol).length > 0 ? symbol : "???";
        } catch {
            return "???";
        }
    }

    /**
     * @notice Safely retrieves the base pair token symbol
     * @dev Returns "None" if address is zero, "Unknown" if the symbol cannot be retrieved
     * @param basePair The base pair token address
     * @return The base pair symbol or fallback value
     */
    function _getBasePairSymbol(
        address basePair
    ) internal view returns (string memory) {
        if (basePair == address(0)) {
            return "None";
        }
        try IERC20Metadata(basePair).symbol() returns (string memory symbol) {
            return bytes(symbol).length > 0 ? symbol : "Unknown";
        } catch {
            return "Unknown";
        }
    }

    /**
     * @notice Safely retrieves the token total supply
     * @dev Returns 0 if the supply cannot be retrieved
     * @param token The token address
     * @return The token total supply or 0
     */
    function _getTotalSupply(
        address token
    ) internal view returns (uint256) {
        try IERC20(token).totalSupply() returns (uint256 supply) {
            return supply;
        } catch {
            return 0;
        }
    }

    /**
     * @notice Calculates the current price per token from curve data
     * @dev Uses the bonding curve formula: price = (reserve + virtualReserve) / supply
     * @param curve The curve information containing reserve data
     * @param totalSupply The current total supply of tokens
     * @return priceString Formatted price string or "0"
     */
    function _getCurrentPrice(
        IPrintrStorage.CurveInfo memory curve,
        uint256 totalSupply
    ) internal pure returns (string memory) {
        if (curve.basePair == address(0) || totalSupply == 0) {
            return "0";
        }

        // Calculate price: (reserve + virtualReserve) * 1e18 / supply
        // This gives approximate current price per token in base pair units
        uint256 totalReserve = curve.reserve + curve.virtualReserve;
        uint256 price = (totalReserve * 1e18) / totalSupply;

        return price.toString();
    }

    /**
     * @notice Determines if the token curve is active or graduated
     * @dev Compares total supply against completion threshold
     * @param completionThreshold The completion threshold from curve data
     * @param totalSupply The current total supply of the token
     * @return "Active" if curve is ongoing, "Graduated" if completed
     */
    function _getCurveStatus(
        uint256 completionThreshold,
        uint256 totalSupply
    ) internal pure returns (string memory) {
        if (completionThreshold == 0) {
            return "Graduated";
        }
        return totalSupply >= completionThreshold ? "Graduated" : "Active";
    }

    /**
     * @notice Converts a PRINTR telecoin address to its corresponding Dev NFT token ID
     * @dev Retrieves the telecoin ID from the PRINTR telecoin contract and converts it to uint256.
     *      This creates a deterministic mapping between PRINTR telecoins and their Dev NFTs.
     * @param token The address of the PRINTR telecoin contract
     * @return id The corresponding Dev NFT token ID (uint256 representation of telecoin ID)
     */
    function tokenToId(
        address token
    ) external view returns (uint256 id) {
        return uint256(IPrintrTeleportingTelecoin(token).telecoinId());
    }

    /**
     * @notice Converts a Dev NFT token ID to its corresponding PRINTR telecoin address
     * @dev Queries the Printr contract to get the telecoin address for the given telecoin ID.
     *      This provides the reverse mapping from Dev NFT to PRINTR telecoin.
     * @param id The Dev NFT token ID to look up
     * @return token The address of the corresponding PRINTR telecoin contract
     */
    function idToToken(
        uint256 id
    ) public view returns (address token) {
        return IPrintrGetters(printr).getTokenAddress(bytes32(id));
    }

}

File 2 of 31 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    mapping(uint256 tokenId => address) private _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        return _requireOwned(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual {
        _approve(to, tokenId, _msgSender());
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual returns (address) {
        _requireOwned(tokenId);

        return _getApproved(tokenId);
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

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

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
    function _getApproved(uint256 tokenId) internal view virtual returns (address) {
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
    }

    /**
     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
     * the `spender` for the specific `tokenId`.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
    function _increaseBalance(address account, uint128 value) internal virtual {
        unchecked {
            _balances[account] += value;
        }
    }

    /**
     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
        address from = _ownerOf(tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(address(0), tokenId, address(0), false);

            unchecked {
                _balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                _balances[to] += 1;
            }
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    /**
     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        _checkOnERC721Received(address(0), to, tokenId, data);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal {
        address previousOwner = _update(address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC721 standard to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId) internal {
        _safeTransfer(from, to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address to, uint256 tokenId, address auth) internal {
        _approve(to, tokenId, auth, true);
    }

    /**
     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        _tokenApprovals[tokenId] = to;
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
     * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    revert ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 0x20)
            let dataPtr := data
            let endPtr := add(data, mload(data))

            // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
            // set it to zero to make sure no dirty bytes are read in that section.
            let afterPtr := add(endPtr, 0x20)
            let afterCache := mload(afterPtr)
            mstore(afterPtr, 0x00)

            // Run over the input, 3 bytes at a time
            for {

            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 byte (24 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
                // Use this as an index into the lookup table, mload an entire word
                // so the desired character is in the least significant byte, and
                // mstore8 this least significant byte into the result and continue.

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // Reset the value that was cached
            mstore(afterPtr, afterCache)

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

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

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
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);
}

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

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

/**
 * @title Printr Getters Interface
 * @notice Interface for the PrintrGetters contract
 * @dev All price calculations use PRECISION_SCALAR (1e18) for accurate floating point math
 *      Implements cross-chain token deployment through ITelecoinFactory integration
 *      This interface defines the core storage and view functions for the Printr system
 */
interface IPrintrGetters is IPrintrStorage {

    /**
     * @notice Retrieves the address of a token based on its universal ID
     * @param telecoinId The universal token ID (deploySalt)
     * @return Address of the token associated with the given ID
     */
    function getTokenAddress(
        bytes32 telecoinId
    ) external view returns (address);

    /**
     * @notice Calculates the deterministic address for a token deployment
     * @dev Uses creator address and deployment parameters to compute the deployment address
     * @param tokenParams Parameters for the token deployment
     * @return tokenAddress The calculated token deployment address
     */
    function getTokenAddress(
        TelecoinParams calldata tokenParams
    ) external view returns (address tokenAddress);

    /**
     * @notice Retrieves information about a specific token
     * @param token Address of the token
     * @return CurveInfo struct containing the token's configuration and state
     */
    function getCurve(
        address token
    ) external view returns (CurveInfo memory);

    /**
     * @notice Gets the lock status for a token
     * @param token Address of the token to check
     * @return Array containing lock information
     */
    function getLiquidityLocks(
        address token
    ) external view returns (uint256[2] memory);

}

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

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

/**
 * @title Printr Storage Interface
 * @notice Interface for the Printr contract, which manages tokens with linear bonding curves
 * @dev All price calculations use PRECISION_SCALAR (1e18) for accurate floating point math
 *      Implements cross-chain token deployment through ITelecoinFactory integration
 *      This interface defines the core storage and view functions for the Printr system
 */
interface IPrintrStorage {

    /// @notice Custom errors for the Printr contract
    error FeeIsTooHigh(uint256 fee);
    error WrongChainName();
    error InsufficientPayment();
    error InsufficientInitialBuy();
    error InvalidBasePairDecimals();
    error InvalidBasePrices();
    error InvalidBasePairs();
    error InvalidCreatorAddress();
    error InvalidInitialPrice();
    error InvalidLength();
    error TooHighThreshold();
    error ZeroAmount();
    error PoolCreationFailed();
    error TokenNotFound();
    error SwapFailed();
    error LiquidityAlreadyDeployed();
    error LiquidityDeploymentFailed();
    error InvalidQuoteResult();
    error PriceExceedsLimit();
    error RefundFailed();
    error RenounceOwnershipDisabled();
    error InvalidImplementation();
    error FeePercentagesMustSum();
    error UnauthorizedCaller();
    error Create3AddressMismatch();

    /**
     * @notice Parameters structure for telecoin deployment
     * @dev Uses bytes32 for efficient storage and cross-chain compatibility
     * @param salt Unique identifier for deployment
     * @param creatorAddresses Creator addresses for different chains encoded as bytes
     * @param name Telecoin name encoded as bytes32, 31 characters max
     * @param symbol Telecoin symbol encoded as bytes32, 31 characters max
     * @param packedParams Packed parameters bit layout:
     *                     [0-7]     uint8   maxTokenSupplyE (power of 10)
     *                     [8-23]    uint16  completionThreshold (basis points, max 10000)
     *                     [24-135]  uint112 initialPrice, assuming 18 decimal places for both tokens
     *                     [136-255] uint120 initialBuySpending, assuming 18 decimal places
     * @param chains Chain names encoded as bytes32, first chain is the home chain
     * @param basePairs Base currency token addresses encoded as bytes32
     * @param basePrices Initial prices in base currency per chain as uint128 packed in bytes, 18 decimals for both
     */
    struct TelecoinParams {
        bytes32 salt;
        bytes creatorAddresses;
        bytes32 name;
        bytes32 symbol;
        bytes32 packedParams;
        bytes32[] chains;
        bytes32[] basePairs;
        bytes basePrices;
    }

    /**
     * @notice Unpacked version of the packedParams for easier handling
     * @dev Packed bytes32 converted into individual parameters
     * @param maxTokenSupplyE Maximum token supply as power of 10
     * @param completionThreshold Completion threshold in basis points (max 10000) of max supply
     * @param initialPrice Initial token price, assuming 18 decimal places for both tokens
     * @param initialBuySpending Initial buy amount, assuming 18 decimal places
     */
    struct UnpackedParams {
        uint8 maxTokenSupplyE;
        uint256 completionThreshold;
        uint256 initialPrice;
        uint256 initialBuySpending;
    }

    /**
     * @notice Comprehensive information about a token's configuration and state
     * @dev Whole struct is packed into 2 storage slots for efficiency
     * @param basePair Address of the base currency token (160 bits)
     * @param totalCurves Number of curves across all chains (16 bits)
     * @param maxTokenSupplyE Maximum token supply as pow of 10 (8 bits)
     * @param virtualReserveE Precision scalar for virtualReserve (8 bits)
     * @param virtualReserve Compressed virtual reserve balance for bonding curve (64 bits)
     * @param reserve Current reserve balance of the base currency token (192 bits)
     * @param completionThreshold Threshold for curve completion as percentage of total supply (64 bits)
     */
    struct Curve {
        /// @dev Slot 1: immutable (160 + 16 + 8 + 8 + 64 = 256 bits, fits in 1 slot)
        address basePair;
        uint16 totalCurves;
        uint8 maxTokenSupplyE;
        uint8 virtualReserveE;
        uint64 virtualReserve;
        /// @dev Slot 2: mutable (192 + 64 = 256 bits)
        uint192 reserve;
        uint64 completionThreshold;
    }

    /**
     * @notice Readable format of curve information
     * @param basePair Base currency token address
     * @param totalCurves Total number of curves across chains
     * @param maxTokenSupply Maximum token supply
     * @param virtualReserve Virtual reserve for curve calculations (already scaled with virtualReserveE)
     * @param reserve Current base currency reserve
     * @param completionThreshold Completion threshold value
     */
    struct CurveInfo {
        address basePair;
        uint16 totalCurves;
        uint256 maxTokenSupply;
        uint256 virtualReserve;
        uint256 reserve;
        uint256 completionThreshold;
    }

    /**
     * @notice Parameters for deploying Printr contracts
     * @dev Used to avoid stack too deep errors in constructor
     * @param chainName Name of the blockchain network
     * @param treasury Address of the treasury contract
     * @param legacyTreasury Address of the legacy treasury contract (for migration)
     * @param mainTelecoinFactory Address of the main token factory contract
     * @param teleportingTelecoinFactory Address of the teleporting token factory contract
     * @param its Address of the Interchain Token Service
     * @param itsFactory Address of the ITS factory
     * @param wrappedNativeToken Address of wrapped native token (e.g., WETH)
     * @param locker Address of the locker contract
     * @param liquidityModule Address of the liquidity module
     * @param create3Deployer Address of the CREATE3 deployer for deterministic LZChannel deployments
     * @param growthFundFund Address of the growth fund
     * @param buybackFund Address of the buyback
     * @param teamTreasuryFund Address of the team treasury
     * @param stakingFund Address of the staking
     * @param printrDev Address of the dev NFT contract
     * @param legacyPrintrDev Address of the first legacy dev NFT contract (for migration from V1 - oldest original)
     * @param legacyPrintrDev2 Address of the second legacy dev NFT contract (for migration from V2 - without base64
     * fix)
     * @param feePercentGrowth Fee percentage for growth fund in basis points
     * @param feePercentBuyback Fee percentage for buyback in basis points
     * @param feePercentTeam Fee percentage for team treasury in basis points
     * @param feePercentCreator Fee percentage for creator in basis points
     * @param tradingFee Trading fee percentage in basis points
     */
    struct DeploymentParams {
        string chainName;
        address treasury;
        address legacyTreasury;
        address mainTelecoinFactory;
        address teleportingTelecoinFactory;
        address its;
        address itsFactory;
        address wrappedNativeToken;
        address locker;
        address liquidityModule;
        address create3Deployer;
        address growthFund;
        address buybackFund;
        address teamTreasuryFund;
        address stakingFund;
        address printrDev;
        address legacyPrintrDev;
        address legacyPrintrDev2;
        uint256 feePercentGrowth;
        uint256 feePercentBuyback;
        uint256 feePercentTeam;
        uint256 feePercentCreator;
        uint16 tradingFee;
    }

    /// @notice Returns the current chain's hash identifier
    function currentChainHash() external view returns (bytes32);

    /// @notice Returns the treasury contract address
    function treasury() external view returns (ITreasury);

    /// @notice Returns the main token factory contract address
    function mainTelecoinFactory() external view returns (address);

    /// @notice Returns the teleporting token factory contract address
    function teleportingTelecoinFactory() external view returns (address);

    /// @notice Returns the ITS contract address
    function interchainTokenService() external view returns (address);

    /// @notice Returns the ITS factory contract address
    function itsFactory() external view returns (address);

    /// @notice Returns the wrapped native token address
    function wrappedNativeToken() external view returns (address);

    /// @notice Returns the liquidity module contract address
    function liquidityModule() external view returns (address);

    /// @notice Returns the locker contract address
    function locker() external view returns (address);

    /// @notice Returns the CREATE3 deployer address
    function create3Deployer() external view returns (address);

    /// @notice Returns the growth fund address
    function growthFund() external view returns (address);

    /// @notice Returns the buyback address
    function buybackFund() external view returns (address);

    /// @notice Returns the team treasury address
    function teamTreasuryFund() external view returns (address);

    /// @notice Returns the staking address
    function stakingFund() external view returns (address);

    /// @notice Returns the creator NFT contract address
    function printrDev() external view returns (address);

    /// @notice Returns the fee percentage for growth fund
    function feePercentGrowth() external view returns (uint256);

    /// @notice Returns the fee percentage for buyback
    function feePercentBuyback() external view returns (uint256);

    /// @notice Returns the fee percentage for team treasury
    function feePercentTeam() external view returns (uint256);

    /// @notice Returns the fee percentage for creator
    function feePercentCreator() external view returns (uint256);

    /// @notice Returns the trading fee percentage in basis points
    function tradingFee() external view returns (uint16);

}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.27;

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

/**
 * @title Printr Trading Interface
 * @notice Interface for trading operations on tokens with linear bonding curves
 * @dev All price calculations use PRECISION_SCALAR (1e18) for accurate floating point math
 *      Handles buying, selling, and cross-chain trading operations
 */
interface IPrintrTrading is IPrintrStorage {

    /**
     * @notice Parameters for token trading operations
     * @param account Account performing the trade
     * @param token Address of the token being traded
     * @param amount Amount of tokens in the trade
     * @param priceLimit Maximum/minimum price limit for slippage protection
     * @param tokenSupply Current token supply before the trade
     * @param tradingFee Fee percentage applied to the trade
     */
    struct TradeParams {
        address account;
        address recipient;
        address token;
        uint256 amount;
        uint256 priceLimit;
        uint16 tradingFee;
    }

    /**
     * @notice Emitted when tokens are traded through the bonding curve
     * @param token Address of the token contract
     * @param trader Address that performed the trade
     * @param isBuy True if tokens were bought, false if sold
     * @param amount Number of tokens traded
     * @param cost Amount of base currency involved in the trade
     * @param priceAfter Price per token achieved in the trade
     * @param issuedSupply New total supply after the trade
     * @param reserve New reserve balance after the trade
     */
    event TokenTrade(
        address indexed token,
        address indexed trader,
        bool isBuy,
        uint256 amount,
        uint256 cost,
        uint256 priceAfter,
        uint256 issuedSupply,
        uint256 reserve
    );

    /**
     * @notice Emitted when a token graduates from bonding curve to liquidity pool
     * @param token Address of the token that graduated
     * @param totalSupply Total supply at graduation
     */
    event TokenGraduated(address indexed token, uint256 totalSupply);

    /**
     * @notice Emitted when a token graduation process encounters a partial failure
     * @dev Used for graceful degradation when liquidity deployment or post-graduation swap fails
     * @param token Address of the token that experienced partial graduation failure
     * @param stage Stage at which failure occurred (0=liquidity deployment, 1=post-graduation swap)
     */
    event TokenGraduationPartialFailure(address indexed token, uint8 stage, uint256 gasLeft);

    /**
     * @notice Estimates the cost of issuing a specific amount of tokens
     * @dev Uses linear bonding curve. All calculations are scaled by PRECISION_SCALAR
     * @param token Address of the token
     * @param tokenAmount Number of tokens to issue
     * @return availableAmount Amount of tokens available for issuing
     * @return cost Cost in base currency to issue the specified amount of tokens
     * @return fee Trading fee amount in base currency
     * @return priceAfter Effective price per token in base currency
     * @return issuedSupply New total supply after the issue
     */
    function estimateTokenCost(
        address token,
        uint256 tokenAmount
    ) external returns (uint256 availableAmount, uint256 cost, uint256 fee, uint256 priceAfter, uint256 issuedSupply);

    /**
     * @notice Quote the amount of tokens receivable for a specific amount of base currency
     * @dev Calculates tokens received including trading fees
     * @param token The token address
     * @param baseAmount The amount of base currency to spend
     * @return tokenAmount The amount of tokens receivable
     * @return cost The actual amount in base currency required for the purchase
     * @return fee The trading fee amount in base currency
     * @return priceAfter The effective price per token
     * @return issuedSupply The new total supply after the purchase
     */
    function quoteTokenAmount(
        address token,
        uint256 baseAmount
    ) external returns (uint256 tokenAmount, uint256 cost, uint256 fee, uint256 priceAfter, uint256 issuedSupply);

    /**
     * @notice Estimates the refund amount for redeeming a specific amount of tokens
     * @dev Uses the same linear bonding curve as issue, but in reverse
     * @param token Address of the token
     * @param tokenAmount Number of tokens to redeem
     * @return tokenAmountIn Amount of tokens that can actually be sold (capped by supply)
     * @return refund Refund amount in base currency for redeeming the specified tokens
     * @return fee Trading fee amount in base currency
     * @return priceAfter Effective price per token in base currency
     * @return issuedSupply New total supply after the redemption
     */
    function estimateTokenRefund(
        address token,
        uint256 tokenAmount
    ) external returns (uint256 tokenAmountIn, uint256 refund, uint256 fee, uint256 priceAfter, uint256 issuedSupply);

    /**
     * @notice Issues tokens according to the bonding curve
     * @param token Address of the token to issue
     * @param recipient Address to receive the issued tokens
     * @param amount Amount of tokens to issue
     * @param maxPrice Maximum acceptable price per token for slippage protection
     */
    function buy(
        address token,
        address recipient,
        uint256 amount,
        uint256 maxPrice
    ) external payable returns (TradeParams memory params);

    /**
     * @notice Buys tokens with a specified amount of base currency
     * @param token Address of the token to buy
     * @param recipient Address to receive the tokens
     * @param baseAmount Amount of base currency to spend. Pass type(uint256).max to use
     *                   the maximum available amount (all approved tokens or all sent ETH)
     * @param maxPrice Maximum acceptable price per token for slippage protection
     */
    function spend(
        address token,
        address recipient,
        uint256 baseAmount,
        uint256 maxPrice
    ) external payable returns (TradeParams memory params);

    /**
     * @notice Redeems tokens and returns base currency according to the bonding curve
     * @param token Address of the token to redeem
     * @param recipient Address to receive the refunded base currency
     * @param amount Amount of tokens to redeem. Pass type(uint256).max to sell all tokens
     * @param minPrice Minimum acceptable refund per token for slippage protection
     */
    function sell(
        address token,
        address recipient,
        uint256 amount,
        uint256 minPrice
    ) external returns (TradeParams memory params);

    /**
     * @notice Sells tokens and returns base currency according to the bonding curve
     * @dev Called by the Telecoin contract via permitWitnessCall
     * @param account Address of the account selling tokens
     * @param token Address of the token to sell
     * @param recipient Address to receive the refund
     * @param amount Amount of tokens to sell. Pass type(uint256).max to sell all tokens
     * @param minPrice Minimum acceptable price per token
     */
    function witnessSell(
        address account,
        address token,
        address recipient,
        uint256 amount,
        uint256 minPrice
    ) external returns (TradeParams memory params);

}

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

import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";

/**
 * @title IPrintrDev
 * @notice Interface for the Dev NFT contract that tracks token creators
 * @dev Each PRINTR token gets one Dev NFT minted to its creator
 */
interface IPrintrDev is IERC721, IERC721Metadata {

    /// @notice Custom errors for the PrintrDev contract
    error OnlyPrintrCanMint();
    error PositionAlreadyCreated();

    /**
     * @notice Mint a new Dev NFT for a token
     * @dev Only callable by the Printr contract
     * @param telecoinId The PRINTR token ID (bytes32)
     * @param creator The creator who will receive the NFT
     * @return id The minted NFT token ID
     */
    function mint(
        bytes32 telecoinId,
        address creator
    ) external returns (uint256 id);

    /**
     * @notice Get the NFT token ID for a specific PRINTR token
     * @param token The PRINTR token address
     * @return id The NFT token ID (which is the token address as uint256)
     */
    function tokenToId(
        address token
    ) external view returns (uint256 id);

    /**
     * @notice Get the PRINTR token address for a specific NFT token ID
     * @param id The NFT token ID
     * @return token The PRINTR token address
     */
    function idToToken(
        uint256 id
    ) external view returns (address token);

}

File 12 of 31 : IPrintrTeleportingTelecoin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import { IPrinted } from "./IPrinted.sol";
import { ITeleportingTelecoin } from "./ITeleporting.sol";

/**
 * @title IPrintrTeleportingTelecoin
 * @notice Interface for the PrintrTelecoin contract with teleport and curve completion functionality
 * @dev Extends ITelecoin interface with additional token management functions
 */
interface IPrintrTeleportingTelecoin is IPrinted, ITeleportingTelecoin { }

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

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

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

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

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

File 14 of 31 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

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

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

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

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

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

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

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

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @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.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

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

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

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

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

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

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

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

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

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

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

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

/**
 * @title ITreasury
 * @notice Interface for managing protocol fee collection and token withdrawals
 * @dev Handles LP fee collection and controlled withdrawals of tokens/native currency
 */
interface ITreasury {

    /**
     * @notice Thrown when an unauthorized address attempts to access treasury functions
     */
    error WrongAccess();

    /**
     * @notice Thrown when a withdrawal of native currency fails
     */
    error FailedWithdrawal();

    /**
     * @notice Thrown when a zero address is provided where a valid address is required
     */
    error ZeroAddress();

    /**
     * @notice Emitted when fees are collected from a liquidity position
     * @param token0 Address of the first token in the pair
     * @param token1 Address of the second token in the pair
     * @param recipient Address receiving the collected fees
     * @param amount0 Amount of token0 collected
     * @param amount1 Amount of token1 collected
     * @param lockId ID of the locked liquidity position
     */
    event CollectedLiquidityFees(
        address indexed token0,
        address indexed token1,
        address indexed recipient,
        uint256 amount0,
        uint256 amount1,
        uint256 lockId
    );

    /**
     * @notice Collects accumulated fees from a locked liquidity position by delegating to the liquidity module
     * @dev Only callable by authorized addresses. Delegates to the appropriate liquidity module
     *      which handles locker-specific logic (GoPlus, generic, etc.) and token ordering.
     * @param liquidityModule Address of the liquidity module that manages this position's DEX
     * @param lockId ID of the locked liquidity position
     * @param token Address of the first token in the pair
     * @param basePair Address of the second token in the pair (base currency)
     * @param recipient Address to receive the collected fees
     * @custom:throws WrongAccess if caller is not authorized
     */
    function collectLiquidityFees(
        address liquidityModule,
        uint256 lockId,
        address token,
        address basePair,
        address recipient
    ) external;

    /**
     * @notice Withdraws tokens or native currency from the treasury
     * @dev Only callable by authorized addresses
     * @param token Address of token to withdraw (address(0) for native currency)
     * @param recipient Address to receive the withdrawal
     * @param amount Amount to withdraw
     * @custom:throws WrongAccess if caller is not authorized
     * @custom:throws FailedWithdrawal if native currency transfer fails
     */
    function withdraw(
        address token,
        address recipient,
        uint256 amount
    ) external;

    /**
     * @notice Handles receipt of ERC721 tokens (LP position NFTs)
     * @dev Required for compatibility with ERC721 token transfers
     * @return bytes4 Function selector to indicate successful receipt
     */
    function onERC721Received(
        address, /* operator */
        address, /* from */
        uint256, /* tokenId */
        bytes calldata /* data */
    ) external pure returns (bytes4);

}

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

/**
 * @title IPrintrTeleportingTelecoin
 * @notice Interface for the PrintrTelecoin contract with teleport and curve completion functionality
 * @dev Extends ITelecoin interface with additional token management functions
 */
interface IPrinted {

    /**
     * @notice Enum representing the teleport type of a Telecoin
     * @dev Values 0-1 are legacy types for backward compatibility with deployed contracts
     *      Values 2-3 are new types that support teleporting by calling methods on a Telecoin
     *      - LegacyMain (0): Legacy Main Telecoin (bool false, uses lock/unlock)
     *      - LegacyTeleporting (1): Legacy Teleporting Telecoin (bool true, uses mint/burn)
     *      - Main (2): New Main Telecoin with Telecoin.teleport() support
     *      - Teleporting (3): New Teleporting Telecoin with Telecoin.teleport() support
     */
    enum TeleportType {
        LegacyMain, // 0 - backward compatible with bool false
        LegacyTeleporting, // 1 - backward compatible with bool true
        Main, // 2 - new Main Telecoin type
        Teleporting // 3 - new Teleporting Telecoin type
    }

    /**
     * @notice Error thrown when attempting to interact with an incomplete curve
     * @dev This error is typically thrown when trying to add liquidity before curve completion
     */
    error CurveIsNotComplete();

    /**
     * @notice Returns the teleport type of this Telecoin
     * @dev Values 0-1 are legacy (backward compatible), values 2-3 support transmitLzSend
     * @return teleportType The TeleportType enum value:
     *         - LegacyMain (0): Legacy Main Telecoin (lock/unlock)
     *         - LegacyTeleporting (1): Legacy Teleporting Telecoin (mint/burn)
     *         - Main (2): New Main Telecoin with transmitLzSend support
     *         - Teleporting (3): New Teleporting Telecoin with transmitLzSend support
     */
    function isTeleporting() external view returns (TeleportType teleportType);

    /**
     * @notice Sets the restricted pool address during curve initialization
     * @dev Can only be called by Printr before completion
     * @param poolAddress The pool address to restrict transfers to
     * @custom:throws UnauthorizedAccount if caller is not Printr or curve is already complete
     * @custom:throws ZeroAddress if pool address is zero
     */
    function setRestrictedPool(
        address poolAddress
    ) external;

    /**
     * @notice Marks the curve as completed, removing transfer restrictions
     * @dev Can only be called by Printr when curve is not already completed
     * @custom:throws UnauthorizedAccount if caller is not Printr or curve is already completed
     */
    function markCurveComplete() external;

}

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

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

/**
 * @title ITeleportingTelecoin
 * @notice Interface for the TeleportingTelecoin contract with teleporting capabilities
 * @dev Extends ITelecoin for teleport functionality with ITS integration
 */
interface ITeleportingTelecoin is ITelecoin {

    /**
     * @notice Error thrown when invalid function selector is provided
     */
    error InvalidFunctionSelector();

    /**
     * @notice Teleports tokens into existence for cross-chain transfers
     * @param to Address to receive the tokens
     * @param value Amount of tokens to teleport in
     */
    function teleportIn(
        address to,
        uint256 value
    ) external;

    /**
     * @notice Teleports tokens out of existence for cross-chain transfers
     * @param from Address whose tokens are being teleported out
     * @param value Amount of tokens to teleport out
     */
    function teleportOut(
        address from,
        uint256 value
    ) external;

}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

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

import { IOFT } from "../layerzero/IOFT.sol";
import { IPrintrTeleport } from "../printr/IPrintrTeleport.sol";
import { IERC20Witness } from "./IERC20Witness.sol";
import { IInterchainStandard } from "./IInterchainStandard.sol";

/**
 * @title ITelecoin
 * @notice Interface for the Telecoin base contract
 * @dev Extends IERC20 for base teleport functionality
 */
interface ITelecoin is IERC20Witness, IInterchainStandard, IOFT {

    /**
     * @notice Struct containing deployment parameters for telecoin contracts
     * @param name Token name
     * @param symbol Token symbol
     * @param maxSupply Maximum supply of the token
     * @param treasury Treasury address to receive initial supply
     * @param interchainTokenService Interchain Token Service address
     * @param itsTokenManager Token manager address
     * @param telecoinId Universal telecoin ID
     * @param interchainTokenId Interchain token ID for ITS compatibility
     */
    struct TelecoinDeployParams {
        bytes32 telecoinId;
        string name;
        string symbol;
        uint256 maxSupply;
        address printr;
        address interchainTokenService;
        address itsTokenManager;
        bytes32 interchainTokenId;
    }

    /**
     * @notice Error thrown when a zero address is provided where not allowed
     */
    error ZeroAddress();

    /**
     * @notice Error thrown when an unauthorized account attempts a restricted operation
     * @param account The address that attempted the unauthorized operation
     */
    error UnauthorizedAccount(address account);

    /**
     * @notice Error thrown when an invalid protocol is specified
     */
    error InvalidProtocol();

    /**
     * @notice Error thrown when a native token transfer fails
     */
    error TransferFailed();

    /**
     * @notice Emitted when tokens are teleported in from another chain
     * @param telecoinId The universal token ID
     * @param to The address receiving the tokens
     * @param value The amount of tokens teleported in
     */
    event TeleportIn(bytes32 indexed telecoinId, address indexed to, uint256 value);

    /**
     * @notice Emitted when tokens are teleported out to another chain
     * @param telecoinId The universal token ID
     * @param from The address from which tokens are teleported
     * @param value The amount of tokens teleported out
     */
    event TeleportOut(bytes32 indexed telecoinId, address indexed from, uint256 value);

    /**
     * @notice Returns the universal token ID (deploySalt)
     * @dev This value is immutable and set during contract construction
     * @return The universal token ID based on deployment salt
     */
    function telecoinId() external view returns (bytes32);

    /**
     * @notice Returns the address of the Printr contract that created this token
     * @dev This value is immutable and set during contract construction
     * @return The address of the Printr contract
     */
    function printr() external view returns (address);

    /**
     * @notice Returns the address of the ITS token manager for minting and burning
     * @dev This value is immutable and set during contract construction
     * @return The address of the ITS token manager
     */
    function itsTokenManager() external view returns (address);

    /**
     * @notice Quotes the total teleport fee for any protocol
     * @dev Delegates to PrintrTeleport's quoteTeleportFee for fee calculation
     * @param params The teleport parameters struct (includes protocol)
     * @return totalNativeFee The total fee in native currency (protocol fee + bridge fee)
     * @return basePairFee The fee in base pair tokens (0 if base pair is native)
     * @return basePair The base pair address (address(0) if native)
     * @return bridgeFee The bridge-specific gas fee (ITS gas fee or LZ messaging fee)
     */
    function quoteTeleportFee(
        IPrintrTeleport.TeleportParams memory params
    ) external returns (uint256 totalNativeFee, uint256 basePairFee, address basePair, uint256 bridgeFee);

    /**
     * @notice Teleports tokens from sender to a destination chain
     * @param params The teleport parameters struct (includes protocol)
     */
    function teleport(
        IPrintrTeleport.TeleportParams calldata params
    ) external payable;

    /**
     * @notice Teleports tokens from a specified sender to a destination chain
     * @param sender The sender of the tokens (must have approved msg.sender)
     * @param params The teleport parameters struct (includes protocol)
     */
    function teleportFrom(
        address sender,
        IPrintrTeleport.TeleportParams calldata params
    ) external payable;

}

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

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

/**
 * @dev Struct representing token parameters for the OFT send() operation.
 */
struct SendParam {
    uint32 dstEid; // Destination endpoint ID
    bytes32 to; // Recipient address
    uint256 amountLD; // Amount to send in local decimals
    uint256 minAmountLD; // Minimum amount to send in local decimals
    bytes extraOptions; // Additional options for LayerZero message
    bytes composeMsg; // Composed message for send() operation
    bytes oftCmd; // OFT command (unused in default implementations)
}

/**
 * @dev Struct representing OFT limit information.
 */
struct OFTLimit {
    uint256 minAmountLD; // Minimum amount in local decimals
    uint256 maxAmountLD; // Maximum amount in local decimals
}

/**
 * @dev Struct representing OFT receipt information.
 */
struct OFTReceipt {
    uint256 amountSentLD; // Amount debited from sender
    uint256 amountReceivedLD; // Amount to be received on remote side
}

/**
 * @dev Struct representing OFT fee details.
 */
struct OFTFeeDetail {
    int256 feeAmountLD; // Amount of fee in local decimals
    string description; // Description of the fee
}

/**
 * @title IOFT
 * @dev Interface for the Omnichain Fungible Token (OFT) standard.
 * @dev Interface ID: 0x02e49c2c
 */
interface IOFT {

    // Custom errors
    error InvalidLocalDecimals();
    error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);

    // Events
    event OFTSent(
        bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD
    );

    event OFTReceived(bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD);

    /**
     * @notice Retrieves interfaceID and version of the OFT.
     * @return interfaceId The interface ID (0x02e49c2c).
     * @return version The version.
     */
    function oftVersion() external view returns (bytes4 interfaceId, uint64 version);

    /**
     * @notice Retrieves the address of the token associated with the OFT.
     * @return token The address of the ERC20 token implementation.
     */
    function token() external view returns (address);

    /**
     * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
     * @return requiresApproval Whether approval is required.
     */
    function approvalRequired() external view returns (bool);

    /**
     * @notice Retrieves the shared decimals of the OFT.
     * @return sharedDecimals The shared decimals (typically 6).
     */
    function sharedDecimals() external view returns (uint8);

    /**
     * @notice Provides a quote for OFT-related operations.
     * @param _sendParam The parameters for the send operation.
     * @return limit The OFT limit information.
     * @return oftFeeDetails The details of OFT fees.
     * @return receipt The OFT receipt information.
     */
    function quoteOFT(
        SendParam calldata _sendParam
    ) external returns (OFTLimit memory limit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory receipt);

    /**
     * @notice Provides a quote for the send() operation.
     * @param _sendParam The parameters for the send() operation.
     * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
     * @return fee The calculated LayerZero messaging fee.
     */
    function quoteSend(
        SendParam calldata _sendParam,
        bool _payInLzToken
    ) external returns (ILayerZeroEndpointV2.MessagingFee memory fee);

    /**
     * @notice Executes the send() operation.
     * @param _sendParam The parameters for the send operation.
     * @param _fee The fee information supplied by the caller.
     * @param _refundAddress The address to receive any excess funds.
     * @return receipt The LayerZero messaging receipt.
     * @return oftReceipt The OFT receipt information.
     */
    function send(
        SendParam calldata _sendParam,
        ILayerZeroEndpointV2.MessagingFee calldata _fee,
        address _refundAddress
    ) external payable returns (ILayerZeroEndpointV2.MessagingReceipt memory receipt, OFTReceipt memory oftReceipt);

}

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

import { IPrintrStorage } from "../printr/IPrintrStorage.sol";

/**
 * @title IPrintrTeleport
 * @notice Interface for PrintrTeleport contract that handles cross-chain messaging with Solana
 * @dev This interface defines the functionality for sending and receiving messages between EVM and Solana
 *      using the Teleport message protocol for token transfers
 */
interface IPrintrTeleport is IPrintrStorage {

    /**
     * @notice Error thrown when payload has invalid length
     */
    error InvalidPayloadLength();

    /**
     * @notice Error thrown when message kind is unexpected
     */
    error UnexpectedMessageKind();

    /**
     * @notice Error thrown when endpoint address is invalid
     */
    error InvalidEndpoint();

    /**
     * @notice Error thrown when solana program address is invalid
     */
    error InvalidSolanaProgram();

    /**
     * @notice Error thrown when sender is not authorized
     */
    error UnauthorizedSender();

    /**
     * @notice Error thrown when contract has insufficient fee balance
     */
    error InsufficientFee();

    /**
     * @notice Error thrown when fee transfer fails
     */
    error FeeTransferFailed();

    /**
     * @notice Error thrown when an invalid protocol is specified
     */
    error InvalidProtocol();

    /**
     * @notice Error thrown when the chain ID length is invalid
     */
    error InvalidChainIdLength();

    /**
     * @notice Error thrown when a non-numeric character is found in chain ID
     */
    error InvalidNumericCharacter();

    /**
     * @notice Error thrown when the endpoint ID is invalid
     */
    error InvalidEndpointId();

    /**
     * @notice Error thrown when calldata is invalid
     */
    error InvalidCalldata();

    /**
     * @notice Error thrown when security level is invalid
     */
    error InvalidSecurityLevel();

    /**
     * @notice Error thrown when protocol is not in global whitelist
     */
    error ProtocolNotWhitelisted();

    /**
     * @notice Error thrown when protocol is in token blacklist
     */
    error ProtocolBlacklisted();

    /**
     * @notice Error thrown when invalid protocol bit position used
     */
    error InvalidProtocolBitPosition();

    /**
     * @notice Error thrown when teleport amount exceeds maximum supported value
     * @dev Maximum supported amount is type(uint64).max * 1e9 (in 18 decimals)
     * @param requestedAmount The amount that was requested to teleport
     * @param maxAmount The maximum amount that can be teleported
     */
    error TeleportAmountOverflow(uint256 requestedAmount, uint256 maxAmount);

    /**
     * @notice Protocol enum for teleportation methods
     * @dev Must match the Protocol enum in ITelecoin for compatibility
     */
    enum TeleportProtocol {
        UNSPECIFIED, // 0 - Used to catch malformed transactions
        ITS, // 1 - Interchain Token Service
        LZ_FAST, // 2 - Fast LayerZero channel with minimal confirmations
        LZ_SECURE, // 3 - Secure LayerZero channel with balanced confirmations
        LZ_SLOW // 4 - Slow LayerZero channel with maximum confirmations
    }

    /**
     * @notice Struct containing constructor parameters for LayerZero deployment
     * @param itsFlatFee Flat fee for ITS transfers
     * @param itsBipsFee Basis points fee for ITS transfers
     * @param lzFlatFee Flat fee for LayerZero transfers
     * @param lzBipsFee Basis points fee for LayerZero transfers
     * @param lzEndpoint LayerZero endpoint address for this chain
     * @param fastSolanaLzPeer Solana LayerZero peer address for fast/low security channel
     * @param secureSolanaLzPeer Solana LayerZero peer address for secure/medium security channel
     * @param slowSolanaLzPeer Solana LayerZero peer address for slow/high security channel
     * @param lzReceiveGasLimit Gas limit for LayerZero receive operations
     * @param lzReceiveNativeDrop Native drop amount for LayerZero receive operations
     */
    struct TeleportDeployParams {
        uint256 itsFlatFee;
        uint16 itsBipsFee;
        uint256 lzFlatFee;
        uint16 lzBipsFee;
        address lzEndpoint;
        bytes32 fastSolanaLzPeer;
        bytes32 secureSolanaLzPeer;
        bytes32 slowSolanaLzPeer;
        uint128 lzReceiveGasLimit;
        uint128 lzReceiveNativeDrop;
    }

    /**
     * @notice Struct containing teleport parameters
     * @param destChain The destination chain identifier
     * @param destAddress The bytes representation of the address of the recipient
     * @param amount The amount of tokens to be transferred
     * @param metadata Additional data for the cross-chain transfer
     * @param protocol The teleportation protocol to use (ITS, LZ_FAST, LZ_SECURE, LZ_SLOW)
     */
    struct TeleportParams {
        string destChain;
        bytes destAddress;
        uint256 amount;
        bytes metadata;
        TeleportProtocol protocol;
    }

    /**
     * @notice Event emitted when a new LayerZero delegate is set for a channel
     * @param protocol The teleport protocol (LZ_FAST, LZ_SECURE, LZ_SLOW)
     * @param delegate Address of the new LayerZero delegate
     */
    event LzDelegateUpdated(TeleportProtocol indexed protocol, address indexed delegate);

    /**
     * @notice Event emitted when global protocol whitelist is updated
     * @param newWhitelist The new whitelist bitmap
     */
    event TeleportProtocolWhitelistUpdated(bytes32 indexed newWhitelist);

    /**
     * @notice Event emitted when a token's protocol blacklist is updated
     * @param telecoinId The telecoin ID
     * @param newBlacklist The new blacklist bitmap
     */
    event TelecoinProtocolBlacklistUpdated(bytes32 indexed telecoinId, bytes32 indexed newBlacklist);

    /**
     * @notice Event emitted when tokens are teleported to another chain
     * @param telecoinId The ID of the token being transferred
     * @param tokenAddress The address of the token contract
     * @param sourceAddress The address initiating the transfer
     * @param destChain The target chain for the transfer
     * @param destAddress The address on the destination chain
     * @param amount The amount of tokens being transferred
     */
    event Teleporting(
        bytes32 indexed telecoinId,
        address indexed tokenAddress,
        address indexed sourceAddress,
        string destChain,
        bytes destAddress,
        uint256 amount
    );

    // ============================================
    // EXTERNAL FUNCTIONS
    // ============================================

    /**
     * @notice Calculates the universal token ID based on token parameters
     * @dev Returns the telecoinId as a universal token ID that's vendor-agnostic
     * @param tokenParams Parameters for the token deployment
     * @return telecoinId The universal token ID
     */
    function getTelecoinId(
        TelecoinParams calldata tokenParams
    ) external pure returns (bytes32 telecoinId);

    /**
     * @notice Sets the LayerZero delegate address for a specific channel
     * @dev Only callable by the contract owner or authorized account
     * @param protocol The teleport protocol to configure (LZ_FAST, LZ_SECURE, LZ_SLOW)
     * @param delegate Address of the new LayerZero delegate
     */
    function setLzDelegate(
        TeleportProtocol protocol,
        address delegate
    ) external;

    /**
     * @notice Gets the LZChannel address for a specific protocol
     * @param protocol The teleport protocol (LZ_FAST, LZ_SECURE, LZ_SLOW)
     * @return channel Address of the LZChannel contract for this protocol
     */
    function getLzChannel(
        TeleportProtocol protocol
    ) external view returns (address channel);

    /**
     * @notice Updates the global protocol whitelist by enabling and/or disabling protocols
     * @dev Only callable by the contract owner. Processes disable array first, then enable array.
     * @param enable Array of protocols to enable globally
     * @param disable Array of protocols to disable globally
     */
    function updateGlobalProtocols(
        TeleportProtocol[] calldata enable,
        TeleportProtocol[] calldata disable
    ) external;

    /**
     * @notice Gets the global protocol whitelist bitmap
     * @return whitelist Bitmap of enabled protocols
     */
    function getTeleportProtocolWhitelist() external view returns (bytes32 whitelist);

    /**
     * @notice Updates the protocol blacklist for a specific telecoin
     * @dev Only callable by the contract owner. Processes enable array first (removes from blacklist), then disable
     * array (adds to blacklist).
     * @param telecoinId The ID of the telecoin
     * @param enable Array of protocols to enable for this telecoin (remove from blacklist)
     * @param disable Array of protocols to disable for this telecoin (add to blacklist)
     */
    function updateTelecoinProtocols(
        bytes32 telecoinId,
        TeleportProtocol[] calldata enable,
        TeleportProtocol[] calldata disable
    ) external;

    /**
     * @notice Gets a token's protocol blacklist bitmap
     * @param telecoinId The telecoin ID
     * @return blacklist Bitmap of disabled protocols for this token
     */
    function getTelecoinProtocolBlacklist(
        bytes32 telecoinId
    ) external view returns (bytes32 blacklist);

    /**
     * @notice Checks if a protocol is allowed for a specific token
     * @dev Returns true if protocol is in global whitelist AND NOT in token blacklist
     * @param telecoinId The telecoin ID
     * @param protocol The protocol to check
     * @return allowed True if protocol is allowed for this token
     */
    function isProtocolAllowed(
        bytes32 telecoinId,
        TeleportProtocol protocol
    ) external view returns (bool allowed);

    /**
     * @notice Quotes the total teleport fee for any protocol
     * @dev Consolidates fee calculation for ITS, LayerZero, and future protocols
     * @param token Token address to transfer
     * @param params The teleport parameters struct (includes protocol)
     * @return totalNativeFee The total fee in native currency (protocol fee + bridge fee)
     * @return basePairFee The fee in base pair tokens (0 if base pair is native)
     * @return basePair The base pair address (address(0) if native)
     * @return bridgeFee The bridge-specific gas fee (ITS gas fee or LZ messaging fee)
     */
    function quoteTeleportFee(
        address token,
        TeleportParams calldata params
    ) external returns (uint256 totalNativeFee, uint256 basePairFee, address basePair, uint256 bridgeFee);

    /**
     * @notice Universal teleport function that routes to ITS or LayerZero
     * @dev Routes tokens to the appropriate cross-chain protocol based on params.protocol
     * @param token The token address to teleport
     * @param params The teleport parameters struct (includes protocol)
     */
    function teleport(
        address token,
        TeleportParams calldata params
    ) external payable;

    /**
     * @notice Universal teleport function with witness signature support
     * @dev Called via permitWitnessCall to enable signature-based teleportation
     * @dev Signer must be the first param to protect from permitWitnessCall attacks
     * @param signer Address initiating the teleport, must be first parameter
     * @param telecoinId The telecoin ID instead of token address
     * @param params The teleport parameters struct (includes protocol)
     */
    function witnessTeleport(
        address signer,
        bytes32 telecoinId,
        TeleportParams calldata params
    ) external payable;

    /**
     * @notice Transmits a LayerZero cross-chain token transfer
     * @dev Called by token contracts to initiate LayerZero transfers
     * @dev telecoinId first param, to protect from permitWitnessCall calls
     * @param telecoinId Universal token ID (32 bytes)
     * @param sender Address initiating the transfer
     * @param params The teleport parameters struct
     */
    function transmitLzSend(
        bytes32 telecoinId,
        address sender,
        TeleportParams calldata params
    ) external payable returns (bytes memory receipt);

    /**
     * @notice Broadcasts an interchain transfer event
     * @dev Emits an InterchainTransfer event with the provided parameters
     * @dev telecoinId first param, to protect from permitWitnessCall calls
     * @param telecoinId The ID of the token being transferred
     * @param sourceAddress The address initiating the transfer
     * @param destChain The target chain for the transfer
     * @param destAddress The address on the destination chain
     * @param amount The amount of tokens being transferred
     */
    function broadcastInterchainTransfer(
        bytes32 telecoinId,
        address sourceAddress,
        string calldata destChain,
        bytes calldata destAddress,
        uint256 amount
    ) external;

    /**
     * @notice Processes an incoming interchain transfer from Solana
     * @dev Processes tokens for the specified sender based on the received message
     * @param telecoinId The ID of the token being transferred
     * @param sender The address receiving the tokens
     * @param amount The amount of tokens to process
     */
    function processInterchainTransfer(
        bytes32 telecoinId,
        address sender,
        uint256 amount
    ) external;

    // ============================================
    // EXTERNAL VIEW FUNCTIONS
    // ============================================

    /**
     * @notice Gets the LayerZero endpoint address
     * @return The LayerZero endpoint address
     */
    function lzEndpoint() external view returns (address);

    /**
     * @notice Gets the current gas limit for LayerZero receive operations
     * @return gasLimit Current gas limit
     */
    function lzReceiveGasLimit() external view returns (uint128 gasLimit);

    /**
     * @notice Gets the current native drop amount for LayerZero receive operations
     * @return nativeDrop Current native drop amount
     */
    function lzReceiveNativeDrop() external view returns (uint128 nativeDrop);

    /**
     * @notice Receives teleport messages from authorized channels
     * @dev Called by LZChannel contracts to forward processed messages
     * @param protocol The teleport protocol of the calling channel (LZ_FAST, LZ_SECURE, LZ_SLOW)
     * @param originSender The LayerZero origin sender address (Solana peer or self)
     * @param message The processed teleport payload
     */
    function receiveFromChannel(
        TeleportProtocol protocol,
        bytes32 originSender,
        bytes calldata message
    ) external payable;

}

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

import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

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

/// @title IERC20Witness Interface
/// @notice Interface for ERC20 tokens with witness-based permit functionality
/// @dev Extends standard ERC20 with permitWitness functions that include additional validation data
interface IERC20Witness is IERC20Permit, IERC20Metadata, IERC20Errors {

    /*//////////////////////////////////////////////////////////////
                          WITNESS-SPECIFIC ERRORS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Thrown when an invalid witness value is provided
     * @dev The witness data failed validation checks
     */
    error ERC20InvalidWitness();

    /**
     * @notice Thrown when attempting to call the contract itself
     * @dev Self-calls are not permitted for security reasons
     */
    error ERC20InvalidCallTarget();

    /**
     * @notice Thrown when invalid call data is provided
     * @dev The call data format or content is malformed
     */
    error ERC20InvalidCallData();

    /**
     * @notice Thrown when an external call fails during witness operations
     * @dev The target contract call returned failure or reverted
     */
    error ERC20CallFailed();

    struct ContractCall {
        address target;
        string method;
        uint256 nativeValue;
        bytes params;
    }

    /*//////////////////////////////////////////////////////////////
                      PERMIT WITNESS LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the permit witness typehash for EIP-712 signature encoding
     * @dev Used for permitWitness function signature validation
     * @return The keccak256 hash of the permit witness type string
     */
    function PERMIT_WITNESS_TYPEHASH() external view returns (bytes32);

    /**
     * @notice Returns the base permit witness type string for EIP-712 structured data
     * @dev Used as a prefix to construct full type strings with witness data
     * @return The base permit witness type string stub
     */
    function PERMIT_WITNESS_TYPESTRING_STAB() external view returns (string memory);

    /**
     * @notice Returns the contract call witness type string completion
     * @dev Appended to PERMIT_WITNESS_TYPESTRING_STAB to form complete EIP-712 type string
     * @return The contract call type string completion
     */
    function WITNESS_CALL_TYPESTRING() external view returns (string memory);

    /**
     * @notice Returns the contract call typehash for EIP-712 signature encoding
     * @dev Used for ContractCall struct hashing in witness validation
     * @return The keccak256 hash of the ContractCall type string
     */
    function WITNESS_CALL_TYPEHASH() external view returns (bytes32);

    /**
     * @notice Returns the witness type identifier for transfer operations
     * @dev Used to validate witness data in transfer contexts
     * @return The bytes32 identifier for transfer witness operations
     */
    function TRANSFER_WITNESS() external view returns (bytes32);

    /**
     * @notice Sets allowance using signature with additional witness validation
     * @dev Extends standard permit functionality with witness data for enhanced security
     * @param owner The owner of the tokens
     * @param spender The address authorized to spend the tokens
     * @param value The amount of tokens to authorize for spending
     * @param deadline The timestamp at which the permit expires
     * @param witness Additional validation data to prevent certain attack vectors
     * @param v The recovery parameter of the signature (27 or 28)
     * @param r The first 32 bytes of the signature
     * @param s The second 32 bytes of the signature
     */
    function permitWitness(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        bytes32 witness,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Sets allowance using signature with custom witness type string for free-form witness data
     * @dev Extends permitWitness with witnessTypeString parameter to support various witness structures
     * @param owner The owner of the tokens
     * @param spender The address authorized to spend the tokens
     * @param value The amount of tokens to authorize for spending
     * @param deadline The timestamp at which the permit expires
     * @param witness Additional validation data hash to prevent certain attack vectors
     * @param witnessTypeString The EIP-712 type string for the witness data structure
     * @param v The recovery parameter of the signature (27 or 28)
     * @param r The first 32 bytes of the signature
     * @param s The second 32 bytes of the signature
     */
    function permitWitness(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        bytes32 witness,
        string calldata witnessTypeString,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Permits and executes a direct transfer using signature authorization
     * @dev Combines permit and transferFrom into a single operation with witness validation
     * @param owner The owner of the tokens to transfer
     * @param spender The recipient address for the transfer
     * @param value The amount of tokens to transfer
     * @param deadline The timestamp at which the permit expires
     * @param v The recovery parameter of the signature (27 or 28)
     * @param r The first 32 bytes of the signature
     * @param s The second 32 bytes of the signature
     * @return success Whether the transfer operation was successful
     */
    function permitTransferFrom(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (bool success);

    /**
     * @notice Permits token allowance and executes an external call in one transaction
     * @dev Combines permit functionality with external contract interaction using witness validation
     * @param owner The owner of the tokens
     * @param spender The address authorized to spend the tokens
     * @param value The amount of tokens to authorize for spending
     * @param deadline The timestamp at which the permit expires
     * @param call ContractCall struct containing target address, method signature, and parameters
     * @param v The recovery parameter of the signature (27 or 28)
     * @param r The first 32 bytes of the signature
     * @param s The second 32 bytes of the signature
     * @return returnData The return data from the executed external call
     */
    function permitWitnessCall(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        ContractCall calldata call,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable returns (bytes memory returnData);

}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IInterchainTokenService } from "../its/IInterchainTokenService.sol";

/**
 * @title IInterchainStandard Interface
 * @notice This interface defines functions for cross-chain token transfers.
 */
interface IInterchainStandard {

    /**
     * @notice Returns the Interchain Token Service instance.
     * @return The IInterchainTokenService contract instance.
     */
    function interchainTokenService() external view returns (IInterchainTokenService);

    /**
     * @notice Returns the interchain token identifier.
     * @return The bytes32 identifier of the interchain token.
     */
    function interchainTokenId() external view returns (bytes32);

    /**
     * @notice Implementation of the interchainTransfer method.
     * @dev We chose to either pass `metadata` as raw data on a remote contract call, or if no data is passed, just do a
     * transfer.
     * A different implementation could use metadata to specify a function to invoke, or for other purposes as well.
     * @param destinationChain The destination chain identifier.
     * @param recipient The bytes representation of the address of the recipient.
     * @param amount The amount of token to be transferred.
     * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract).
     */
    function interchainTransfer(
        string calldata destinationChain,
        bytes calldata recipient,
        uint256 amount,
        bytes calldata metadata
    ) external payable;

    /**
     * @notice Implementation of the interchainTransferFrom method
     * @dev We chose to either pass `metadata` as raw data on a remote contract call, or, if no data is passed, just do
     * a transfer.
     * A different implementation could use metadata to specify a function to invoke, or for other purposes as well.
     * @param sender The sender of the tokens. They need to have approved `msg.sender` before this is called.
     * @param destinationChain The string representation of the destination chain.
     * @param recipient The bytes representation of the address of the recipient.
     * @param amount The amount of token to be transferred.
     * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract.)
     */
    function interchainTransferFrom(
        address sender,
        string calldata destinationChain,
        bytes calldata recipient,
        uint256 amount,
        bytes calldata metadata
    ) external payable;

}

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

/**
 * @title ILayerZeroEndpointV2
 * @notice Interface for LayerZero V2 endpoint contract
 * @dev Defines the core messaging functions for cross-chain communication
 */
interface ILayerZeroEndpointV2 {

    /**
     * @notice Struct containing messaging fee information
     * @param nativeFee Native fee amount to send
     * @param lzTokenFee LayerZero token fee amount
     */
    struct MessagingFee {
        uint256 nativeFee;
        uint256 lzTokenFee;
    }

    /**
     * @notice Struct containing messaging parameters
     * @param dstEid Destination endpoint ID
     * @param receiver Receiver address on destination chain (as bytes32)
     * @param message Message payload to send
     * @param options Execution options for the message
     * @param payInLzToken Whether to pay fees in LZ token
     */
    struct MessagingParams {
        uint32 dstEid;
        bytes32 receiver;
        bytes message;
        bytes options;
        bool payInLzToken;
    }

    /**
     * @notice Struct containing messaging receipt information
     * @param guid Globally unique identifier for the message
     * @param nonce Message nonce
     * @param fee Messaging fee paid
     */
    struct MessagingReceipt {
        bytes32 guid;
        uint64 nonce;
        MessagingFee fee;
    }

    /**
     * @notice Send a message to another chain
     * @param params Messaging parameters
     * @param refundAddress Address to refund excess fees
     * @return receipt Message receipt containing guid and fee info
     */
    function send(
        MessagingParams calldata params,
        address refundAddress
    ) external payable returns (MessagingReceipt memory receipt);

    /**
     * @notice Quote the fee for sending a message
     * @param params Messaging parameters
     * @param sender The sender address
     * @return fee The messaging fee quote
     */
    function quote(
        MessagingParams calldata params,
        address sender
    ) external view returns (MessagingFee memory fee);

    /**
     * @notice Struct for setting configuration parameters
     * @param eid Endpoint ID
     * @param configType Configuration type (1=Executor, 2=ULN)
     * @param config Encoded configuration data
     */
    struct SetConfigParam {
        uint32 eid;
        uint32 configType;
        bytes config;
    }

    /**
     * @notice Set configuration for an OApp
     * @param oapp The OApp address to configure
     * @param lib The library address (SendLib or ReceiveLib)
     * @param params Array of configuration parameters
     */
    function setConfig(
        address oapp,
        address lib,
        SetConfigParam[] calldata params
    ) external;

    /**
     * @notice Set send library for an OApp
     * @param _oapp The OApp address
     * @param _eid Destination endpoint ID
     * @param _newLib Send library address
     */
    function setSendLibrary(
        address _oapp,
        uint32 _eid,
        address _newLib
    ) external;

    /**
     * @notice Set receive library for an OApp
     * @param _oapp The OApp address
     * @param _eid Source endpoint ID
     * @param _newLib Receive library address
     * @param _gracePeriod Grace period for library switch
     */
    function setReceiveLibrary(
        address _oapp,
        uint32 _eid,
        address _newLib,
        uint256 _gracePeriod
    ) external;

    /**
     * @notice Set delegate for message handling
     * @param delegate Address of the delegate
     */
    function setDelegate(
        address delegate
    ) external;

    /**
     * @notice Get the send library for an OApp and destination
     * @param _sender The sender address
     * @param _eid Destination endpoint ID
     * @return lib The send library address
     */
    function getSendLibrary(
        address _sender,
        uint32 _eid
    ) external view returns (address lib);

    /**
     * @notice Get the receive library for an OApp and source
     * @param _receiver The receiver address
     * @param _eid Source endpoint ID
     * @return lib The receive library address
     */
    function getReceiveLibrary(
        address _receiver,
        uint32 _eid
    ) external view returns (address lib, bool);

    /**
     * @notice Get the default send library for an endpoint
     * @param _eid Destination endpoint ID
     * @return The default send library address
     */
    function defaultSendLibrary(
        uint32 _eid
    ) external view returns (address);

    /**
     * @notice Get the default receive library for an endpoint
     * @param _eid Source endpoint ID
     * @return The default receive library address
     */
    function defaultReceiveLibrary(
        uint32 _eid
    ) external view returns (address);

}

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

/// @title IERC20Permit Interface with Errors
/// @notice Interface for ERC20 Permit extension with proper error definitions
/// @dev Extends the standard EIP-2612 permit functionality with error definitions
interface IERC20Permit {

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Thrown when a permit signature has expired
     * @param deadline The timestamp at which the signature expired
     */
    error ERC2612ExpiredSignature(uint256 deadline);

    /**
     * @notice Thrown when the recovered signer does not match the expected owner
     * @param signer The address recovered from the signature
     * @param owner The expected owner address
     */
    error ERC2612InvalidSigner(address signer, address owner);

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Emitted when an owner manually invalidates their nonce
     * @param owner The address that invalidated their nonce
     * @param newNonce The new nonce value after invalidation
     */
    event NonceInvalidated(address indexed owner, uint256 newNonce);

    /*//////////////////////////////////////////////////////////////
                          PERMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the current nonce for the given owner
     * @dev This value must be included whenever a signature is generated for permit
     * @param owner The address to get the nonce for
     * @return The current nonce value for the owner
     */
    function nonces(
        address owner
    ) external view returns (uint256);

    /**
     * @notice Returns the domain separator used in the encoding of signatures
     * @dev Used to prevent signature replay attacks across different domains
     * @return The EIP-712 domain separator hash
     */
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /**
     * @notice Returns the permit typehash used in EIP-712 signature encoding
     * @dev Constant value defining the structure of permit messages
     * @return The keccak256 hash of the permit type string
     */
    function PERMIT_TYPEHASH() external view returns (bytes32);

    /**
     * @notice Sets allowance using EIP-2612 signature-based authorization
     * @dev Allows setting allowance without requiring a separate transaction from the token owner
     * @param owner The owner of the tokens
     * @param spender The address authorized to spend the tokens
     * @param value The amount of tokens to authorize for spending
     * @param deadline The timestamp at which the permit expires
     * @param v The recovery parameter of the signature (27 or 28)
     * @param r The first 32 bytes of the signature
     * @param s The second 32 bytes of the signature
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Invalidates the current nonce for msg.sender
     * @dev Increments the nonce, making all outstanding permit signatures invalid
     * This provides users with an emergency mechanism to cancel pending permits in case of:
     * - Key compromise or suspected phishing
     * - Changed mind about a signed but not executed permit
     * - Need to invalidate multiple permits at once
     * Emits a NonceInvalidated event
     */
    function invalidateNonce() external;

}

// SPDX-License-Identifier: MIT
// https://github.com/axelarnetwork/interchain-token-service/blob/main/contracts/interfaces/IInterchainTokenService.sol

pragma solidity ^0.8.0;

type ITokenManager is address;

/**
 * @title IInterchainGasEstimation Interface
 * @notice This is an interface for the InterchainGasEstimation contract
 * which allows for estimating gas fees for cross-chain communication on the Axelar network.
 */
interface IInterchainGasEstimation {

    /**
     * @notice Estimates the gas fee for a cross-chain contract call.
     * @param destinationChain Axelar registered name of the destination chain
     * @param destinationAddress Destination contract address being called
     * @param executionGasLimit The gas limit to be used for the destination contract execution,
     *        e.g. pass in 200k if your app consumes needs upto 200k for this contract call
     * @param params Additional parameters for the gas estimation
     * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be
     * forwarded to the gas service.
     */
    function estimateGasFee(
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        uint256 executionGasLimit,
        bytes calldata params
    ) external view returns (uint256 gasEstimate);

}

/**
 * @title IInterchainTokenService Interface
 * @notice Interface for the Interchain Token Service
 */
interface IInterchainTokenService {

    enum TokenManagerType {
        NATIVE_INTERCHAIN_TOKEN, // This type is reserved for interchain tokens deployed by ITS, and can't be used by
        // custom token managers.
        MINT_BURN_FROM, // The token will be minted/burned on transfers. The token needs to give mint permission to the
        // token manager, but burning happens via an approval.
        LOCK_UNLOCK, // The token will be locked/unlocked at the token manager.
        LOCK_UNLOCK_FEE, // The token will be locked/unlocked at the token manager, which will account for any
        // fee-on-transfer behaviour.
        MINT_BURN // The token will be minted/burned on transfers. The token needs to give mint and burn permission to
        // the token manager.
    }

    event InterchainTransfer(
        bytes32 indexed tokenId,
        address indexed sourceAddress,
        string destinationChain,
        bytes destinationAddress,
        uint256 amount,
        bytes32 indexed dataHash
    );
    event InterchainTransferReceived(
        bytes32 indexed commandId,
        bytes32 indexed tokenId,
        string sourceChain,
        bytes sourceAddress,
        address indexed destinationAddress,
        uint256 amount,
        bytes32 dataHash
    );
    event TokenMetadataRegistered(address indexed tokenAddress, uint8 decimals);
    event LinkTokenStarted(
        bytes32 indexed tokenId,
        string destinationChain,
        bytes sourceTokenAddress,
        bytes destinationTokenAddress,
        TokenManagerType indexed tokenManagerType,
        bytes params
    );
    event InterchainTokenDeploymentStarted(
        bytes32 indexed tokenId,
        string tokenName,
        string tokenSymbol,
        uint8 tokenDecimals,
        bytes minter,
        string destinationChain
    );
    event TokenManagerDeployed(
        bytes32 indexed tokenId, address tokenManager, TokenManagerType indexed tokenManagerType, bytes params
    );
    event InterchainTokenDeployed(
        bytes32 indexed tokenId,
        address tokenAddress,
        address indexed minter,
        string name,
        string symbol,
        uint8 decimals
    );
    event InterchainTokenIdClaimed(bytes32 indexed tokenId, address indexed deployer, bytes32 indexed salt);

    /**
     * @notice Returns the address of the interchain gas service contract.
     * @return gasService The instance of the IInterchainGasEstimation contract.
     */
    function gasService() external view returns (IInterchainGasEstimation);

    /**
     * @notice Returns the address of the ITS Hub contract.
     * @return hubAddress The address of the ITS Hub contract.
     */
    function itsHubAddress() external view returns (string memory hubAddress);

    /**
     * @notice Returns the address of the token manager deployer contract.
     * @return tokenManagerDeployerAddress The address of the token manager deployer contract.
     */
    function tokenManagerDeployer() external view returns (address tokenManagerDeployerAddress);

    /**
     * @notice Returns the address of the interchain token deployer contract.
     * @return interchainTokenDeployerAddress The address of the interchain token deployer contract.
     */
    function interchainTokenDeployer() external view returns (address interchainTokenDeployerAddress);

    /**
     * @notice Returns the address of TokenManager implementation.
     * @return tokenManagerAddress_ The address of the token manager contract.
     */
    function tokenManager() external view returns (address tokenManagerAddress_);

    /**
     * @notice Returns the address of TokenHandler implementation.
     * @return tokenHandlerAddress The address of the token handler contract.
     */
    function tokenHandler() external view returns (address tokenHandlerAddress);

    /**
     * @notice Returns the address of the interchain token factory.
     * @return address The address of the interchain token factory.
     */
    function interchainTokenFactory() external view returns (address);

    /**
     * @notice Returns the hash of the chain name.
     * @return bytes32 The hash of the chain name.
     */
    function chainNameHash() external view returns (bytes32);

    /**
     * @notice Returns the address of the token manager associated with the given tokenId.
     * @param tokenId The tokenId of the token manager.
     * @return tokenManagerAddress_ The address of the token manager.
     */
    function tokenManagerAddress(
        bytes32 tokenId
    ) external view returns (address tokenManagerAddress_);

    /**
     * @notice Returns the instance of ITokenManager from a specific tokenId.
     * @param tokenId The tokenId of the deployed token manager.
     * @return tokenManager_ The instance of ITokenManager associated with the specified tokenId.
     */
    function deployedTokenManager(
        bytes32 tokenId
    ) external view returns (ITokenManager tokenManager_);

    /**
     * @notice Returns the address of the token that an existing tokenManager points to.
     * @param tokenId The tokenId of the registered token.
     * @return tokenAddress The address of the token.
     */
    function registeredTokenAddress(
        bytes32 tokenId
    ) external view returns (address tokenAddress);

    /**
     * @notice Returns the address of the interchain token associated with the given tokenId.
     * @param tokenId The tokenId of the interchain token.
     * @return tokenAddress The address of the interchain token.
     */
    function interchainTokenAddress(
        bytes32 tokenId
    ) external view returns (address tokenAddress);

    /**
     * @notice Returns the custom tokenId associated with the given operator and salt.
     * @param operator_ The operator address.
     * @param salt The salt used for token id calculation.
     * @return tokenId The custom tokenId associated with the operator and salt.
     */
    function interchainTokenId(
        address operator_,
        bytes32 salt
    ) external view returns (bytes32 tokenId);

    /**
     * @notice Registers metadata for a token on the ITS Hub. This metadata is used for scaling linked tokens.
     * The token metadata must be registered before linkToken can be called for the corresponding token.
     * @param tokenAddress The address of the token.
     * @param gasValue The cross-chain gas value for sending the registration message to ITS Hub.
     */
    function registerTokenMetadata(
        address tokenAddress,
        uint256 gasValue
    ) external payable;

    /**
     * @notice Only to be used by the InterchainTokenFactory to register custom tokens to this chain. Then link token
     * can be used to register those tokens to other chains.
     * @param salt A unique salt to derive tokenId from.
     * @param tokenManagerType The type of the token manager to use for the token registration.
     * @param linkParams The operator for the token.
     */
    function registerCustomToken(
        bytes32 salt,
        address tokenAddress,
        TokenManagerType tokenManagerType,
        bytes calldata linkParams
    ) external payable returns (bytes32 tokenId);

    /**
     * @notice If `destinationChain` is an empty string, this function will register the token address on the current
     * chain.
     * Otherwise, it will link the token address on the destination chain with the token corresponding to the tokenId on
     * the current chain.
     * A token manager is deployed on EVM chains that's responsible for managing the linked token.
     * @dev This function replaces the prior `deployTokenManager` function.
     * @param salt A unique identifier to allow for multiple tokens registered per deployer.
     * @param destinationChain The chain to link the token to. Pass an empty string for this chain.
     * @param destinationTokenAddress The token address to link, as bytes.
     * @param tokenManagerType The type of the token manager to use to send and receive tokens.
     * @param linkParams Additional parameteres to use to link the token. Fow not it is just the address of the
     * operator.
     * @param gasValue Pass a non-zero value only for remote linking, which should be the gas to use to pay for the
     * contract call.
     * @return tokenId The tokenId associated with the token manager.
     */
    function linkToken(
        bytes32 salt,
        string calldata destinationChain,
        bytes memory destinationTokenAddress,
        TokenManagerType tokenManagerType,
        bytes memory linkParams,
        uint256 gasValue
    ) external payable returns (bytes32 tokenId);

    /**
     * @notice Deploys and registers an interchain token on a remote chain.
     * @param salt The salt used for token deployment.
     * @param destinationChain The name of the destination chain. Use '' for this chain.
     * @param name The name of the interchain tokens.
     * @param symbol The symbol of the interchain tokens.
     * @param decimals The number of decimals for the interchain tokens.
     * @param minter The minter data for mint/burn operations.
     * @param gasValue The gas value for deployment.
     * @return tokenId The tokenId corresponding to the deployed InterchainToken.
     */
    function deployInterchainToken(
        bytes32 salt,
        string calldata destinationChain,
        string memory name,
        string memory symbol,
        uint8 decimals,
        bytes memory minter,
        uint256 gasValue
    ) external payable returns (bytes32 tokenId);

    /**
     * @notice Initiates an interchain transfer of a specified token to a destination chain.
     * @param tokenId The unique identifier of the token to be transferred.
     * @param destinationChain The destination chain to send the tokens to.
     * @param destinationAddress The address on the destination chain to send the tokens to.
     * @param amount The amount of tokens to be transferred.
     * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract).
     */
    function interchainTransfer(
        bytes32 tokenId,
        string calldata destinationChain,
        bytes calldata destinationAddress,
        uint256 amount,
        bytes calldata metadata,
        uint256 gasValue
    ) external payable;

    /**
     * @notice Sets the flow limits for multiple tokens.
     * @param tokenIds An array of tokenIds.
     * @param flowLimits An array of flow limits corresponding to the tokenIds.
     */
    function setFlowLimits(
        bytes32[] calldata tokenIds,
        uint256[] calldata flowLimits
    ) external;

    /**
     * @notice Allows the owner to pause/unpause the token service.
     * @param paused whether to pause or unpause.
     */
    function setPauseStatus(
        bool paused
    ) external;

    /**
     * @notice Allows the owner to migrate legacy tokens that cannot be migrated automatically.
     * @param tokenId the tokenId of the registered token.
     */
    function migrateInterchainToken(
        bytes32 tokenId
    ) external;

    /**
     * @notice Transmit an interchain transfer for the given tokenId.
     * @dev Only callable by a token registered under a tokenId.
     * @param tokenId The tokenId of the token (which must be the msg.sender).
     * @param sourceAddress The address where the token is coming from.
     * @param destinationChain The name of the chain to send tokens to.
     * @param destinationAddress The destinationAddress for the interchainTransfer.
     * @param amount The amount of token to give.
     * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract).
     */
    function transmitInterchainTransfer(
        bytes32 tokenId,
        address sourceAddress,
        string calldata destinationChain,
        bytes memory destinationAddress,
        uint256 amount,
        bytes calldata metadata
    ) external payable;

}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_printr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[],"name":"OnlyPrintrCanMint","type":"error"},{"inputs":[],"name":"PositionAlreadyCreated","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"idToToken","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"telecoinId","type":"bytes32"},{"internalType":"address","name":"creator","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"printr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"tokenToId","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234801561000f575f5ffd5b506040516128fa3803806128fa83398101604081905261002e916100b6565b6040518060400160405280601d81526020017f5072696e747220446576204c697175696469747920506f736974696f6e0000008152506040518060400160405280600a815260200169282924a72a29102222ab60b11b815250815f9081610095919061017b565b5060016100a2828261017b565b5050506001600160a01b0316608052610235565b5f602082840312156100c6575f5ffd5b81516001600160a01b03811681146100dc575f5ffd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061010b57607f821691505b60208210810361012957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561017657805f5260205f20601f840160051c810160208510156101545750805b601f840160051c820191505b81811015610173575f8155600101610160565b50505b505050565b81516001600160401b03811115610194576101946100e3565b6101a8816101a284546100f7565b8461012f565b6020601f8211600181146101da575f83156101c35750848201515b5f19600385901b1c1916600184901b178455610173565b5f84815260208120601f198516915b8281101561020957878501518255602094850194600190920191016101e9565b508482101561022657868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6080516126986102625f395f81816102aa01528181610528015281816107ad0152610dd301526126985ff3fe608060405234801561000f575f5ffd5b5060043610610115575f3560e01c806370a08231116100ad578063c87b56dd1161007d578063e985e9c511610063578063e985e9c514610257578063f5c6b96b14610292578063f7cca092146102a5575f5ffd5b8063c87b56dd14610231578063ca82a01414610244575f5ffd5b806370a08231146101f057806395d89b4114610203578063a22cb4651461020b578063b88d4fde1461021e575f5ffd5b806323b872dd116100e857806323b872dd14610196578063293c6a3a146101a957806342842e0e146101ca5780636352211e146101dd575f5ffd5b806301ffc9a71461011957806306fdde0314610141578063081812fc14610156578063095ea7b314610181575b5f5ffd5b61012c610127366004611b00565b6102cc565b60405190151581526020015b60405180910390f35b6101496103b0565b6040516101389190611b49565b610169610164366004611b5b565b61043f565b6040516001600160a01b039091168152602001610138565b61019461018f366004611b86565b610466565b005b6101946101a4366004611bb0565b610475565b6101bc6101b7366004611bee565b61051c565b604051908152602001610138565b6101946101d8366004611bb0565b6105da565b6101696101eb366004611b5b565b6105f9565b6101bc6101fe366004611c1c565b610603565b610149610661565b610194610219366004611c37565b610670565b61019461022c366004611cfc565b61067b565b61014961023f366004611b5b565b610692565b6101bc610252366004611c1c565b61071b565b61012c610265366004611da4565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b6101696102a0366004611b5b565b61077c565b6101697f000000000000000000000000000000000000000000000000000000000000000081565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061035e57507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b806103aa57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60605f80546103be90611dd0565b80601f01602080910402602001604051908101604052809291908181526020018280546103ea90611dd0565b80156104355780601f1061040c57610100808354040283529160200191610435565b820191905f5260205f20905b81548152906001019060200180831161041857829003601f168201915b5050505050905090565b5f6104498261081e565b505f828152600460205260409020546001600160a01b03166103aa565b61047182823361086f565b5050565b6001600160a01b0382166104a357604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f6104af83833361087c565b9050836001600160a01b0316816001600160a01b031614610516576040517f64283d7b0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152602482018490528216604482015260640161049a565b50505050565b5f336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461057f576040517f878d41a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f8281526002602052604090205482906001600160a01b0316156105d0576040517f0f04799c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103aa8282610986565b6105f483838360405180602001604052805f81525061067b565b505050565b5f6103aa8261081e565b5f6001600160a01b038216610646576040517f89c62b640000000000000000000000000000000000000000000000000000000081525f600482015260240161049a565b506001600160a01b03165f9081526003602052604090205490565b6060600180546103be90611dd0565b610471338383610a00565b610686848484610475565b61051684848484610ad5565b606061069d8261081e565b505f6106a88361077c565b90505f6106b58483610c45565b6106be83610c96565b6106c784610d59565b6040516020016106d993929190611e1f565b60405160208183030381529060405290506106f381610eac565b6040516020016107039190611e6b565b60405160208183030381529060405292505050919050565b5f816001600160a01b03166381cce2456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610758573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190611e9c565b6040517fb12e4410000000000000000000000000000000000000000000000000000000008152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b12e441090602401602060405180830381865afa1580156107fa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190611eb3565b5f818152600260205260408120546001600160a01b0316806103aa576040517f7e2732890000000000000000000000000000000000000000000000000000000081526004810184905260240161049a565b6105f48383836001611007565b5f828152600260205260408120546001600160a01b03908116908316156108a8576108a881848661115a565b6001600160a01b038116156108e2576108c35f855f5f611007565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615610910576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b6001600160a01b0382166109af57604051633250574960e11b81525f600482015260240161049a565b5f6109bb83835f61087c565b90506001600160a01b038116156105f4576040517f73c6ac6e0000000000000000000000000000000000000000000000000000000081525f600482015260240161049a565b6001600160a01b038216610a4b576040517f5b08ba180000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015260240161049a565b6001600160a01b038381165f8181526005602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001600160a01b0383163b15610516576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0384169063150b7a0290610b30903390889087908790600401611ece565b6020604051808303815f875af1925050508015610b6a575060408051601f3d908101601f19168201909252610b6791810190611f0e565b60015b610bd1573d808015610b97576040519150601f19603f3d011682016040523d82523d5f602084013e610b9c565b606091505b5080515f03610bc957604051633250574960e11b81526001600160a01b038516600482015260240161049a565b805181602001fd5b7fffffffff0000000000000000000000000000000000000000000000000000000081167f150b7a020000000000000000000000000000000000000000000000000000000014610c3e57604051633250574960e11b81526001600160a01b038516600482015260240161049a565b5050505050565b6060610c50836111f0565b610c62836001600160a01b031661128d565b610c6d85602061129f565b604051602001610c7f93929190611f29565b604051602081830303815290604052905092915050565b6060610caa826001600160a01b031661128d565b610cb3836114bf565b610cbc846115a1565b610d30610d22866001600160a01b03166381cce2456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cfe573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101eb9190611e9c565b6001600160a01b031661128d565b604051602001610d439493929190612084565b6040516020818303038152906040529050919050565b6060610d9b6040518060c001604052805f6001600160a01b031681526020015f61ffff1681526020015f81526020015f81526020015f81526020015f81525090565b6040517f61f029fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f000000000000000000000000000000000000000000000000000000000000000016906361f029fb9060240160c060405180830381865afa925050508015610e36575060408051601f3d908101601f19168201909252610e3391810190612232565b60015b15610e3e5790505b5f610e4884611680565b9050610e5482826116e6565b610e5d826111f0565b610e6a84604001516111f0565b610e788560a0015185611786565b8551610e8390611849565b8651610e97906001600160a01b031661128d565b604051602001610703969594939291906122a3565b606081515f03610ec957505060408051602081019091525f815290565b5f6040518060600160405280604081526020016126236040913990505f600384516002610ef6919061253b565b610f00919061254e565b610f0b90600461256d565b67ffffffffffffffff811115610f2357610f23611c67565b6040519080825280601f01601f191660200182016040528015610f4d576020820181803683370190505b509050600182016020820185865187016020810180515f82525b82841015610fc2576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610f67565b9052505085516003900660018114610fe15760028114610ff457610ffc565b603d6001830353603d6002830353610ffc565b603d60018303535b509195945050505050565b808061101b57506001600160a01b03821615155b15611113575f61102a8461081e565b90506001600160a01b038316158015906110565750826001600160a01b0316816001600160a01b031614155b801561108757506001600160a01b038082165f9081526005602090815260408083209387168352929052205460ff16155b156110c9576040517fa9fbf51f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161049a565b81156111115783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b61116583838361196f565b6105f4576001600160a01b0383166111ac576040517f7e2732890000000000000000000000000000000000000000000000000000000081526004810182905260240161049a565b6040517f177e802f0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161049a565b60605f6111fc836119ef565b60010190505f8167ffffffffffffffff81111561121b5761121b611c67565b6040519080825280601f01601f191660200182016040528015611245576020820181803683370190505b5090508181016020015b5f19017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461124f57509392505050565b60606103aa6001600160a01b03831660145b6060825f6112ae84600261256d565b6112b990600261253b565b67ffffffffffffffff8111156112d1576112d1611c67565b6040519080825280601f01601f1916602001820160405280156112fb576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f8151811061133157611331612584565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061139357611393612584565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f6113cd85600261256d565b6113d890600161253b565b90505b6001811115611474577f303132333435363738396162636465660000000000000000000000000000000083600f166010811061141957611419612584565b1a60f81b82828151811061142f5761142f612584565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a90535060049290921c9161146d81612598565b90506113db565b5081156114b7576040517fe22e27eb000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260440161049a565b949350505050565b6060816001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa92505050801561151e57506040513d5f823e601f3d908101601f1916820160405261151b91908101906125ad565b60015b61155b57505060408051808201909152600d81527f556e6b6e6f776e20546f6b656e00000000000000000000000000000000000000602082015290565b5f8151116103aa576040518060400160405280600d81526020017f556e6b6e6f776e20546f6b656e000000000000000000000000000000000000008152505b9392505050565b6060816001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa92505050801561160057506040513d5f823e601f3d908101601f191682016040526115fd91908101906125ad565b60015b61163d57505060408051808201909152600381527f3f3f3f0000000000000000000000000000000000000000000000000000000000602082015290565b5f8151116103aa576040518060400160405280600381526020017f3f3f3f000000000000000000000000000000000000000000000000000000000081525061159a565b5f816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156116db575060408051601f3d908101601f191682019092526116d891810190611e9c565b60015b6103aa57505f919050565b81516060906001600160a01b031615806116fe575081155b1561173d575060408051808201909152600181527f300000000000000000000000000000000000000000000000000000000000000060208201526103aa565b5f83606001518460800151611752919061253b565b90505f8361176883670de0b6b3a764000061256d565b611772919061254e565b905061177d816111f0565b95945050505050565b6060825f036117c9575060408051808201909152600981527f477261647561746564000000000000000000000000000000000000000000000060208201526103aa565b8282101561180c576040518060400160405280600681526020017f416374697665000000000000000000000000000000000000000000000000000081525061159a565b6040518060400160405280600981526020017f47726164756174656400000000000000000000000000000000000000000000008152509392505050565b60606001600160a01b03821661189257505060408051808201909152600481527f4e6f6e6500000000000000000000000000000000000000000000000000000000602082015290565b816001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa9250505080156118ef57506040513d5f823e601f3d908101601f191682016040526118ec91908101906125ad565b60015b61192c57505060408051808201909152600781527f556e6b6e6f776e00000000000000000000000000000000000000000000000000602082015290565b5f8151116103aa576040518060400160405280600781526020017f556e6b6e6f776e0000000000000000000000000000000000000000000000000081525061159a565b5f6001600160a01b038316158015906114b75750826001600160a01b0316846001600160a01b031614806119c757506001600160a01b038085165f9081526005602090815260408083209387168352929052205460ff165b806114b75750505f908152600460205260409020546001600160a01b03908116911614919050565b5f807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611a37577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310611a63576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310611a8157662386f26fc10000830492506010015b6305f5e1008310611a99576305f5e100830492506008015b6127108310611aad57612710830492506004015b60648310611abf576064830492506002015b600a83106103aa5760010192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114611afd575f5ffd5b50565b5f60208284031215611b10575f5ffd5b813561159a81611ad0565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61159a6020830184611b1b565b5f60208284031215611b6b575f5ffd5b5035919050565b6001600160a01b0381168114611afd575f5ffd5b5f5f60408385031215611b97575f5ffd5b8235611ba281611b72565b946020939093013593505050565b5f5f5f60608486031215611bc2575f5ffd5b8335611bcd81611b72565b92506020840135611bdd81611b72565b929592945050506040919091013590565b5f5f60408385031215611bff575f5ffd5b823591506020830135611c1181611b72565b809150509250929050565b5f60208284031215611c2c575f5ffd5b813561159a81611b72565b5f5f60408385031215611c48575f5ffd5b8235611c5381611b72565b915060208301358015158114611c11575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b60405160c0810167ffffffffffffffff81118282101715611c9e57611c9e611c67565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611ccd57611ccd611c67565b604052919050565b5f67ffffffffffffffff821115611cee57611cee611c67565b50601f01601f191660200190565b5f5f5f5f60808587031215611d0f575f5ffd5b8435611d1a81611b72565b93506020850135611d2a81611b72565b925060408501359150606085013567ffffffffffffffff811115611d4c575f5ffd5b8501601f81018713611d5c575f5ffd5b8035611d6f611d6a82611cd5565b611ca4565b818152886020838501011115611d83575f5ffd5b816020840160208301375f6020838301015280935050505092959194509250565b5f5f60408385031215611db5575f5ffd5b8235611dc081611b72565b91506020830135611c1181611b72565b600181811c90821680611de457607f821691505b602082108103611e0257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f81518060208401855e5f93019283525090919050565b5f611e3c611e36611e308488611e08565b86611e08565b84611e08565b7f5d7d000000000000000000000000000000000000000000000000000000000000815260020195945050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f61159a601d830184611e08565b5f60208284031215611eac575f5ffd5b5051919050565b5f60208284031215611ec3575f5ffd5b815161159a81611b72565b6001600160a01b03851681526001600160a01b0384166020820152826040820152608060608201525f611f046080830184611b1b565b9695505050505050565b5f60208284031215611f1e575f5ffd5b815161159a81611ad0565b7f7b226e616d65223a225052494e54522044657620506f736974696f6e2023000081525f611f5a601e830186611e08565b7f222c226465736372697074696f6e223a22446576656c6f70657220726967687481527f7320666f72205052494e54522074656c65636f696e220000000000000000000060208201527f2c2265787465726e616c5f75726c223a2268747470733a2f2f7072696e74722e60368201527f6d6f6e65792f746f6b656e2f00000000000000000000000000000000000000006056820152611ffc6062820186611e08565b90507f222c22696d616765223a2268747470733a2f2f63646e2e7072696e74722e6d6f81527f6e65792f6e66742f00000000000000000000000000000000000000000000000060208201526120546028820185611e08565b7f2e737667222c2261747472696275746573223a5b00000000000000000000000081526014019695505050505050565b7f7b2274726169745f74797065223a22546f6b656e2041646472657373222c227681526630b63ab2911d1160c91b60208201525f6120c56027830187611e08565b7f227d2c7b2274726169745f74797065223a22546f6b656e204e616d65222c227681526630b63ab2911d1160c91b60208201526121056027820187611e08565b90507f227d2c7b2274726169745f74797065223a22546f6b656e2053796d626f6c222c81527f2276616c7565223a220000000000000000000000000000000000000000000000602082015261215d6029820186611e08565b90507f227d2c7b2274726169745f74797065223a22446563696d616c73222c2276616c81527f7565223a223138227d000000000000000000000000000000000000000000000060208201527f2c7b2274726169745f74797065223a2243726561746f72222c2276616c75652260298201527f3a220000000000000000000000000000000000000000000000000000000000006049820152612201604b820185611e08565b7f227d0000000000000000000000000000000000000000000000000000000000008152600201979650505050505050565b5f60c0828403128015612243575f5ffd5b5061224c611c7b565b825161225781611b72565b8152602083015161ffff8116811461226d575f5ffd5b602082015260408381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b7f2c7b2274726169745f74797065223a2243757272656e74205072696365222c2281527f76616c7565223a2200000000000000000000000000000000000000000000000060208201525f6122fa6028830189611e08565b7f222c22646973706c61795f74797065223a226e756d626572227d00000000000081527f2c7b2274726169745f74797065223a22436861696e20537570706c79222c2276601a8201526630b63ab2911d1160c91b603a8201526123606041820189611e08565b90507f222c22646973706c61795f74797065223a226e756d626572227d2c7b2274726181527f69745f74797065223a22476c6f62616c20537570706c79222c2276616c75652260208201527f3a2200000000000000000000000000000000000000000000000000000000000060408201526123de6042820188611e08565b7f222c22646973706c61795f74797065223a226e756d626572227d2c7b2274726181527f69745f74797065223a22537461747573222c2276616c7565223a22000000000060208201529050603b81016125196124f06124ea61249b612495612446868d611e08565b7f227d2c7b2274726169745f74797065223a224261736520506169722053796d6281527f6f6c222c2276616c7565223a22000000000000000000000000000000000000006020820152602d0190565b8a611e08565b7f227d2c7b2274726169745f74797065223a22426173652050616972204164647281527f657373222c2276616c7565223a220000000000000000000000000000000000006020820152602e0190565b87611e08565b7f227d000000000000000000000000000000000000000000000000000000000000815260020190565b9a9950505050505050505050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156103aa576103aa612527565b5f8261256857634e487b7160e01b5f52601260045260245ffd5b500490565b80820281158282048414176103aa576103aa612527565b634e487b7160e01b5f52603260045260245ffd5b5f816125a6576125a6612527565b505f190190565b5f602082840312156125bd575f5ffd5b815167ffffffffffffffff8111156125d3575f5ffd5b8201601f810184136125e3575f5ffd5b80516125f1611d6a82611cd5565b818152856020838501011115612605575f5ffd5b8160208401602083015e5f9181016020019190915294935050505056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122004d128e854659d626dbea689e34ff98df0928a760c5392e4e75baef777ed989d64736f6c634300081b0033000000000000000000000000b77726291b125515d0a7affeea2b04f2ff243172

Deployed Bytecode

0x608060405234801561000f575f5ffd5b5060043610610115575f3560e01c806370a08231116100ad578063c87b56dd1161007d578063e985e9c511610063578063e985e9c514610257578063f5c6b96b14610292578063f7cca092146102a5575f5ffd5b8063c87b56dd14610231578063ca82a01414610244575f5ffd5b806370a08231146101f057806395d89b4114610203578063a22cb4651461020b578063b88d4fde1461021e575f5ffd5b806323b872dd116100e857806323b872dd14610196578063293c6a3a146101a957806342842e0e146101ca5780636352211e146101dd575f5ffd5b806301ffc9a71461011957806306fdde0314610141578063081812fc14610156578063095ea7b314610181575b5f5ffd5b61012c610127366004611b00565b6102cc565b60405190151581526020015b60405180910390f35b6101496103b0565b6040516101389190611b49565b610169610164366004611b5b565b61043f565b6040516001600160a01b039091168152602001610138565b61019461018f366004611b86565b610466565b005b6101946101a4366004611bb0565b610475565b6101bc6101b7366004611bee565b61051c565b604051908152602001610138565b6101946101d8366004611bb0565b6105da565b6101696101eb366004611b5b565b6105f9565b6101bc6101fe366004611c1c565b610603565b610149610661565b610194610219366004611c37565b610670565b61019461022c366004611cfc565b61067b565b61014961023f366004611b5b565b610692565b6101bc610252366004611c1c565b61071b565b61012c610265366004611da4565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b6101696102a0366004611b5b565b61077c565b6101697f000000000000000000000000b77726291b125515d0a7affeea2b04f2ff24317281565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061035e57507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b806103aa57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60605f80546103be90611dd0565b80601f01602080910402602001604051908101604052809291908181526020018280546103ea90611dd0565b80156104355780601f1061040c57610100808354040283529160200191610435565b820191905f5260205f20905b81548152906001019060200180831161041857829003601f168201915b5050505050905090565b5f6104498261081e565b505f828152600460205260409020546001600160a01b03166103aa565b61047182823361086f565b5050565b6001600160a01b0382166104a357604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f6104af83833361087c565b9050836001600160a01b0316816001600160a01b031614610516576040517f64283d7b0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152602482018490528216604482015260640161049a565b50505050565b5f336001600160a01b037f000000000000000000000000b77726291b125515d0a7affeea2b04f2ff243172161461057f576040517f878d41a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f8281526002602052604090205482906001600160a01b0316156105d0576040517f0f04799c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103aa8282610986565b6105f483838360405180602001604052805f81525061067b565b505050565b5f6103aa8261081e565b5f6001600160a01b038216610646576040517f89c62b640000000000000000000000000000000000000000000000000000000081525f600482015260240161049a565b506001600160a01b03165f9081526003602052604090205490565b6060600180546103be90611dd0565b610471338383610a00565b610686848484610475565b61051684848484610ad5565b606061069d8261081e565b505f6106a88361077c565b90505f6106b58483610c45565b6106be83610c96565b6106c784610d59565b6040516020016106d993929190611e1f565b60405160208183030381529060405290506106f381610eac565b6040516020016107039190611e6b565b60405160208183030381529060405292505050919050565b5f816001600160a01b03166381cce2456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610758573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190611e9c565b6040517fb12e4410000000000000000000000000000000000000000000000000000000008152600481018290525f907f000000000000000000000000b77726291b125515d0a7affeea2b04f2ff2431726001600160a01b03169063b12e441090602401602060405180830381865afa1580156107fa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190611eb3565b5f818152600260205260408120546001600160a01b0316806103aa576040517f7e2732890000000000000000000000000000000000000000000000000000000081526004810184905260240161049a565b6105f48383836001611007565b5f828152600260205260408120546001600160a01b03908116908316156108a8576108a881848661115a565b6001600160a01b038116156108e2576108c35f855f5f611007565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615610910576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b6001600160a01b0382166109af57604051633250574960e11b81525f600482015260240161049a565b5f6109bb83835f61087c565b90506001600160a01b038116156105f4576040517f73c6ac6e0000000000000000000000000000000000000000000000000000000081525f600482015260240161049a565b6001600160a01b038216610a4b576040517f5b08ba180000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015260240161049a565b6001600160a01b038381165f8181526005602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001600160a01b0383163b15610516576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0384169063150b7a0290610b30903390889087908790600401611ece565b6020604051808303815f875af1925050508015610b6a575060408051601f3d908101601f19168201909252610b6791810190611f0e565b60015b610bd1573d808015610b97576040519150601f19603f3d011682016040523d82523d5f602084013e610b9c565b606091505b5080515f03610bc957604051633250574960e11b81526001600160a01b038516600482015260240161049a565b805181602001fd5b7fffffffff0000000000000000000000000000000000000000000000000000000081167f150b7a020000000000000000000000000000000000000000000000000000000014610c3e57604051633250574960e11b81526001600160a01b038516600482015260240161049a565b5050505050565b6060610c50836111f0565b610c62836001600160a01b031661128d565b610c6d85602061129f565b604051602001610c7f93929190611f29565b604051602081830303815290604052905092915050565b6060610caa826001600160a01b031661128d565b610cb3836114bf565b610cbc846115a1565b610d30610d22866001600160a01b03166381cce2456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cfe573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101eb9190611e9c565b6001600160a01b031661128d565b604051602001610d439493929190612084565b6040516020818303038152906040529050919050565b6060610d9b6040518060c001604052805f6001600160a01b031681526020015f61ffff1681526020015f81526020015f81526020015f81526020015f81525090565b6040517f61f029fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f000000000000000000000000b77726291b125515d0a7affeea2b04f2ff24317216906361f029fb9060240160c060405180830381865afa925050508015610e36575060408051601f3d908101601f19168201909252610e3391810190612232565b60015b15610e3e5790505b5f610e4884611680565b9050610e5482826116e6565b610e5d826111f0565b610e6a84604001516111f0565b610e788560a0015185611786565b8551610e8390611849565b8651610e97906001600160a01b031661128d565b604051602001610703969594939291906122a3565b606081515f03610ec957505060408051602081019091525f815290565b5f6040518060600160405280604081526020016126236040913990505f600384516002610ef6919061253b565b610f00919061254e565b610f0b90600461256d565b67ffffffffffffffff811115610f2357610f23611c67565b6040519080825280601f01601f191660200182016040528015610f4d576020820181803683370190505b509050600182016020820185865187016020810180515f82525b82841015610fc2576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610f67565b9052505085516003900660018114610fe15760028114610ff457610ffc565b603d6001830353603d6002830353610ffc565b603d60018303535b509195945050505050565b808061101b57506001600160a01b03821615155b15611113575f61102a8461081e565b90506001600160a01b038316158015906110565750826001600160a01b0316816001600160a01b031614155b801561108757506001600160a01b038082165f9081526005602090815260408083209387168352929052205460ff16155b156110c9576040517fa9fbf51f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161049a565b81156111115783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b61116583838361196f565b6105f4576001600160a01b0383166111ac576040517f7e2732890000000000000000000000000000000000000000000000000000000081526004810182905260240161049a565b6040517f177e802f0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161049a565b60605f6111fc836119ef565b60010190505f8167ffffffffffffffff81111561121b5761121b611c67565b6040519080825280601f01601f191660200182016040528015611245576020820181803683370190505b5090508181016020015b5f19017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461124f57509392505050565b60606103aa6001600160a01b03831660145b6060825f6112ae84600261256d565b6112b990600261253b565b67ffffffffffffffff8111156112d1576112d1611c67565b6040519080825280601f01601f1916602001820160405280156112fb576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f8151811061133157611331612584565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061139357611393612584565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f6113cd85600261256d565b6113d890600161253b565b90505b6001811115611474577f303132333435363738396162636465660000000000000000000000000000000083600f166010811061141957611419612584565b1a60f81b82828151811061142f5761142f612584565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a90535060049290921c9161146d81612598565b90506113db565b5081156114b7576040517fe22e27eb000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260440161049a565b949350505050565b6060816001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa92505050801561151e57506040513d5f823e601f3d908101601f1916820160405261151b91908101906125ad565b60015b61155b57505060408051808201909152600d81527f556e6b6e6f776e20546f6b656e00000000000000000000000000000000000000602082015290565b5f8151116103aa576040518060400160405280600d81526020017f556e6b6e6f776e20546f6b656e000000000000000000000000000000000000008152505b9392505050565b6060816001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa92505050801561160057506040513d5f823e601f3d908101601f191682016040526115fd91908101906125ad565b60015b61163d57505060408051808201909152600381527f3f3f3f0000000000000000000000000000000000000000000000000000000000602082015290565b5f8151116103aa576040518060400160405280600381526020017f3f3f3f000000000000000000000000000000000000000000000000000000000081525061159a565b5f816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156116db575060408051601f3d908101601f191682019092526116d891810190611e9c565b60015b6103aa57505f919050565b81516060906001600160a01b031615806116fe575081155b1561173d575060408051808201909152600181527f300000000000000000000000000000000000000000000000000000000000000060208201526103aa565b5f83606001518460800151611752919061253b565b90505f8361176883670de0b6b3a764000061256d565b611772919061254e565b905061177d816111f0565b95945050505050565b6060825f036117c9575060408051808201909152600981527f477261647561746564000000000000000000000000000000000000000000000060208201526103aa565b8282101561180c576040518060400160405280600681526020017f416374697665000000000000000000000000000000000000000000000000000081525061159a565b6040518060400160405280600981526020017f47726164756174656400000000000000000000000000000000000000000000008152509392505050565b60606001600160a01b03821661189257505060408051808201909152600481527f4e6f6e6500000000000000000000000000000000000000000000000000000000602082015290565b816001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa9250505080156118ef57506040513d5f823e601f3d908101601f191682016040526118ec91908101906125ad565b60015b61192c57505060408051808201909152600781527f556e6b6e6f776e00000000000000000000000000000000000000000000000000602082015290565b5f8151116103aa576040518060400160405280600781526020017f556e6b6e6f776e0000000000000000000000000000000000000000000000000081525061159a565b5f6001600160a01b038316158015906114b75750826001600160a01b0316846001600160a01b031614806119c757506001600160a01b038085165f9081526005602090815260408083209387168352929052205460ff165b806114b75750505f908152600460205260409020546001600160a01b03908116911614919050565b5f807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611a37577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310611a63576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310611a8157662386f26fc10000830492506010015b6305f5e1008310611a99576305f5e100830492506008015b6127108310611aad57612710830492506004015b60648310611abf576064830492506002015b600a83106103aa5760010192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114611afd575f5ffd5b50565b5f60208284031215611b10575f5ffd5b813561159a81611ad0565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61159a6020830184611b1b565b5f60208284031215611b6b575f5ffd5b5035919050565b6001600160a01b0381168114611afd575f5ffd5b5f5f60408385031215611b97575f5ffd5b8235611ba281611b72565b946020939093013593505050565b5f5f5f60608486031215611bc2575f5ffd5b8335611bcd81611b72565b92506020840135611bdd81611b72565b929592945050506040919091013590565b5f5f60408385031215611bff575f5ffd5b823591506020830135611c1181611b72565b809150509250929050565b5f60208284031215611c2c575f5ffd5b813561159a81611b72565b5f5f60408385031215611c48575f5ffd5b8235611c5381611b72565b915060208301358015158114611c11575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b60405160c0810167ffffffffffffffff81118282101715611c9e57611c9e611c67565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611ccd57611ccd611c67565b604052919050565b5f67ffffffffffffffff821115611cee57611cee611c67565b50601f01601f191660200190565b5f5f5f5f60808587031215611d0f575f5ffd5b8435611d1a81611b72565b93506020850135611d2a81611b72565b925060408501359150606085013567ffffffffffffffff811115611d4c575f5ffd5b8501601f81018713611d5c575f5ffd5b8035611d6f611d6a82611cd5565b611ca4565b818152886020838501011115611d83575f5ffd5b816020840160208301375f6020838301015280935050505092959194509250565b5f5f60408385031215611db5575f5ffd5b8235611dc081611b72565b91506020830135611c1181611b72565b600181811c90821680611de457607f821691505b602082108103611e0257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f81518060208401855e5f93019283525090919050565b5f611e3c611e36611e308488611e08565b86611e08565b84611e08565b7f5d7d000000000000000000000000000000000000000000000000000000000000815260020195945050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f61159a601d830184611e08565b5f60208284031215611eac575f5ffd5b5051919050565b5f60208284031215611ec3575f5ffd5b815161159a81611b72565b6001600160a01b03851681526001600160a01b0384166020820152826040820152608060608201525f611f046080830184611b1b565b9695505050505050565b5f60208284031215611f1e575f5ffd5b815161159a81611ad0565b7f7b226e616d65223a225052494e54522044657620506f736974696f6e2023000081525f611f5a601e830186611e08565b7f222c226465736372697074696f6e223a22446576656c6f70657220726967687481527f7320666f72205052494e54522074656c65636f696e220000000000000000000060208201527f2c2265787465726e616c5f75726c223a2268747470733a2f2f7072696e74722e60368201527f6d6f6e65792f746f6b656e2f00000000000000000000000000000000000000006056820152611ffc6062820186611e08565b90507f222c22696d616765223a2268747470733a2f2f63646e2e7072696e74722e6d6f81527f6e65792f6e66742f00000000000000000000000000000000000000000000000060208201526120546028820185611e08565b7f2e737667222c2261747472696275746573223a5b00000000000000000000000081526014019695505050505050565b7f7b2274726169745f74797065223a22546f6b656e2041646472657373222c227681526630b63ab2911d1160c91b60208201525f6120c56027830187611e08565b7f227d2c7b2274726169745f74797065223a22546f6b656e204e616d65222c227681526630b63ab2911d1160c91b60208201526121056027820187611e08565b90507f227d2c7b2274726169745f74797065223a22546f6b656e2053796d626f6c222c81527f2276616c7565223a220000000000000000000000000000000000000000000000602082015261215d6029820186611e08565b90507f227d2c7b2274726169745f74797065223a22446563696d616c73222c2276616c81527f7565223a223138227d000000000000000000000000000000000000000000000060208201527f2c7b2274726169745f74797065223a2243726561746f72222c2276616c75652260298201527f3a220000000000000000000000000000000000000000000000000000000000006049820152612201604b820185611e08565b7f227d0000000000000000000000000000000000000000000000000000000000008152600201979650505050505050565b5f60c0828403128015612243575f5ffd5b5061224c611c7b565b825161225781611b72565b8152602083015161ffff8116811461226d575f5ffd5b602082015260408381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b7f2c7b2274726169745f74797065223a2243757272656e74205072696365222c2281527f76616c7565223a2200000000000000000000000000000000000000000000000060208201525f6122fa6028830189611e08565b7f222c22646973706c61795f74797065223a226e756d626572227d00000000000081527f2c7b2274726169745f74797065223a22436861696e20537570706c79222c2276601a8201526630b63ab2911d1160c91b603a8201526123606041820189611e08565b90507f222c22646973706c61795f74797065223a226e756d626572227d2c7b2274726181527f69745f74797065223a22476c6f62616c20537570706c79222c2276616c75652260208201527f3a2200000000000000000000000000000000000000000000000000000000000060408201526123de6042820188611e08565b7f222c22646973706c61795f74797065223a226e756d626572227d2c7b2274726181527f69745f74797065223a22537461747573222c2276616c7565223a22000000000060208201529050603b81016125196124f06124ea61249b612495612446868d611e08565b7f227d2c7b2274726169745f74797065223a224261736520506169722053796d6281527f6f6c222c2276616c7565223a22000000000000000000000000000000000000006020820152602d0190565b8a611e08565b7f227d2c7b2274726169745f74797065223a22426173652050616972204164647281527f657373222c2276616c7565223a220000000000000000000000000000000000006020820152602e0190565b87611e08565b7f227d000000000000000000000000000000000000000000000000000000000000815260020190565b9a9950505050505050505050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156103aa576103aa612527565b5f8261256857634e487b7160e01b5f52601260045260245ffd5b500490565b80820281158282048414176103aa576103aa612527565b634e487b7160e01b5f52603260045260245ffd5b5f816125a6576125a6612527565b505f190190565b5f602082840312156125bd575f5ffd5b815167ffffffffffffffff8111156125d3575f5ffd5b8201601f810184136125e3575f5ffd5b80516125f1611d6a82611cd5565b818152856020838501011115612605575f5ffd5b8160208401602083015e5f9181016020019190915294935050505056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122004d128e854659d626dbea689e34ff98df0928a760c5392e4e75baef777ed989d64736f6c634300081b0033

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

000000000000000000000000b77726291b125515d0a7affeea2b04f2ff243172

-----Decoded View---------------
Arg [0] : _printr (address): 0xb77726291b125515D0A7AfFeeA2b04f2FF243172

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b77726291b125515d0a7affeea2b04f2ff243172


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ 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.