Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 211 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Set Approval For... | 31231077 | 32 mins ago | IN | 0 FRAX | 0.00001226 | ||||
| Fuse Tokens | 31199380 | 18 hrs ago | IN | 0 FRAX | 0.00000747 | ||||
| Paid Mint | 31199372 | 18 hrs ago | IN | 0 FRAX | 0.00000721 | ||||
| Paid Mint | 31199324 | 18 hrs ago | IN | 0 FRAX | 0.00000788 | ||||
| Paid Mint | 31199283 | 18 hrs ago | IN | 0 FRAX | 0.00000741 | ||||
| Paid Mint | 31199258 | 18 hrs ago | IN | 0 FRAX | 0.00000814 | ||||
| Paid Mint | 31199127 | 18 hrs ago | IN | 0 FRAX | 0.00000817 | ||||
| Paid Mint | 31198820 | 18 hrs ago | IN | 0 FRAX | 0.00000566 | ||||
| Paid Mint | 31198766 | 18 hrs ago | IN | 0 FRAX | 0.00000497 | ||||
| Fuse Tokens | 31192867 | 21 hrs ago | IN | 0 FRAX | 0.00001384 | ||||
| Paid Mint | 31192856 | 21 hrs ago | IN | 0 FRAX | 0.00001318 | ||||
| Gift Mint | 31192660 | 21 hrs ago | IN | 0 FRAX | 0.0000186 | ||||
| Paid Mint | 31192477 | 21 hrs ago | IN | 0 FRAX | 0.000015 | ||||
| Paid Mint | 31192457 | 21 hrs ago | IN | 0 FRAX | 0.00001617 | ||||
| Paid Mint | 31192439 | 22 hrs ago | IN | 0 FRAX | 0.00001679 | ||||
| Fuse Tokens | 31191762 | 22 hrs ago | IN | 0 FRAX | 0.00001257 | ||||
| Paid Mint | 31191735 | 22 hrs ago | IN | 0 FRAX | 0.00001132 | ||||
| Paid Mint | 31191729 | 22 hrs ago | IN | 0 FRAX | 0.00001066 | ||||
| Paid Mint | 31191706 | 22 hrs ago | IN | 0 FRAX | 0.00000852 | ||||
| Paid Mint | 31191700 | 22 hrs ago | IN | 0 FRAX | 0.00000891 | ||||
| Paid Mint | 31191665 | 22 hrs ago | IN | 0 FRAX | 0.00000913 | ||||
| Paid Mint | 31191056 | 22 hrs ago | IN | 0 FRAX | 0.00001141 | ||||
| Paid Mint | 31190910 | 22 hrs ago | IN | 0 FRAX | 0.00001195 | ||||
| Gift Mint | 31190294 | 23 hrs ago | IN | 0 FRAX | 0.00002658 | ||||
| Paid Mint | 31190182 | 23 hrs ago | IN | 0 FRAX | 0.00001951 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Fraxiversary
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 800 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ========================= Fraxiversary ============================
* ====================================================================
* Fraxiversary NFT contract for the 5th anniversary of Frax Finance
* Frax Finance: https://github.com/FraxFinance
*/
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC721} from "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
import {ERC721Burnable} from "openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import {ERC721Enumerable} from "openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import {ERC721Pausable} from "openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pausable.sol";
import {ERC721URIStorage} from "openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import {IFraxiversaryErrors} from "./interfaces/IFraxiversaryErrors.sol";
import {IFraxiversaryEvents} from "./interfaces/IFraxiversaryEvents.sol";
import {IERC6454} from "./interfaces/IERC6454.sol";
import {IERC7590} from "./interfaces/IERC7590.sol";
import {IERC4906} from "openzeppelin-contracts/contracts/interfaces/IERC4906.sol";
import {ONFT721Core} from "@layerzerolabs/onft-evm/contracts/onft721/ONFT721Core.sol";
import {
IONFT721,
MessagingFee,
MessagingReceipt,
SendParam
} from "@layerzerolabs/onft-evm/contracts/onft721/interfaces/IONFT721.sol";
import {ONFT721MsgCodec} from "@layerzerolabs/onft-evm/contracts/onft721/libs/ONFT721MsgCodec.sol";
import {ONFTComposeMsgCodec} from "@layerzerolabs/onft-evm/contracts/libs/ONFTComposeMsgCodec.sol";
import {IOAppMsgInspector} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol";
import {Origin} from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
/**
* @title Fraxiversary
* @author Frax Finance
* @notice Fraxiversary is a composable ERC721 that tokenizes ERC20 deposits and supports cross-chain movement
* @dev The contract supports BASE mints funded by approved Frax ERC20s, GIFT mints funded by WFRAX,
* SOULBOUND mints for curated distribution, and FUSED tokens created from four BASE tokens
* @dev Minting for BASE and GIFT is time-boxed by mintingCutoffBlock which is calculated assuming Fraxtal 2s blocks
* @dev ERC20 deposits are accounted per tokenId and can only be withdrawn by burning the NFT
* @dev Soulbound restrictions are enforced via _update with a bridge-aware bypass used during ONFT operations
* @dev Frax Reviewer(s) / Contributor(s)
* Jan Turk: https://github.com/ThunderDeliverer
* Sam Kazemian: https://github.com/samkazemian
* Bjirke (honorary mention for the original idea)
*/
contract Fraxiversary is
ERC721,
ERC721Enumerable,
ERC721URIStorage,
ERC721Pausable,
ERC721Burnable,
IERC6454,
IERC7590,
IFraxiversaryErrors,
IFraxiversaryEvents,
ONFT721Core
{
using ONFT721MsgCodec for bytes;
using ONFT721MsgCodec for bytes32;
using SafeERC20 for IERC20;
/// @notice Canonical WFRAX address on Fraxtal used for gift mints and internal accounting
address public constant WFRAX_ADDRESS = 0xFc00000000000000000000000000000000000002;
/// @notice Maximum basis points value used for fee calculations
uint256 public constant MAX_BASIS_POINTS = 1e4;
/// @notice Stores base mint price for each supported ERC20
/// @dev erc20 ERC20 token address used for BASE mint payments
/// @dev price Price in ERC20 units excluding minting fee
mapping(address erc20 => uint256 price) public mintPrices;
/// @notice Stores the token URI assigned to BASE NFTs minted with a given ERC20
/// @dev erc20 ERC20 token address used for BASE mint payments
/// @dev uri Metadata URI applied to newly minted BASE tokens
mapping(address erc20 => string uri) public baseAssetTokenUris;
/// @notice Tracks fees collected per ERC20 from BASE and GIFT minting
/// @dev erc20 ERC20 token address used for mint payment
/// @dev fee Accumulated fee amount held by the contract
mapping(address erc20 => uint256 fee) public collectedFees;
/// @notice Stores supported ERC20 token addresses by index for enumeration
/// @dev index Position in the supported ERC20 list
/// @dev erc20 ERC20 token address at the stored index
mapping(uint256 index => address erc20) public supportedErc20s;
/// @notice Stores the underlying ERC20 asset addresses attached to each tokenId
/// @dev tokenId Fraxiversary token ID that holds underlying assets
/// @dev index Position of the underlying asset for the tokenId
/// @dev assets ERC20 token address stored at a given index for the tokenId
mapping(uint256 tokenId => address[] assets) public underlyingAssets;
/// @notice Stores the outbound ERC20 transfer nonce for each tokenId
/// @dev tokenId Fraxiversary token ID that holds underlying assets
/// @dev transferOutNonce Number of successful ERC20 withdrawals triggered by burn
mapping(uint256 tokenId => uint256 transferOutNonce) public transferOutNonces;
/// @notice Stores the internal ERC20 balance credited to each tokenId per ERC20
/// @dev tokenId Fraxiversary token ID that holds underlying assets
/// @dev erc20 ERC20 token address for which the internal balance is tracked
/// @dev balance Internal accounting balance for the ERC20 held by the tokenId
mapping(uint256 tokenId => mapping(address erc20 => uint256 balance)) public erc20Balances;
/// @notice Stores the four underlying BASE token IDs that make up a FUSED token
/// @dev tokenId FUSED token ID that references underlying BASE tokens
/// @dev index Position of the underlying BASE token ID for the FUSED token 0-3
/// @dev underlyingTokenId Underlying BASE token ID stored at the index for the tokenId
mapping(uint256 tokenId => mapping(uint256 index => uint256 underlyingTokenId)) public underlyingTokenIds;
/// @notice Marks whether a tokenId is non-transferable under IERC6454 rules
/// @dev tokenId Fraxiversary token ID to check
/// @dev nonTransferable True if the token is soulbound
mapping(uint256 tokenId => bool nonTransferable) public isNonTransferrable;
/// @notice Stores the TokenType classification for each tokenId
/// @dev tokenId Fraxiversary token ID to classify
/// @dev tokenType Current type for the tokenId
mapping(uint256 tokenId => TokenType tokenType) public tokenTypes;
/// @notice Next BASE tokenId to be minted
uint256 public nextTokenId;
/// @notice Next GIFT tokenId to be minted
uint256 public nextGiftTokenId;
/// @notice Next premium tokenId to be minted for FUSED or SOULBOUND tokens
uint256 public nextPremiumTokenId;
/// @notice Number of ERC20 tokens currently supported for BASE minting
uint256 public totalNumberOfSupportedErc20s;
/// @notice Maximum number of BASE tokens that can be minted
uint256 public mintingLimit;
/// @notice Maximum number of GIFT tokens that can be minted
uint256 public giftMintingLimit;
/// @notice Base price for a single GIFT mint denominated in WFRAX
uint256 public giftMintingPrice;
/// @notice Minting fee in basis points applied to BASE and GIFT mint prices
uint256 public mintingFeeBasisPoints;
/// @notice Block number after which BASE and GIFT minting is disabled
uint256 public mintingCutoffBlock;
/// @dev Flag that disables soulbound checks during bridge operations
bool private _isBridgeOperation;
/**
* @notice TokenType describes the classification and rules for each minted token
* @dev NONEXISTENT is used after burn and for unminted token IDs
* @dev BASE tokens are backed by a single ERC20 deposit
* @dev FUSED tokens are backed by four distinct BASE tokens
* @dev SOULBOUND tokens are non-transferable and may represent curated rewards
* @dev GIFT tokens are backed by WFRAX and have a separate supply and price
*/
enum TokenType {
NONEXISTENT, // 0 - Token does not exist
BASE, // 1 - NFTs that are minted using ERC20 tokens
FUSED, // 2 - NFTs that are created by combining multiple base NFTs
SOULBOUND, // 3 - NFTs that are non-transferable and tied to a specific user
GIFT // 4 - NFTs that are gifted and have a separate minting limit and separate mint prices
}
/// @dev Default metadata URI for GIFT tokens
string private giftTokenUri;
/// @dev Default metadata URI for FUSED tokens
string private premiumTokenUri;
/**
* @notice Initializes Fraxiversary with supply caps, fee settings, and ONFT configuration
* @dev The mintingCutoffBlock is calculated assuming a fixed 2 second Fraxtal block time
* @dev nextGiftTokenId starts immediately after the BASE tokenId range
* @dev nextPremiumTokenId starts immediately after the GIFT tokenId range
* @param _initialOwner Address that will own the contract and control admin functions
* @param _lzEndpoint LayerZero endpoint used by ONFT721Core
*/
constructor(address _initialOwner, address _lzEndpoint)
ERC721("Fraxiversary", "FRAX5Y")
ONFT721Core(_lzEndpoint, _initialOwner)
{
mintingLimit = 12_000;
giftMintingLimit = 50_000;
giftMintingPrice = 50 * 1e18; // 50 WFRAX
nextGiftTokenId = mintingLimit;
nextPremiumTokenId = mintingLimit + giftMintingLimit;
mintingFeeBasisPoints = 25; // 0.25%
mintingCutoffBlock = block.number + (35 days / 2 seconds); // Approximately 5 weeks with 2s blocktime
giftTokenUri = "https://arweave.net/gv2ghexT4l3LVsheyz1MCO86yZnTFeFo__G6guMq7OE";
premiumTokenUri = "https://arweave.net/-pyCwZBiSn498egF8gfqrCJz4gmdVC0XxrN8wO9DuWQ";
}
/**
* @notice Returns the base URI for token metadata resolution
* @dev The contract relies on per-token URIs for all token types
* @return Empty base URI string
*/
function _baseURI() internal pure override returns (string memory) {
return "";
}
/**
* @notice Pauses all transfers and minting that rely on ERC721Pausable checks
* @dev Only the contract owner can pause the contract
*/
function pause() public onlyOwner {
_pause();
}
/**
* @notice Unpauses the contract so transfers and minting can resume
* @dev Only the contract owner can unpause the contract
*/
function unpause() public onlyOwner {
_unpause();
}
/**
* @notice Mints a BASE token by transferring the configured ERC20 price plus fee into the contract
* @dev Reverts if the minting period is over or the BASE minting limit is reached
* @dev The ERC20 must be supported via updateBaseAssetMintPrice with a non-zero price
* @dev The deposited amount excluding fee is credited to the new tokenId internal balance
* @param _erc20Contract ERC20 token address used to pay for the mint
* @return Newly minted BASE token ID
*/
function paidMint(address _erc20Contract) public returns (uint256) {
if (block.number > mintingCutoffBlock) revert MintingPeriodOver();
if (nextTokenId >= mintingLimit) revert MintingLimitReached();
if (mintPrices[_erc20Contract] == 0) revert UnsupportedToken();
uint256 tokenId = nextTokenId++;
_transferERC20ToToken(_erc20Contract, tokenId, msg.sender);
// Update underlying assets with the asset being used when minting
underlyingAssets[tokenId].push(_erc20Contract);
// Set the token type to BASE
tokenTypes[tokenId] = TokenType.BASE;
_safeMint(msg.sender, tokenId);
_setTokenURI(tokenId, baseAssetTokenUris[_erc20Contract]);
return tokenId;
}
/**
* @notice Mints a GIFT token to a recipient by paying the configured WFRAX price plus fee
* @dev Reverts if the minting period is over or the GIFT minting limit is reached
* @dev The caller pays the giftMintingPrice while the recipient receives the NFT
* @param _recipient Address that will receive the newly minted GIFT NFT
* @return Newly minted GIFT token ID
*/
function giftMint(address _recipient) public returns (uint256) {
if (block.number > mintingCutoffBlock) revert MintingPeriodOver();
if (nextGiftTokenId >= mintingLimit + giftMintingLimit) revert GiftMintingLimitReached();
uint256 tokenId = nextGiftTokenId;
nextGiftTokenId += 1;
_transferERC20ToToken(WFRAX_ADDRESS, tokenId, msg.sender, giftMintingPrice);
// Update underlying assets with the asset being used when minting
underlyingAssets[tokenId].push(WFRAX_ADDRESS);
// Set the token type to GIFT
tokenTypes[tokenId] = TokenType.GIFT;
_safeMint(_recipient, tokenId);
_setTokenURI(tokenId, giftTokenUri);
emit GiftMinted(msg.sender, _recipient, tokenId, giftMintingPrice);
return tokenId;
}
/**
* @notice Mints a SOULBOUND token to a recipient with a custom token URI
* @dev Only the contract owner can mint SOULBOUND tokens
* @dev Soulbound tokens are marked non-transferable and are excluded from underlying balance views
* @param _recipient Address that will receive the soulbound NFT
* @param _tokenUri Metadata URI assigned to the soulbound NFT
* @return Newly minted SOULBOUND token ID
*/
function soulboundMint(address _recipient, string memory _tokenUri) public onlyOwner returns (uint256) {
uint256 tokenId = nextPremiumTokenId;
_safeMint(_recipient, tokenId);
_setTokenURI(tokenId, _tokenUri);
isNonTransferrable[tokenId] = true;
tokenTypes[tokenId] = TokenType.SOULBOUND;
nextPremiumTokenId += 1;
emit NewSoulboundToken(_recipient, tokenId);
return tokenId;
}
/**
* @notice Burns a token and returns all held ERC20 balances to the token owner
* @dev Only the current token owner can burn
* @dev FUSED tokens must be unfused before they can be burned
* @dev ERC20 transfers are performed per underlying asset recorded for the tokenId
* @param _tokenId Token ID to burn and redeem underlying ERC20 balances from
*/
function burn(uint256 _tokenId) public override(ERC721Burnable) {
if (msg.sender != ownerOf(_tokenId)) revert OnlyTokenOwnerCanBurnTheToken();
if (tokenTypes[_tokenId] == TokenType.FUSED) revert UnfuseTokenBeforeBurning();
// First retrieve and delete the list of underlying assets
address[] memory assets = underlyingAssets[_tokenId];
delete underlyingAssets[_tokenId];
// Transfer out the held ERC20 and then burn the NFT
for (uint256 i; i < assets.length;) {
_transferHeldERC20FromToken(assets[i], _tokenId, msg.sender, erc20Balances[_tokenId][assets[i]]);
unchecked {
++i;
}
}
super.burn(_tokenId);
tokenTypes[_tokenId] = TokenType.NONEXISTENT;
}
/**
* @notice Sets the metadata URI used for BASE tokens minted with a specified ERC20
* @dev Only the contract owner can set base asset URIs
* @param _erc20Contract ERC20 token address whose BASE mint URI will be updated
* @param _uri New metadata URI for BASE tokens minted with the ERC20
*/
function setBaseAssetTokenUri(address _erc20Contract, string memory _uri) public onlyOwner {
baseAssetTokenUris[_erc20Contract] = _uri;
}
/**
* @notice Sets the default metadata URI used for all GIFT tokens
* @dev Only the contract owner can set the GIFT token URI
* @param _uri New default metadata URI for GIFT tokens
*/
function setGiftTokenUri(string memory _uri) public onlyOwner {
giftTokenUri = _uri;
}
/**
* @notice Sets the default metadata URI used for all FUSED tokens
* @dev Only the contract owner can set the premium token URI
* @param _uri New default metadata URI for FUSED tokens
*/
function setPremiumTokenUri(string memory _uri) public onlyOwner {
premiumTokenUri = _uri;
}
/**
* @notice Refreshes BASE token URIs for an inclusive tokenId range
* @dev Only the contract owner can refresh BASE token URIs
* @dev The function reads the underlying asset at index 0 and updates the token URI if balance is non-zero
* @param _firstTokenId First tokenId in the range to update
* @param _lastTokenId Last tokenId in the range to update
*/
function refreshBaseTokenUris(uint256 _firstTokenId, uint256 _lastTokenId) public onlyOwner {
if (_lastTokenId < _firstTokenId) revert InvalidRange();
if (_lastTokenId >= nextTokenId) revert OutOfBounds();
for (uint256 tokenId = _firstTokenId; tokenId <= _lastTokenId;) {
if (tokenTypes[tokenId] != TokenType.BASE) {
unchecked {
++tokenId;
}
continue;
}
address underlyingAsset = underlyingAssets[tokenId][0];
// Only update if there is an underlying asset (if the token exists)
if (underlyingAsset != address(0) && erc20Balances[tokenId][underlyingAsset] > 0) {
_setTokenURI(tokenId, baseAssetTokenUris[underlyingAsset]);
}
unchecked {
++tokenId;
}
}
emit BatchMetadataUpdate(_firstTokenId, _lastTokenId);
}
/**
* @notice Refreshes GIFT token URIs for an inclusive tokenId range
* @dev Only the contract owner can refresh GIFT token URIs
* @dev The range must start within the GIFT tokenId space and end below nextGiftTokenId
* @param _firstTokenId First tokenId in the GIFT range to update
* @param _lastTokenId Last tokenId in the GIFT range to update
*/
function refreshGiftTokenUris(uint256 _firstTokenId, uint256 _lastTokenId) public onlyOwner {
if (_firstTokenId < mintingLimit) revert OutOfBounds();
if (_lastTokenId < _firstTokenId) revert InvalidRange();
if (_lastTokenId >= nextGiftTokenId) revert OutOfBounds();
for (uint256 tokenId = _firstTokenId; tokenId <= _lastTokenId;) {
// Only update if there is an underlying asset (if the token exists)
if (erc20Balances[tokenId][WFRAX_ADDRESS] > 0) {
_setTokenURI(tokenId, giftTokenUri);
}
unchecked {
++tokenId;
}
}
emit BatchMetadataUpdate(_firstTokenId, _lastTokenId);
}
/**
* @notice Refreshes premium token URIs for an inclusive tokenId range
* @dev Only the contract owner can refresh premium token URIs
* @dev Only FUSED tokens are updated as they hold underlyingTokenIds references
* @param _firstTokenId First tokenId in the premium range to update
* @param _lastTokenId Last tokenId in the premium range to update
*/
function refreshPremiumTokenUris(uint256 _firstTokenId, uint256 _lastTokenId) public onlyOwner {
if (_lastTokenId < _firstTokenId) revert InvalidRange();
if (_firstTokenId < mintingLimit + giftMintingLimit) revert OutOfBounds();
if (_lastTokenId >= nextPremiumTokenId) revert OutOfBounds();
for (uint256 tokenId = _firstTokenId; tokenId <= _lastTokenId;) {
// Only update if the token has underlying tokens (this ensures that the token exists and that it isn't soulbound)
if (underlyingTokenIds[tokenId][0] != 0 || underlyingTokenIds[tokenId][1] != 0) {
// This is in case the first underlying token ID is 0
_setTokenURI(tokenId, premiumTokenUri);
}
unchecked {
++tokenId;
}
}
emit BatchMetadataUpdate(_firstTokenId, _lastTokenId);
}
/**
* @notice Updates the metadata URI for a specific existing token
* @dev Only the contract owner can update token URIs
* @dev Reverts if the token has been burned or never existed
* @param _tokenId Token ID whose metadata URI will be updated
* @param _uri New metadata URI for the _tokenId
*/
function updateSpecificTokenUri(uint256 _tokenId, string memory _uri) public onlyOwner {
if (tokenTypes[_tokenId] == TokenType.NONEXISTENT) revert TokenDoesNotExist();
_setTokenURI(_tokenId, _uri);
}
/**
* @notice Adds, updates, or removes an ERC20 from the BASE mint allowlist by setting its mint price
* @dev Setting a non-zero price adds the token to supportedErc20s if it was not previously supported
* @dev Setting the price to zero removes the token from supportedErc20s
* @dev The function emits MintPriceUpdated for all price changes
* @param _erc20Contract ERC20 token address to update
* @param _mintPrice New BASE mint price for the ERC20
*/
function updateBaseAssetMintPrice(address _erc20Contract, uint256 _mintPrice) public onlyOwner {
uint256 previousMintPrice = mintPrices[_erc20Contract];
if (previousMintPrice == _mintPrice) revert AttemptingToSetExistingMintPrice();
mintPrices[_erc20Contract] = _mintPrice;
if (previousMintPrice == 0) {
supportedErc20s[totalNumberOfSupportedErc20s] = _erc20Contract;
totalNumberOfSupportedErc20s += 1;
}
if (_mintPrice == 0) {
uint256 erc20Index = type(uint256).max;
for (uint256 i; i < totalNumberOfSupportedErc20s;) {
if (supportedErc20s[i] == _erc20Contract) {
erc20Index = i;
break;
}
unchecked {
++i;
}
}
// This shouldn't even be reachable, but it is defensive guard in case the guard at the beginning of the
// function doesn't catch an unsupported token
if (erc20Index == type(uint256).max) revert UnsupportedToken();
supportedErc20s[erc20Index] = supportedErc20s[totalNumberOfSupportedErc20s - 1];
totalNumberOfSupportedErc20s -= 1;
supportedErc20s[totalNumberOfSupportedErc20s] = address(0);
}
emit MintPriceUpdated(_erc20Contract, previousMintPrice, _mintPrice);
}
/**
* @notice Updates the base WFRAX price used for GIFT minting
* @dev Only the contract owner can update the GIFT mint price
* @dev The price must be greater than 1 WFRAX
* @param _newPrice New GIFT mint price denominated in WFRAX
*/
function updateGiftMintingPrice(uint256 _newPrice) public onlyOwner {
if (_newPrice <= 1e18) revert InvalidGiftMintPrice(); // Minimum 1 WFRAX
uint256 previousPrice = giftMintingPrice;
if (previousPrice == _newPrice) revert AttemptingToSetExistingMintPrice();
giftMintingPrice = _newPrice;
emit GiftMintPriceUpdated(previousPrice, _newPrice);
}
/**
* @notice Updates the minting fee charged on top of BASE and GIFT prices
* @dev Only the contract owner can update fee basis points
* @dev The new value must not exceed MAX_BASIS_POINTS
* @param _newFeeBasisPoints New fee value expressed in basis points
*/
function updateMintingFeeBasisPoints(uint256 _newFeeBasisPoints) public onlyOwner {
if (_newFeeBasisPoints > MAX_BASIS_POINTS) revert OutOfBounds();
uint256 previousFeeBasisPoints = mintingFeeBasisPoints;
mintingFeeBasisPoints = _newFeeBasisPoints;
emit MintingFeeUpdated(previousFeeBasisPoints, _newFeeBasisPoints);
}
/**
* @notice Updates the block cutoff after which BASE and GIFT minting is disabled
* @dev Only the contract owner can update the cutoff block
* @param _newCutoffBlock New block number used as the minting cutoff
*/
function updateMintingCutoffBlock(uint256 _newCutoffBlock) public onlyOwner {
uint256 previousCutoffBlock = mintingCutoffBlock;
mintingCutoffBlock = _newCutoffBlock;
emit MintingCutoffBlockUpdated(previousCutoffBlock, _newCutoffBlock);
}
/**
* @notice Transfers accumulated minting fees for a given ERC20 to a recipient
* @dev Only the contract owner can retrieve collected fees
* @dev No action is taken if the recorded fee amount is zero
* @param _erc20Contract ERC20 token address whose fees will be retrieved
* @param _to Address that will receive the collected fees
*/
function retrieveCollectedFees(address _erc20Contract, address _to) public onlyOwner {
uint256 feeAmount = collectedFees[_erc20Contract];
if (feeAmount == 0) return;
collectedFees[_erc20Contract] = 0;
if (!IERC20(_erc20Contract).trySafeTransfer(_to, feeAmount)) revert TransferFailed();
emit FeesRetrieved(_erc20Contract, _to, feeAmount);
}
/**
* @notice Returns the BASE mint price, fee, and total price for a given ERC20
* @dev The returned fee uses mintingFeeBasisPoints and MAX_BASIS_POINTS
* @param _erc20Contract ERC20 token address to quote
* @return mintPrice_ Base price excluding fee
* @return fee_ Fee amount added on top of the base price
* @return totalPrice_ Sum of mintPrice_ and fee_
*/
function getMintingPriceWithFee(address _erc20Contract)
public
view
returns (uint256 mintPrice_, uint256 fee_, uint256 totalPrice_)
{
mintPrice_ = mintPrices[_erc20Contract];
fee_ = (mintPrice_ * mintingFeeBasisPoints) / MAX_BASIS_POINTS;
totalPrice_ = mintPrice_ + fee_;
}
/**
* @notice Returns the GIFT mint price, fee, and total price for WFRAX
* @dev The returned fee uses mintingFeeBasisPoints and MAX_BASIS_POINTS
* @return mintPrice_ Base GIFT price excluding fee
* @return fee_ Fee amount added on top of the base price
* @return totalPrice_ Sum of mintPrice_ and fee_
*/
function getGiftMintingPriceWithFee() public view returns (uint256 mintPrice_, uint256 fee_, uint256 totalPrice_) {
mintPrice_ = giftMintingPrice;
fee_ = (mintPrice_ * mintingFeeBasisPoints) / MAX_BASIS_POINTS;
totalPrice_ = mintPrice_ + fee_;
}
/**
* @notice Returns the four underlying BASE token IDs referenced by a premium token
* @dev The values are meaningful for FUSED tokens and are zeros for other token types
* @param _premiumTokenId Token ID expected to be a FUSED token
* @return tokenId1_ Underlying BASE token ID stored at index 0
* @return tokenId2_ Underlying BASE token ID stored at index 1
* @return tokenId3_ Underlying BASE token ID stored at index 2
* @return tokenId4_ Underlying BASE token ID stored at index 3
*/
function getUnderlyingTokenIds(uint256 _premiumTokenId)
external
view
returns (uint256 tokenId1_, uint256 tokenId2_, uint256 tokenId3_, uint256 tokenId4_)
{
return (
underlyingTokenIds[_premiumTokenId][0],
underlyingTokenIds[_premiumTokenId][1],
underlyingTokenIds[_premiumTokenId][2],
underlyingTokenIds[_premiumTokenId][3]
);
}
/**
* @notice Returns underlying ERC20 contracts and balances associated with a tokenId
* @dev Reverts if the tokenId is marked as NONEXISTENT
* @dev SOULBOUND tokens return empty arrays as they do not represent ERC20 deposits
* @dev BASE and GIFT tokens return a single ERC20 entry
* @dev FUSED tokens return four entries derived from their underlying BASE tokens
* @param _tokenId Token ID to query
* @return erc20Contracts_ Array of ERC20 token addresses
* @return balances_ Array of internal balances_ corresponding to erc20Contracts_
*/
function getUnderlyingBalances(uint256 _tokenId)
external
view
returns (address[] memory erc20Contracts_, uint256[] memory balances_)
{
if (tokenTypes[_tokenId] == TokenType.NONEXISTENT) revert TokenDoesNotExist();
if (tokenTypes[_tokenId] == TokenType.SOULBOUND) return (new address[](0), new uint256[](0));
if (tokenTypes[_tokenId] == TokenType.BASE || tokenTypes[_tokenId] == TokenType.GIFT) {
erc20Contracts_ = new address[](1);
balances_ = new uint256[](1);
address erc20Contract = underlyingAssets[_tokenId][0];
erc20Contracts_[0] = erc20Contract;
balances_[0] = erc20Balances[_tokenId][erc20Contract];
return (erc20Contracts_, balances_);
}
erc20Contracts_ = new address[](4);
balances_ = new uint256[](4);
for (uint256 i; i < 4;) {
uint256 underlyingTokenId = underlyingTokenIds[_tokenId][i];
address erc20Contract = underlyingAssets[underlyingTokenId][0];
erc20Contracts_[i] = erc20Contract;
balances_[i] = erc20Balances[underlyingTokenId][erc20Contract];
unchecked {
++i;
}
}
return (erc20Contracts_, balances_);
}
/**
* @notice Returns the full list of supported ERC20s and their BASE mint prices
* @dev The order corresponds to supportedErc20s indexes and may change when tokens are removed
* @return erc20Contracts_ Array of supported ERC20 token addresses
* @return mintPricesOut_ Array of mint prices aligned with erc20Contracts_
*/
function getSupportedErc20s()
external
view
returns (address[] memory erc20Contracts_, uint256[] memory mintPricesOut_)
{
erc20Contracts_ = new address[](totalNumberOfSupportedErc20s);
mintPricesOut_ = new uint256[](totalNumberOfSupportedErc20s);
for (uint256 i; i < totalNumberOfSupportedErc20s;) {
address erc20Contract = supportedErc20s[i];
erc20Contracts_[i] = erc20Contract;
mintPricesOut_[i] = mintPrices[erc20Contract];
unchecked {
++i;
}
}
return (erc20Contracts_, mintPricesOut_);
}
/**
* @notice Returns whether a tokenId is transferable under IERC6454 semantics
* @dev This is a lightweight view that reflects the isNonTransferrable flag
* @dev This function preserves the interface of IERC6454 even though _from and _to are unused
* @param _tokenId Token ID to check
* @param _from Current owner address provided for interface compliance
* @param _to Intended recipient address provided for interface compliance
* @return True if the token is not marked as non-transferable
*/
function isTransferable(uint256 _tokenId, address _from, address _to) public view override returns (bool) {
return !isNonTransferrable[_tokenId];
}
/**
* @notice Returns the internal balance of an ERC20 held by a given tokenId
* @dev This value reflects contract-side accounting rather than direct ERC20 balances
* @param _erc20Contract ERC20 token address to query
* @param _tokenId Token ID whose internal balance will be returned
* @return Internal accounting balance for the ERC20 and _tokenId
*/
function balanceOfERC20(address _erc20Contract, uint256 _tokenId) external view override returns (uint256) {
return erc20Balances[_tokenId][_erc20Contract];
}
/**
* @notice Disallows arbitrary ERC20 withdrawals from tokenIds
* @dev ERC20 retrieval is only supported via burn to preserve accounting invariants
* @param _erc20Contract ERC20 token address requested for withdrawal
* @param _tokenId Token ID requested for withdrawal
* @param _to Recipient address requested for withdrawal
* @param _amount Amount requested for withdrawal
* @param _data Unused calldata parameter for interface compliance
*/
function transferHeldERC20FromToken(
address _erc20Contract,
uint256 _tokenId,
address _to,
uint256 _amount,
bytes memory _data
) external pure override {
revert TokensCanOnlyBeRetrievedByNftBurn();
}
/**
* @notice Disallows arbitrary ERC20 deposits into tokenIds after mint
* @dev ERC20 deposits are only supported during minting flows
* @param _erc20Contract ERC20 token address requested for deposit
* @param _tokenId Token ID requested for deposit
* @param _amount Amount requested for deposit
* @param _data Unused calldata parameter for interface compliance
*/
function transferERC20ToToken(address _erc20Contract, uint256 _tokenId, uint256 _amount, bytes memory _data)
external
pure
override
{
revert TokensCanOnlyBeDepositedByNftMint();
}
/**
* @notice Returns the number of ERC20 transfers executed out of a tokenId
* @dev The nonce increments for each underlying asset transferred during burn
* @param _tokenId Token ID to query
* @return Current outbound transfer nonce value
*/
function erc20TransferOutNonce(uint256 _tokenId) external view override returns (uint256) {
return transferOutNonces[_tokenId];
}
/**
* @notice Fuses four BASE tokens into a single FUSED premium token
* @dev The caller must own all four tokens and each must be of type BASE
* @dev All four BASE tokens must have distinct underlying ERC20 assets
* @dev The BASE tokens are transferred into the contract as custody for the FUSED token
* @param _tokenId1 First BASE token ID to fuse
* @param _tokenId2 Second BASE token ID to fuse
* @param _tokenId3 Third BASE token ID to fuse
* @param _tokenId4 Fourth BASE token ID to fuse
* @return premiumTokenId_ Newly minted FUSED token ID
*/
function fuseTokens(uint256 _tokenId1, uint256 _tokenId2, uint256 _tokenId3, uint256 _tokenId4)
public
returns (uint256 premiumTokenId_)
{
if (
ownerOf(_tokenId1) != msg.sender || ownerOf(_tokenId2) != msg.sender || ownerOf(_tokenId3) != msg.sender
|| ownerOf(_tokenId4) != msg.sender
) revert OnlyTokenOwnerCanFuseTokens();
if (
tokenTypes[_tokenId1] != TokenType.BASE || tokenTypes[_tokenId2] != TokenType.BASE
|| tokenTypes[_tokenId3] != TokenType.BASE || tokenTypes[_tokenId4] != TokenType.BASE
) revert CanOnlyFuseBaseTokens();
if (
underlyingAssets[_tokenId1][0] == underlyingAssets[_tokenId2][0]
|| underlyingAssets[_tokenId1][0] == underlyingAssets[_tokenId3][0]
|| underlyingAssets[_tokenId1][0] == underlyingAssets[_tokenId4][0]
|| underlyingAssets[_tokenId2][0] == underlyingAssets[_tokenId3][0]
|| underlyingAssets[_tokenId2][0] == underlyingAssets[_tokenId4][0]
|| underlyingAssets[_tokenId3][0] == underlyingAssets[_tokenId4][0]
) revert SameTokenUnderlyingAssets();
premiumTokenId_ = nextPremiumTokenId;
_update(address(this), _tokenId1, msg.sender);
_update(address(this), _tokenId2, msg.sender);
_update(address(this), _tokenId3, msg.sender);
_update(address(this), _tokenId4, msg.sender);
_safeMint(msg.sender, premiumTokenId_);
_setTokenURI(premiumTokenId_, premiumTokenUri);
// Assign the fused tokens to the premium token, so they can be extracted when unfusing them
underlyingTokenIds[premiumTokenId_][0] = _tokenId1;
underlyingTokenIds[premiumTokenId_][1] = _tokenId2;
underlyingTokenIds[premiumTokenId_][2] = _tokenId3;
underlyingTokenIds[premiumTokenId_][3] = _tokenId4;
tokenTypes[premiumTokenId_] = TokenType.FUSED;
nextPremiumTokenId += 1;
emit TokenFused(msg.sender, _tokenId1, _tokenId2, _tokenId3, _tokenId4, premiumTokenId_);
}
/**
* @notice Burns a FUSED token and returns its four underlying BASE tokens to the owner
* @dev The caller must own the premium token and it must be of type FUSED
* @dev Underlying token references are cleared after successful unfuse
* @param _premiumTokenId FUSED token ID to unfuse
* @return tokenId1_ First underlying BASE token ID returned
* @return tokenId2_ Second underlying BASE token ID returned
* @return tokenId3_ Third underlying BASE token ID returned
* @return tokenId4_ Fourth underlying BASE token ID returned
*/
function unfuseTokens(uint256 _premiumTokenId)
public
returns (uint256 tokenId1_, uint256 tokenId2_, uint256 tokenId3_, uint256 tokenId4_)
{
if (ownerOf(_premiumTokenId) != msg.sender) {
revert OnlyTokenOwnerCanUnfuseTokens();
}
if (tokenTypes[_premiumTokenId] != TokenType.FUSED) revert CanOnlyUnfuseFusedTokens();
tokenId1_ = underlyingTokenIds[_premiumTokenId][0];
tokenId2_ = underlyingTokenIds[_premiumTokenId][1];
tokenId3_ = underlyingTokenIds[_premiumTokenId][2];
tokenId4_ = underlyingTokenIds[_premiumTokenId][3];
_burn(_premiumTokenId);
tokenTypes[_premiumTokenId] = TokenType.NONEXISTENT;
underlyingTokenIds[_premiumTokenId][0] = 0;
underlyingTokenIds[_premiumTokenId][1] = 0;
underlyingTokenIds[_premiumTokenId][2] = 0;
underlyingTokenIds[_premiumTokenId][3] = 0;
_update(msg.sender, tokenId1_, address(this));
_update(msg.sender, tokenId2_, address(this));
_update(msg.sender, tokenId3_, address(this));
_update(msg.sender, tokenId4_, address(this));
emit TokenUnfused(msg.sender, tokenId1_, tokenId2_, tokenId3_, tokenId4_, _premiumTokenId);
}
// ********** ONFT functional overrides **********
/**
* @notice Returns the token address used by the ONFT interface
* @dev For ONFT721 this must be the address of the NFT contract itself
* @return Address of this contract
*/
function token() external view override returns (address) {
return address(this);
}
/**
* @notice Indicates whether explicit approvals are required for bridging operations
* @dev The contract opts into a no-approval bridging model in ONFT721Core
* @return False indicating approvals are not required
*/
function approvalRequired() public view override returns (bool) {
return false;
}
/**
* @notice Sends a tokenId to a destination chain via LayerZero ONFT bridging
* @dev This override only builds the message options before calling _debit, so it is operatonally identical to the
* original one
* @param _sendParam Parameters for the ONFT bridging
* @param _fee Fee that the bridge is expected to cost in wei
* @param _refundAddress Address to refund the excess fee to
* @return msgReceipt MessagingReceipt containing details of the bridging operation
*/
function send(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress)
external
payable
override
returns (MessagingReceipt memory msgReceipt)
{
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam);
_debit(msg.sender, _sendParam.tokenId, _sendParam.dstEid);
// @dev Sends the message to the LayerZero Endpoint, returning the MessagingReceipt.
msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);
emit ONFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, _sendParam.tokenId);
}
// ********** Internal functions to facilitate the ERC6454 functionality **********
/**
* @notice Enforces soulbound restrictions during standard transfers and burns
* @dev The check is bypassed during bridge operations to allow _debit and _credit flows
* @param _tokenId Token ID to validate for transferability
*/
function _soulboundCheck(uint256 _tokenId) internal view {
if (!_isBridgeOperation && isNonTransferrable[_tokenId]) revert CannotTransferSoulboundToken();
}
// ********** Internal functions to facilitate the ERC7590 functionality **********
/**
* @notice Transfers a recorded ERC20 balance from a tokenId to a recipient
* @dev Used during burn to return underlying ERC20 assets to the token owner
* @dev Reverts if the internal balance is insufficient or the ERC20 transfer fails
* @param _erc20Contract ERC20 token address to transfer
* @param _tokenId Token ID whose internal balance will be debited
* @param _to Recipient address that will receive the ERC20
* @param _amount Amount of ERC20 to transfer based on internal accounting
*/
function _transferHeldERC20FromToken(address _erc20Contract, uint256 _tokenId, address _to, uint256 _amount)
internal
{
IERC20 erc20Token = IERC20(_erc20Contract);
if (erc20Balances[_tokenId][_erc20Contract] < _amount) revert InsufficientBalance();
erc20Balances[_tokenId][_erc20Contract] -= _amount;
transferOutNonces[_tokenId]++;
if (!erc20Token.trySafeTransfer(_to, _amount)) revert TransferFailed();
emit TransferredERC20(_erc20Contract, _tokenId, _to, _amount);
}
/**
* @notice Transfers the configured BASE mint price plus fee into the contract for a new tokenId
* @dev The price is read from mintPrices for the provided ERC20
* @param _erc20Contract ERC20 token address used for payment
* @param _tokenId Token ID being minted and credited with the deposit
* @param _from Address paying the mint price and fee
*/
function _transferERC20ToToken(address _erc20Contract, uint256 _tokenId, address _from) internal {
uint256 price = mintPrices[_erc20Contract];
_transferERC20ToToken(_erc20Contract, _tokenId, _from, price);
}
/**
* @notice Transfers a specified ERC20 amount plus fee into the contract and credits internal balances
* @dev Used for both BASE minting and GIFT minting with explicit amount inputs
* @dev The fee is recorded in collectedFees and the net amount is recorded in erc20Balances
* @param _erc20Contract ERC20 token address used for payment
* @param _tokenId Token ID being minted and credited with the deposit
* @param _from Address paying the amount and fee
* @param _amount Net _amount to credit to the _tokenId excluding fee
*/
function _transferERC20ToToken(address _erc20Contract, uint256 _tokenId, address _from, uint256 _amount) internal {
IERC20 erc20Token = IERC20(_erc20Contract);
uint256 fee = (_amount * mintingFeeBasisPoints) / MAX_BASIS_POINTS;
uint256 amountWithFee = _amount + fee;
if (erc20Token.allowance(_from, address(this)) < amountWithFee) revert InsufficientAllowance();
if (erc20Token.balanceOf(_from) < amountWithFee) revert InsufficientBalance();
if (!erc20Token.trySafeTransferFrom(_from, address(this), amountWithFee)) revert TransferFailed();
erc20Balances[_tokenId][_erc20Contract] += _amount;
collectedFees[_erc20Contract] += fee;
emit ReceivedERC20(_erc20Contract, _tokenId, _from, _amount);
emit FeeCollected(_erc20Contract, _from, fee);
}
// ********** Internal functions to facilitate the ONFT operations **********
/**
* @notice Performs a bridge-aware burn that preserves token-linked ERC20 state
* @dev This is not a full burn of storage and is used by _debit to represent
* a token leaving the source chain
* @param _owner Current owner of the token being bridged out
* @param _tokenId Token ID to bridge-burn
*/
function _bridgeBurn(address _owner, uint256 _tokenId) internal {
_isBridgeOperation = true;
// Token should only be burned, but the state including ERC20 balances should be preserved
_update(address(0), _tokenId, _owner);
_isBridgeOperation = false;
}
/**
* @notice Debits a token from the source chain during an ONFT send
* @dev Validates approval when the caller is not the owner
* @dev Uses _bridgeBurn to preserve ERC20 state for later credit on the destination chain
* @param _from Address initiating the debit which must be owner or approved
* @param _tokenId Token ID to debit from the source chain
* @param _dstEid Destination endpoint ID provided by ONFT721Core (unused)
*/
function _debit(address _from, uint256 _tokenId, uint32 _dstEid) internal override {
address owner = ownerOf(_tokenId);
if (_from != owner && !isApprovedForAll(owner, _from) && getApproved(_tokenId) != _from) {
revert ERC721InsufficientApproval(_from, _tokenId);
}
_bridgeBurn(owner, _tokenId);
}
/**
* @notice Credits a token on the destination chain during an ONFT receive
* @dev Reverts if the token already exists on the destination chain
* @dev Uses a bridge-aware update that bypasses soulbound checks
* @param _to Address that will receive ownership on the destination chain
* @param _tokenId Token ID to credit on the destination chain
* @param _srcEid Source endpoint ID provided by ONFT721Core
*/
function _credit(address _to, uint256 _tokenId, uint32 _srcEid) internal override {
if (_ownerOf(_tokenId) != address(0)) revert TokenAlreadyExists(_tokenId);
_isBridgeOperation = true;
_update(_to, _tokenId, address(0));
_isBridgeOperation = false;
}
/**
* @notice Builds the ONFT message and options payload for cross-chain sends
* @dev Encodes tokenURI and soulbound flag into the composed message
* @dev Reverts if the receiver is zero or if a soulbound token is sent to a non-owner address
* @param _sendParam SendParam struct containing destination and token data
* @return _message Encoded ONFT message to be dispatched
* @return _options Encoded LayerZero options for the send
*/
function _buildMsgAndOptions(SendParam calldata _sendParam)
internal
view
override
returns (bytes memory _message, bytes memory _options)
{
if (_sendParam.to == bytes32(0)) revert InvalidReceiver();
string memory tokenUri = tokenURI(_sendParam.tokenId);
bool isSoulbound = isNonTransferrable[_sendParam.tokenId];
bytes memory composedMessage = abi.encode(tokenUri, isSoulbound);
if (isSoulbound && _sendParam.to.bytes32ToAddress() != ownerOf(_sendParam.tokenId)) {
revert CannotTransferSoulboundToken();
}
bool hasCompose;
(_message, hasCompose) = ONFT721MsgCodec.encode(_sendParam.to, _sendParam.tokenId, composedMessage);
uint16 msgType = hasCompose ? SEND_AND_COMPOSE : SEND;
_options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);
address inspector = msgInspector;
if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(_message, _options);
}
/**
* @notice Receives a composed ONFT message and reconstructs token state on the destination chain
* @dev Expects a composed message that includes tokenURI and soulbound flag
* @dev Calls _credit before applying the token URI and soulbound flag locally
* @param _origin Origin struct containing srcEid and nonce data
* @param _guid Global unique identifier for the LayerZero message
* @param _message Encoded ONFT message containing composed payload
* @param _executor Unused executor parameter for LayerZero interface compatibility
* @param _executorData Unused executor data parameter for LayerZero interface compatibility
*/
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _executorData
) internal override {
address toAddress = _message.sendTo().bytes32ToAddress();
uint256 tokenId = _message.tokenId();
if (!_message.isComposed()) revert MissingComposedMessage();
bytes memory rawCompose = _message.composeMsg();
bytes memory rawMessage = rawCompose;
uint256 len;
assembly {
len := mload(rawCompose)
// shift pointer forward by 32 bytes (skip fromOApp word)
rawMessage := add(rawMessage, 32)
// set length = originalLength - 32
mstore(rawMessage, sub(len, 32))
}
(string memory tokenUri, bool isSoulbound) = abi.decode(rawMessage, (string, bool));
_credit(toAddress, tokenId, _origin.srcEid);
_setTokenURI(tokenId, tokenUri);
isNonTransferrable[tokenId] = isSoulbound;
emit ONFTReceived(_guid, _origin.srcEid, toAddress, tokenId);
}
// ********** The following functions are overrides required by Solidity. **********
/**
* @notice Central transfer hook used by ERC721, Enumerable, and Pausable logic
* @dev Enforces soulbound rules via _soulboundCheck before delegating to OZ logic
* @param _to Address receiving the token
* @param _tokenId Token ID being updated
* @param _auth Address attempting to authorize the update
* @return Previous owner address returned by the parent implementation
*/
function _update(address _to, uint256 _tokenId, address _auth)
internal
override(ERC721, ERC721Enumerable, ERC721Pausable)
returns (address)
{
_soulboundCheck(_tokenId);
return super._update(_to, _tokenId, _auth);
}
/**
* @notice Resolves the multiple inheritance requirement for _increaseBalance
* @dev Delegates to the OZ implementation to preserve Enumerable invariants
* @param _account Address whose balance is increased
* @param _value Amount of balance increase
*/
function _increaseBalance(address _account, uint128 _value) internal override(ERC721, ERC721Enumerable) {
super._increaseBalance(_account, _value);
}
/**
* @notice Sets a token URI and emits an IERC4906 MetadataUpdate event
* @dev This override ensures metadata refresh signals are emitted for indexers
* @param _tokenId Token ID whose URI is being updated
* @param _tokenUri New token URI to assign
*/
function _setTokenURI(uint256 _tokenId, string memory _tokenUri) internal override {
super._setTokenURI(_tokenId, _tokenUri);
emit MetadataUpdate(_tokenId);
}
/**
* @notice Returns the token URI for a given tokenId
* @dev Resolves the multiple inheritance between ERC721 and ERC721URIStorage
* @param _tokenId Token ID whose URI will be returned
* @return Token URI string
*/
function tokenURI(uint256 _tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(_tokenId);
}
/**
* @notice Declares supported interfaces across ERC721 extensions and custom standards
* @dev Includes IERC7590, IERC6454, IERC4906, and IONFT721 support
* @param _interfaceId Interface identifier to check
* @return True if the interface is supported
*/
function supportsInterface(bytes4 _interfaceId)
public
view
override(ERC721, ERC721Enumerable, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(_interfaceId) || _interfaceId == type(IERC7590).interfaceId
|| _interfaceId == type(IERC6454).interfaceId || _interfaceId == type(IERC4906).interfaceId
|| _interfaceId == type(IONFT721).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "./IERC721.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {ERC721Utils} from "./utils/ERC721Utils.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[ERC-721] 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_;
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/// @inheritdoc IERC721
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/// @inheritdoc IERC721
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/// @inheritdoc IERC721Metadata
function name() public view virtual returns (string memory) {
return _name;
}
/// @inheritdoc IERC721Metadata
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/// @inheritdoc IERC721Metadata
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 "";
}
/// @inheritdoc IERC721
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/// @inheritdoc IERC721
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/// @inheritdoc IERC721
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/// @inheritdoc IERC721
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/// @inheritdoc IERC721
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);
}
}
/// @inheritdoc IERC721
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/// @inheritdoc IERC721
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
ERC721Utils.checkOnERC721Received(_msgSender(), 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 ERC-721 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 `owner` for `tokenId`.
* - `spender` does not have approval to manage all of `owner`'s assets.
*
* 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);
ERC721Utils.checkOnERC721Received(_msgSender(), 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 ERC-721 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);
ERC721Utils.checkOnERC721Received(_msgSender(), 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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/extensions/ERC721Burnable.sol)
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {Context} from "../../../utils/Context.sol";
/**
* @title ERC-721 Burnable Token
* @dev ERC-721 Token that can be burned (destroyed).
*/
abstract contract ERC721Burnable is Context, ERC721 {
/**
* @dev Burns `tokenId`. See {ERC721-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/
function burn(uint256 tokenId) public virtual {
// 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.
_update(address(0), tokenId, _msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/ERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {IERC721Enumerable} from "./IERC721Enumerable.sol";
import {IERC165} from "../../../utils/introspection/ERC165.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the ERC that adds enumerability
* of all the token ids in the contract as well as all token ids owned by each account.
*
* CAUTION: {ERC721} extensions that implement custom `balanceOf` logic, such as {ERC721Consecutive},
* interfere with enumerability and should not be used together with {ERC721Enumerable}.
*/
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens;
mapping(uint256 tokenId => uint256) private _ownedTokensIndex;
uint256[] private _allTokens;
mapping(uint256 tokenId => uint256) private _allTokensIndex;
/**
* @dev An `owner`'s token query was out of bounds for `index`.
*
* NOTE: The owner being `address(0)` indicates a global out of bounds index.
*/
error ERC721OutOfBoundsIndex(address owner, uint256 index);
/**
* @dev Batch mint is not allowed.
*/
error ERC721EnumerableForbiddenBatchMint();
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
/// @inheritdoc IERC721Enumerable
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
if (index >= balanceOf(owner)) {
revert ERC721OutOfBoundsIndex(owner, index);
}
return _ownedTokens[owner][index];
}
/// @inheritdoc IERC721Enumerable
function totalSupply() public view virtual returns (uint256) {
return _allTokens.length;
}
/// @inheritdoc IERC721Enumerable
function tokenByIndex(uint256 index) public view virtual returns (uint256) {
if (index >= totalSupply()) {
revert ERC721OutOfBoundsIndex(address(0), index);
}
return _allTokens[index];
}
/// @inheritdoc ERC721
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
address previousOwner = super._update(to, tokenId, auth);
if (previousOwner == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_removeTokenFromOwnerEnumeration(previousOwner, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_addTokenToOwnerEnumeration(to, tokenId);
}
return previousOwner;
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = balanceOf(to) - 1;
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = balanceOf(from);
uint256 tokenIndex = _ownedTokensIndex[tokenId];
mapping(uint256 index => uint256) storage _ownedTokensByOwner = _ownedTokens[from];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokensByOwner[lastTokenIndex];
_ownedTokensByOwner[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokensByOwner[lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
/**
* See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
*/
function _increaseBalance(address account, uint128 amount) internal virtual override {
if (amount > 0) {
revert ERC721EnumerableForbiddenBatchMint();
}
super._increaseBalance(account, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/extensions/ERC721Pausable.sol)
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {Pausable} from "../../../utils/Pausable.sol";
/**
* @dev ERC-721 token with pausable token transfers, minting and burning.
*
* Useful for scenarios such as preventing trades until the end of an evaluation
* period, or having an emergency switch for freezing all token transfers in the
* event of a large bug.
*
* IMPORTANT: This contract does not include public pause and unpause functions. In
* addition to inheriting this contract, you must define both functions, invoking the
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
* make the contract pause mechanism of the contract unreachable, and thus unusable.
*/
abstract contract ERC721Pausable is ERC721, Pausable {
/**
* @dev See {ERC721-_update}.
*
* Requirements:
*
* - the contract must not be paused.
*/
function _update(
address to,
uint256 tokenId,
address auth
) internal virtual override whenNotPaused returns (address) {
return super._update(to, tokenId, auth);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/ERC721URIStorage.sol)
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {IERC721Metadata} from "./IERC721Metadata.sol";
import {Strings} from "../../../utils/Strings.sol";
import {IERC4906} from "../../../interfaces/IERC4906.sol";
import {IERC165} from "../../../interfaces/IERC165.sol";
/**
* @dev ERC-721 token with storage based token URI management.
*/
abstract contract ERC721URIStorage is IERC4906, ERC721 {
using Strings for uint256;
// Interface ID as defined in ERC-4906. This does not correspond to a traditional interface ID as ERC-4906 only
// defines events and does not include any external function.
bytes4 private constant ERC4906_INTERFACE_ID = bytes4(0x49064906);
// Optional mapping for token URIs
mapping(uint256 tokenId => string) private _tokenURIs;
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC165) returns (bool) {
return interfaceId == ERC4906_INTERFACE_ID || super.supportsInterface(interfaceId);
}
/// @inheritdoc IERC721Metadata
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireOwned(tokenId);
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via string.concat).
if (bytes(_tokenURI).length > 0) {
return string.concat(base, _tokenURI);
}
return super.tokenURI(tokenId);
}
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Emits {IERC4906-MetadataUpdate}.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
_tokenURIs[tokenId] = _tokenURI;
emit MetadataUpdate(tokenId);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ===================== IFraxiversaryErrors =========================
* ====================================================================
* Errors of the Fraxiversary NFT contract for the 5th anniversary of Frax Finance
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title IFraxiversaryErrors
* @author Frax Finance
* @notice A collection of errors used by the Fraxiversary NFT collection.
*/
contract IFraxiversaryErrors {
/// @notice Attempted to set a mint price to the same value already stored
error AttemptingToSetExistingMintPrice();
/// @notice Attempted to transfer a token marked as non-transferable
error CannotTransferSoulboundToken();
/// @notice Attempted to fuse tokens that are not all BASE type
error CanOnlyFuseBaseTokens();
/// @notice Attempted to unfuse a token that is not FUSED type
error CanOnlyUnfuseFusedTokens();
/// @notice Gift minting supply limit has been reached
error GiftMintingLimitReached();
/// @notice ERC20 allowance is insufficient for the required transfer
error InsufficientAllowance();
/// @notice Balance is insufficient for the requested operation
error InsufficientBalance();
/// @notice Gift mint price is outside allowed constraints
error InvalidGiftMintPrice();
/// @notice Provided tokenId range is invalid
error InvalidRange();
/// @notice Base minting supply limit has been reached
error MintingLimitReached();
/// @notice Minting period has ended based on the cutoff block
error MintingPeriodOver();
/// @notice Expected a composed ONFT message but none was provided
error MissingComposedMessage();
/// @notice Only the current token owner can burn the token
error OnlyTokenOwnerCanBurnTheToken();
/// @notice Only the owner of all provided tokens can fuse them
error OnlyTokenOwnerCanFuseTokens();
/// @notice Only the owner of the premium token can unfuse it
error OnlyTokenOwnerCanUnfuseTokens();
/// @notice Provided value or tokenId is outside allowed bounds
error OutOfBounds();
/// @notice Underlying ERC20 assets for BASE tokens must all be distinct
error SameTokenUnderlyingAssets();
/// @notice Attempted to mint or credit a tokenId that already exists
/// @dev tokenId Token ID that already exists on the current chain
error TokenAlreadyExists(uint256 tokenId);
/// @notice Token does not exist or has been burned
error TokenDoesNotExist();
/// @notice ERC20 deposits to tokenIds are only allowed during minting
error TokensCanOnlyBeDepositedByNftMint();
/// @notice ERC20 withdrawals from tokenIds are only allowed via burn
error TokensCanOnlyBeRetrievedByNftBurn();
/// @notice ERC20 transfer or transferFrom returned false
error TransferFailed();
/// @notice FUSED tokens must be unfused before burning
error UnfuseTokenBeforeBurning();
/// @notice ERC20 token is not supported for BASE minting
error UnsupportedToken();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ===================== IFraxiversaryEvents =========================
* ====================================================================
* Events of the Fraxiversary NFT contract for the 5th anniversary of Frax Finance
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title IFraxiversaryEvents
* @author Frax Finance
* @notice A collection of events used by the Fraxiversary NFT collection.
*/
contract IFraxiversaryEvents {
/**
* @notice Emitted when a minting fee is collected for a given ERC20
* @dev This is emitted during BASE and GIFT mint flows when a fee is added on top of the net amount
* @param erc20Contract ERC20 token used for the mint payment
* @param from Address that paid the fee
* @param feeAmount Fee amount recorded for the ERC20
*/
event FeeCollected(address indexed erc20Contract, address indexed from, uint256 feeAmount);
/**
* @notice Emitted when the contract owner retrieves accumulated minting fees
* @dev The fee balance for the ERC20 is reset to zero before transferring out
* @param erc20Contract ERC20 token whose fees were retrieved
* @param to Address that received the fees
* @param feeAmount Fee amount transferred to the recipient
*/
event FeesRetrieved(address indexed erc20Contract, address indexed to, uint256 feeAmount);
/**
* @notice Emitted when a GIFT token is minted for a recipient
* @dev The minter pays the giftMintingPrice plus fee while the recipient receives the NFT
* @param minter Address that paid for the gift mint
* @param recipient Address that received the newly minted GIFT token
* @param tokenId Newly minted GIFT token ID
* @param mintPrice Net mint price excluding fee
*/
event GiftMinted(address indexed minter, address indexed recipient, uint256 tokenId, uint256 mintPrice);
/**
* @notice Emitted when the base GIFT mint price is updated
* @dev This event tracks changes to giftMintingPrice
* @param previousMintPrice Previous GIFT mint price
* @param newMintPrice New GIFT mint price
*/
event GiftMintPriceUpdated(uint256 previousMintPrice, uint256 newMintPrice);
/**
* @notice Emitted when the minting cutoff block is updated
* @dev The cutoff governs when BASE and GIFT minting is disabled
* @param previousCutoffBlock Previous cutoff block number
* @param newCutoffBlock New cutoff block number
*/
event MintingCutoffBlockUpdated(uint256 previousCutoffBlock, uint256 newCutoffBlock);
/**
* @notice Emitted when the minting fee basis points value is updated
* @dev The fee applies to both BASE and GIFT mint prices
* @param previousFeeBasisPoints Previous fee value in basis points
* @param newFeeBasisPoints New fee value in basis points
*/
event MintingFeeUpdated(uint256 previousFeeBasisPoints, uint256 newFeeBasisPoints);
/**
* @notice Emitted when a BASE mint price is added, updated, or removed for an ERC20
* @dev Setting newMintPrice to zero indicates removal from the supported list
* @param erc20Contract ERC20 token whose mint price was updated
* @param previousMintPrice Previous BASE mint price for the ERC20
* @param newMintPrice New BASE mint price for the ERC20
*/
event MintPriceUpdated(address indexed erc20Contract, uint256 previousMintPrice, uint256 newMintPrice);
/**
* @notice Emitted when a new SOULBOUND token is minted
* @dev SOULBOUND tokens are marked non-transferable
* @param tokenOwner Address that received the soulbound token
* @param tokenId Newly minted SOULBOUND token ID
*/
event NewSoulboundToken(address indexed tokenOwner, uint256 tokenId);
/**
* @notice Emitted when four BASE tokens are fused into a FUSED premium token
* @dev The underlying BASE tokens are transferred into contract custody
* @param owner Address that performed the fuse and received the premium token
* @param underlyingToken1 First underlying BASE token ID
* @param underlyingToken2 Second underlying BASE token ID
* @param underlyingToken3 Third underlying BASE token ID
* @param underlyingToken4 Fourth underlying BASE token ID
* @param premiumTokenId Newly minted FUSED token ID
*/
event TokenFused(
address indexed owner,
uint256 underlyingToken1,
uint256 underlyingToken2,
uint256 underlyingToken3,
uint256 underlyingToken4,
uint256 premiumTokenId
);
/**
* @notice Emitted when a FUSED premium token is burned and its BASE tokens are returned
* @dev The underlying token references on the premium token are cleared after unfusing
* @param owner Address that unfused the premium token and received the BASE tokens back
* @param underlyingToken1 First underlying BASE token ID returned
* @param underlyingToken2 Second underlying BASE token ID returned
* @param underlyingToken3 Third underlying BASE token ID returned
* @param underlyingToken4 Fourth underlying BASE token ID returned
* @param premiumTokenId FUSED token ID that was unfused and burned
*/
event TokenUnfused(
address indexed owner,
uint256 underlyingToken1,
uint256 underlyingToken2,
uint256 underlyingToken3,
uint256 underlyingToken4,
uint256 premiumTokenId
);
}/// @title EIP-6454 Minimalistic Non-Transferable interface for NFTs
/// @dev See https://eips.ethereum.org/EIPS/eip-6454
/// @dev Note: the ERC-165 identifier for this interface is 0x91a6262f.
pragma solidity ^0.8.30;
interface IERC6454 {
/* is IERC165 */
/**
* @notice Used to check whether the given token is transferable or not.
* @dev If this function returns `false`, the transfer of the token MUST revert execution.
* @dev If the tokenId does not exist, this method MUST revert execution, unless the token is being checked for
* minting.
* @dev The `from` parameter MAY be used to also validate the approval of the token for transfer, but anyone
* interacting with this function SHOULD NOT rely on it as it is not mandated by the proposal.
* @param tokenId ID of the token being checked
* @param from Address from which the token is being transferred
* @param to Address to which the token is being transferred
* @return Boolean value indicating whether the given token is transferable
*/
function isTransferable(uint256 tokenId, address from, address to) external view returns (bool);
}// SPDX-License-Identifier: CC0
pragma solidity ^0.8.30;
interface IERC7590 {
/**
* @notice Used to notify listeners that the token received ERC-20 tokens.
* @param erc20Contract The address of the ERC-20 smart contract
* @param toTokenId The ID of the token receiving the ERC-20 tokens
* @param from The address of the account from which the tokens are being transferred
* @param amount The number of ERC-20 tokens received
*/
event ReceivedERC20(address indexed erc20Contract, uint256 indexed toTokenId, address indexed from, uint256 amount);
/**
* @notice Used to notify the listeners that the ERC-20 tokens have been transferred.
* @param erc20Contract The address of the ERC-20 smart contract
* @param fromTokenId The ID of the token from which the ERC-20 tokens have been transferred
* @param to The address receiving the ERC-20 tokens
* @param amount The number of ERC-20 tokens transferred
*/
event TransferredERC20(
address indexed erc20Contract, uint256 indexed fromTokenId, address indexed to, uint256 amount
);
/**
* @notice Used to retrieve the given token's specific ERC-20 balance
* @param erc20Contract The address of the ERC-20 smart contract
* @param tokenId The ID of the token being checked for ERC-20 balance
* @return The amount of the specified ERC-20 tokens owned by a given token
*/
function balanceOfERC20(address erc20Contract, uint256 tokenId) external view returns (uint256);
/**
* @notice Transfer ERC-20 tokens from a specific token.
* @dev The balance MUST be transferred from this smart contract.
* @dev MUST increase the transfer-out-nonce for the tokenId
* @dev MUST revert if the `msg.sender` is not the owner of the NFT or approved to manage it.
* @param erc20Contract The address of the ERC-20 smart contract
* @param tokenId The ID of the token to transfer the ERC-20 tokens from
* @param amount The number of ERC-20 tokens to transfer
* @param data Additional data with no specified format, to allow for custom logic
*/
function transferHeldERC20FromToken(
address erc20Contract,
uint256 tokenId,
address to,
uint256 amount,
bytes memory data
) external;
/**
* @notice Transfer ERC-20 tokens to a specific token.
* @dev The ERC-20 smart contract must have approval for this contract to transfer the ERC-20 tokens.
* @dev The balance MUST be transferred from the `msg.sender`.
* @param erc20Contract The address of the ERC-20 smart contract
* @param tokenId The ID of the token to transfer ERC-20 tokens to
* @param amount The number of ERC-20 tokens to transfer
* @param data Additional data with no specified format, to allow for custom logic
*/
function transferERC20ToToken(address erc20Contract, uint256 tokenId, uint256 amount, bytes memory data) external;
/**
* @notice Nonce increased every time an ERC20 token is transferred out of a token
* @param tokenId The ID of the token to check the nonce for
* @return The nonce of the token
*/
function erc20TransferOutNonce(uint256 tokenId) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC4906.sol)
pragma solidity >=0.6.2;
import {IERC165} from "./IERC165.sol";
import {IERC721} from "./IERC721.sol";
/// @title ERC-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
/// @dev This event emits when the metadata of a token is changed.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFT.
event MetadataUpdate(uint256 _tokenId);
/// @dev This event emits when the metadata of a range of tokens is changed.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFTs.
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { OApp, Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol";
import { IOAppMsgInspector } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol";
import { OAppPreCrimeSimulator } from "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol";
import { IONFT721, MessagingFee, MessagingReceipt, SendParam } from "./interfaces/IONFT721.sol";
import { ONFT721MsgCodec } from "./libs/ONFT721MsgCodec.sol";
import { ONFTComposeMsgCodec } from "../libs/ONFTComposeMsgCodec.sol";
/**
* @title ONFT721Core
* @dev Abstract contract for an ONFT721 token.
*/
abstract contract ONFT721Core is IONFT721, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {
using ONFT721MsgCodec for bytes;
using ONFT721MsgCodec for bytes32;
// @notice Msg types that are used to identify the various OFT operations.
// @dev This can be extended in child contracts for non-default oft operations
// @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.
uint16 public constant SEND = 1;
uint16 public constant SEND_AND_COMPOSE = 2;
// Address of an optional contract to inspect both 'message' and 'options'
address public msgInspector;
event MsgInspectorSet(address inspector);
/**
* @dev Constructor.
* @param _lzEndpoint The address of the LayerZero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*/
constructor(address _lzEndpoint, address _delegate) Ownable(_delegate) OApp(_lzEndpoint, _delegate) {}
/**
* @notice Retrieves interfaceID and the version of the ONFT.
* @return interfaceId The interface ID (0x23e18da6).
* @return version The version.
* @dev version: Indicates a cross-chain compatible msg encoding with other ONFTs.
* @dev If a new feature is added to the ONFT cross-chain msg encoding, the version will be incremented.
* @dev ie. localONFT version(x,1) CAN send messages to remoteONFT version(x,1)
*/
function onftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {
return (type(IONFT721).interfaceId, 1);
}
/**
* @notice Sets the message inspector address for the OFT.
* @param _msgInspector The address of the message inspector.
* @dev This is an optional contract that can be used to inspect both 'message' and 'options'.
* @dev Set it to address(0) to disable it, or set it to a contract address to enable it.
*/
function setMsgInspector(address _msgInspector) public virtual onlyOwner {
msgInspector = _msgInspector;
emit MsgInspectorSet(_msgInspector);
}
function quoteSend(
SendParam calldata _sendParam,
bool _payInLzToken
) external view virtual returns (MessagingFee memory msgFee) {
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam);
return _quote(_sendParam.dstEid, message, options, _payInLzToken);
}
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable virtual returns (MessagingReceipt memory msgReceipt) {
_debit(msg.sender, _sendParam.tokenId, _sendParam.dstEid);
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam);
// @dev Sends the message to the LayerZero Endpoint, returning the MessagingReceipt.
msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);
emit ONFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, _sendParam.tokenId);
}
/**
* @dev Internal function to build the message and options.
* @param _sendParam The parameters for the send() operation.
* @return message The encoded message.
* @return options The encoded options.
*/
function _buildMsgAndOptions(
SendParam calldata _sendParam
) internal view virtual returns (bytes memory message, bytes memory options) {
if (_sendParam.to == bytes32(0)) revert InvalidReceiver();
bool hasCompose;
(message, hasCompose) = ONFT721MsgCodec.encode(_sendParam.to, _sendParam.tokenId, _sendParam.composeMsg);
uint16 msgType = hasCompose ? SEND_AND_COMPOSE : SEND;
options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);
// @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.
// @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean
address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read
if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);
}
/**
* @dev Internal function to handle the receive on the LayerZero endpoint.
* @param _origin The origin information.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address from the src chain.
* - nonce: The nonce of the LayerZero message.
* @param _guid The unique identifier for the received LayerZero message.
* @param _message The encoded message.
* @dev _executor The address of the executor.
* @dev _extraData Additional data.
*/
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address /*_executor*/, // @dev unused in the default implementation.
bytes calldata /*_extraData*/ // @dev unused in the default implementation.
) internal virtual override {
address toAddress = _message.sendTo().bytes32ToAddress();
uint256 tokenId = _message.tokenId();
_credit(toAddress, tokenId, _origin.srcEid);
if (_message.isComposed()) {
bytes memory composeMsg = ONFTComposeMsgCodec.encode(_origin.nonce, _origin.srcEid, _message.composeMsg());
// @dev As batching is not implemented, the compose index is always 0.
// @dev If batching is added, the index will need to be tracked.
endpoint.sendCompose(toAddress, _guid, 0 /* the index of composed message*/, composeMsg);
}
emit ONFTReceived(_guid, _origin.srcEid, toAddress, tokenId);
}
/*
* @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.
* @param _origin The origin information.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address from the src chain.
* - nonce: The nonce of the LayerZero message.
* @param _guid The unique identifier for the received LayerZero message.
* @param _message The LayerZero message.
* @param _executor The address of the off-chain executor.
* @param _extraData Arbitrary data passed by the msg executor.
* @dev Enables the preCrime simulator to mock sending lzReceive() messages,
* routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.
*/
function _lzReceiveSimulate(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) internal virtual override {
_lzReceive(_origin, _guid, _message, _executor, _extraData);
}
/**
* @dev Check if the peer is considered 'trusted' by the OApp.
* @param _eid The endpoint ID to check.
* @param _peer The peer to check.
* @return Whether the peer passed is considered 'trusted' by the OApp.
* @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.
*/
function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {
return peers[_eid] == _peer;
}
function _debit(address /*_from*/, uint256 /*_tokenId*/, uint32 /*_dstEid*/) internal virtual;
function _credit(address /*_to*/, uint256 /*_tokenId*/, uint32 /*_srcEid*/) internal virtual;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { MessagingFee, MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol";
/**
* @dev Struct representing token parameters for the ONFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination LayerZero EndpointV2 ID.
bytes32 to; // Recipient address.
uint256 tokenId;
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes onftCmd; // The ONFT command to be executed, unused in default ONFT implementations.
}
/**
* @title IONFT
* @dev Interface for the ONFT721 token.
* @dev Does not inherit ERC721 to accommodate usage by OFT721Adapter.
*/
interface IONFT721 {
// Custom error messages
error InvalidReceiver();
error OnlyNFTOwner(address caller, address owner);
// Events
event ONFTSent(
bytes32 indexed guid, // GUID of the ONFT message.
uint32 dstEid, // Destination Endpoint ID.
address indexed fromAddress, // Address of the sender on the src chain.
uint256 tokenId // ONFT ID sent.
);
event ONFTReceived(
bytes32 indexed guid, // GUID of the ONFT message.
uint32 srcEid, // Source Endpoint ID.
address indexed toAddress, // Address of the recipient on the dst chain.
uint256 tokenId // ONFT ID received.
);
/**
* @notice Retrieves interfaceID and the version of the ONFT.
* @return interfaceId The interface ID.
* @return version The version.
* @dev interfaceId: This specific interface ID is '0x94642228'.
* @dev version: Indicates a cross-chain compatible msg encoding with other ONFTs.
* @dev If a new feature is added to the ONFT cross-chain msg encoding, the version will be incremented.
* ie. localONFT version(x,1) CAN send messages to remoteONFT version(x,1)
*/
function onftVersion() external view returns (bytes4 interfaceId, uint64 version);
/**
* @notice Retrieves the address of the token associated with the ONFT.
* @return token The address of the ERC721 token implementation.
*/
function token() external view returns (address);
/**
* @notice Indicates whether the ONFT contract requires approval of the 'token()' to send.
* @return requiresApproval Needs approval of the underlying token implementation.
* @dev Allows things like wallet implementers to determine integration requirements,
* without understanding the underlying token implementation.
*/
function approvalRequired() external view returns (bool);
/**
* @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 from the send() operation.
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);
/**
* @notice Executes the send() operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The fee information supplied by the caller.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds from fees etc. on the src.
* @return receipt The LayerZero messaging receipt from the send() operation.
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/**
* @title ONFT721MsgCodec
* @notice Library for encoding and decoding ONFT721 LayerZero messages.
*/
library ONFT721MsgCodec {
uint8 private constant SEND_TO_OFFSET = 32;
uint8 private constant TOKEN_ID_OFFSET = 64;
/**
* @dev Encodes an ONFT721 LayerZero message payload.
* @param _sendTo The recipient address.
* @param _tokenId The ID of the token to transfer.
* @param _composeMsg The composed payload.
* @return payload The encoded message payload.
* @return hasCompose A boolean indicating whether the message payload contains a composed payload.
*/
function encode(
bytes32 _sendTo,
uint256 _tokenId,
bytes memory _composeMsg
) internal view returns (bytes memory payload, bool hasCompose) {
hasCompose = _composeMsg.length > 0;
payload = hasCompose
? abi.encodePacked(_sendTo, _tokenId, addressToBytes32(msg.sender), _composeMsg)
: abi.encodePacked(_sendTo, _tokenId);
}
/**
* @dev Decodes sendTo from the ONFT LayerZero message.
* @param _msg The message.
* @return The recipient address in bytes32 format.
*/
function sendTo(bytes calldata _msg) internal pure returns (bytes32) {
return bytes32(_msg[:SEND_TO_OFFSET]);
}
/**
* @dev Decodes tokenId from the ONFT LayerZero message.
* @param _msg The message.
* @return The ID of the tokens to transfer.
*/
function tokenId(bytes calldata _msg) internal pure returns (uint256) {
return uint256(bytes32(_msg[SEND_TO_OFFSET:TOKEN_ID_OFFSET]));
}
/**
* @dev Decodes whether there is a composed payload.
* @param _msg The message.
* @return A boolean indicating whether the message has a composed payload.
*/
function isComposed(bytes calldata _msg) internal pure returns (bool) {
return _msg.length > TOKEN_ID_OFFSET;
}
/**
* @dev Decodes the composed message.
* @param _msg The message.
* @return The composed message.
*/
function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {
return _msg[TOKEN_ID_OFFSET:];
}
/**
* @dev Converts an address to bytes32.
* @param _addr The address to convert.
* @return The bytes32 representation of the address.
*/
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
/**
* @dev Converts bytes32 to an address.
* @param _b The bytes32 value to convert.
* @return The address representation of bytes32.
*/
function bytes32ToAddress(bytes32 _b) internal pure returns (address) {
return address(uint160(uint256(_b)));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/**
* @title ONFT Composed Message Codec
* @notice Library for encoding and decoding ONFT composed messages.
*/
library ONFTComposeMsgCodec {
// Offset constants for decoding composed messages
uint8 private constant NONCE_OFFSET = 8;
uint8 private constant SRC_EID_OFFSET = 12;
uint8 private constant COMPOSE_FROM_OFFSET = 44;
/**
* @dev Encodes a ONFT721 composed message.
* @param _nonce The nonce value.
* @param _srcEid The source LayerZero endpoint ID.
* @param _composeMsg The composed message.
* @return The encoded payload, including the composed message.
*/
function encode(
uint64 _nonce,
uint32 _srcEid,
bytes memory _composeMsg // 0x[composeFrom][composeMsg]
) internal pure returns (bytes memory) {
return abi.encodePacked(_nonce, _srcEid, _composeMsg);
}
/**
* @dev Retrieves the nonce for the composed message.
* @param _msg The message.
* @return The nonce value.
*/
function nonce(bytes calldata _msg) internal pure returns (uint64) {
return uint64(bytes8(_msg[:NONCE_OFFSET]));
}
/**
* @dev Retrieves the source LayerZero endpoint ID for the composed message.
* @param _msg The message.
* @return The source LayerZero endpoint ID.
*/
function srcEid(bytes calldata _msg) internal pure returns (uint32) {
return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));
}
/**
* @dev Retrieves the composeFrom value from the composed message.
* @param _msg The message.
* @return The composeFrom value as bytes32.
*/
function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {
return bytes32(_msg[SRC_EID_OFFSET:COMPOSE_FROM_OFFSET]);
}
/**
* @dev Retrieves the composed message.
* @param _msg The message.
* @return The composed message.
*/
function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {
return _msg[COMPOSE_FROM_OFFSET:];
}
/**
* @dev Converts an address to bytes32.
* @param _addr The address to convert.
* @return The bytes32 representation of the address.
*/
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
/**
* @dev Converts bytes32 to an address.
* @param _b The bytes32 value to convert.
* @return The address representation of bytes32.
*/
function bytes32ToAddress(bytes32 _b) internal pure returns (address) {
return address(uint160(uint256(_b)));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title IOAppMsgInspector
* @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.
*/
interface IOAppMsgInspector {
// Custom error message for inspection failure
error InspectionFailed(bytes message, bytes options);
/**
* @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.
* @param _message The message payload to be inspected.
* @param _options Additional options or parameters for inspection.
* @return valid A boolean indicating whether the inspection passed (true) or failed (false).
*
* @dev Optionally done as a revert, OR use the boolean provided to handle the failure.
*/
function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers
// solhint-disable-next-line no-unused-import
import { OAppSender, MessagingFee, MessagingReceipt } from "./OAppSender.sol";
// @dev Import the 'Origin' so it's exposed to OApp implementers
// solhint-disable-next-line no-unused-import
import { OAppReceiver, Origin } from "./OAppReceiver.sol";
import { OAppCore } from "./OAppCore.sol";
/**
* @title OApp
* @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.
*/
abstract contract OApp is OAppSender, OAppReceiver {
/**
* @dev Constructor to initialize the OApp with the provided endpoint and owner.
* @param _endpoint The address of the LOCAL LayerZero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*/
constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol implementation.
* @return receiverVersion The version of the OAppReceiver.sol implementation.
*/
function oAppVersion()
public
pure
virtual
override(OAppSender, OAppReceiver)
returns (uint64 senderVersion, uint64 receiverVersion)
{
return (SENDER_VERSION, RECEIVER_VERSION);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol)
pragma solidity >=0.6.2;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 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 ERC-721 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 ERC-721
* 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity >=0.6.2;
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.4.0) (token/ERC721/utils/ERC721Utils.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../IERC721Receiver.sol";
import {IERC721Errors} from "../../../interfaces/draft-IERC6093.sol";
/**
* @dev Library that provide common ERC-721 utility functions.
*
* See https://eips.ethereum.org/EIPS/eip-721[ERC-721].
*
* _Available since v5.1._
*/
library ERC721Utils {
/**
* @dev Performs an acceptance check for the provided `operator` by calling {IERC721Receiver-onERC721Received}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
*
* The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).
* Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept
* the transfer.
*/
function checkOnERC721Received(
address operator,
address from,
address to,
uint256 tokenId,
bytes memory data
) internal {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
// Token rejected
revert IERC721Errors.ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC721Receiver implementer
revert IERC721Errors.ERC721InvalidReceiver(to);
} else {
assembly ("memory-safe") {
revert(add(reason, 0x20), mload(reason))
}
}
}
}
}
}// 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.4.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
using SafeCast for *;
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
uint256 private constant SPECIAL_CHARS_LOOKUP =
(1 << 0x08) | // backspace
(1 << 0x09) | // tab
(1 << 0x0a) | // newline
(1 << 0x0c) | // form feed
(1 << 0x0d) | // carriage return
(1 << 0x22) | // double quote
(1 << 0x5c); // backslash
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev The string being parsed contains characters that are not in scope of the given base.
*/
error StringsInvalidChar();
/**
* @dev The string being parsed is not a properly formatted address.
*/
error StringsInvalidAddressFormat();
/**
* @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;
assembly ("memory-safe") {
ptr := add(add(buffer, 0x20), length)
}
while (true) {
ptr--;
assembly ("memory-safe") {
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 Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
* representation, according to EIP-55.
*/
function toChecksumHexString(address addr) internal pure returns (string memory) {
bytes memory buffer = bytes(toHexString(addr));
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue;
assembly ("memory-safe") {
hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
}
for (uint256 i = 41; i > 1; --i) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
// case shift by xoring with 0x20
buffer[i] ^= 0x20;
}
hashValue >>= 4;
}
return string(buffer);
}
/**
* @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));
}
/**
* @dev Parse a decimal string and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input) internal pure returns (uint256) {
return parseUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
uint256 result = 0;
for (uint256 i = begin; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 9) return (false, 0);
result *= 10;
result += chr;
}
return (true, result);
}
/**
* @dev Parse a decimal string and returns the value as a `int256`.
*
* Requirements:
* - The string must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input) internal pure returns (int256) {
return parseInt(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
(bool success, int256 value) = tryParseInt(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
* the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
}
uint256 private constant ABS_MIN_INT256 = 2 ** 255;
/**
* @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character or if the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, int256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseIntUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseIntUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, int256 value) {
bytes memory buffer = bytes(input);
// Check presence of a negative sign.
bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
bool positiveSign = sign == bytes1("+");
bool negativeSign = sign == bytes1("-");
uint256 offset = (positiveSign || negativeSign).toUint();
(bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
if (absSuccess && absValue < ABS_MIN_INT256) {
return (true, negativeSign ? -int256(absValue) : int256(absValue));
} else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
return (true, type(int256).min);
} else return (false, 0);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input) internal pure returns (uint256) {
return parseHexUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseHexUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
* invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseHexUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseHexUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
// skip 0x prefix if present
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 offset = hasPrefix.toUint() * 2;
uint256 result = 0;
for (uint256 i = begin + offset; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 15) return (false, 0);
result *= 16;
unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr;
}
}
return (true, result);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input) internal pure returns (address) {
return parseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
(bool success, address value) = tryParseAddress(input, begin, end);
if (!success) revert StringsInvalidAddressFormat();
return value;
}
/**
* @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
* formatted address. See {parseAddress-string} requirements.
*/
function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
return tryParseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
* formatted address. See {parseAddress-string-uint256-uint256} requirements.
*/
function tryParseAddress(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, address value) {
if (end > bytes(input).length || begin > end) return (false, address(0));
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
// check that input is the correct length
if (end - begin == expectedLength) {
// length guarantees that this does not overflow, and value is at most type(uint160).max
(bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
return (s, address(uint160(v)));
} else {
return (false, address(0));
}
}
function _tryParseChr(bytes1 chr) private pure returns (uint8) {
uint8 value = uint8(chr);
// Try to parse `chr`:
// - Case 1: [0-9]
// - Case 2: [a-f]
// - Case 3: [A-F]
// - otherwise not supported
unchecked {
if (value > 47 && value < 58) value -= 48;
else if (value > 96 && value < 103) value -= 87;
else if (value > 64 && value < 71) value -= 55;
else return type(uint8).max;
}
return value;
}
/**
* @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
*
* WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
*
* NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
* RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
* characters that are not in this range, but other tooling may provide different results.
*/
function escapeJSON(string memory input) internal pure returns (string memory) {
bytes memory buffer = bytes(input);
bytes memory output = new bytes(2 * buffer.length); // worst case scenario
uint256 outputLength = 0;
for (uint256 i; i < buffer.length; ++i) {
bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
output[outputLength++] = "\\";
if (char == 0x08) output[outputLength++] = "b";
else if (char == 0x09) output[outputLength++] = "t";
else if (char == 0x0a) output[outputLength++] = "n";
else if (char == 0x0c) output[outputLength++] = "f";
else if (char == 0x0d) output[outputLength++] = "r";
else if (char == 0x5c) output[outputLength++] = "\\";
else if (char == 0x22) {
// solhint-disable-next-line quotes
output[outputLength++] = '"';
}
} else {
output[outputLength++] = char;
}
}
// write the actual length and deallocate unused memory
assembly ("memory-safe") {
mstore(output, outputLength)
mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
}
return string(output);
}
/**
* @dev Reads a bytes32 from a bytes array without bounds checking.
*
* NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
* assembly block as such would prevent some optimizations.
*/
function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
assembly ("memory-safe") {
value := mload(add(add(buffer, 0x20), offset))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.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 ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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.4.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity >=0.6.2;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC721.sol)
pragma solidity >=0.6.2;
import {IERC721} from "../token/ERC721/IERC721.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IOAppOptionsType3, EnforcedOptionParam } from "../interfaces/IOAppOptionsType3.sol";
/**
* @title OAppOptionsType3
* @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.
*/
abstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {
uint16 internal constant OPTION_TYPE_3 = 3;
// @dev The "msgType" should be defined in the child contract.
mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;
/**
* @dev Sets the enforced options for specific endpoint and message type combinations.
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
* @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
* eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
* if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
*/
function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {
_setEnforcedOptions(_enforcedOptions);
}
/**
* @dev Sets the enforced options for specific endpoint and message type combinations.
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
*
* @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
* @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
* eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
* if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
*/
function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {
for (uint256 i = 0; i < _enforcedOptions.length; i++) {
// @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.
_assertOptionsType3(_enforcedOptions[i].options);
enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;
}
emit EnforcedOptionSet(_enforcedOptions);
}
/**
* @notice Combines options for a given endpoint and message type.
* @param _eid The endpoint ID.
* @param _msgType The OAPP message type.
* @param _extraOptions Additional options passed by the caller.
* @return options The combination of caller specified options AND enforced options.
*
* @dev If there is an enforced lzReceive option:
* - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}
* - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.
* @dev This presence of duplicated options is handled off-chain in the verifier/executor.
*/
function combineOptions(
uint32 _eid,
uint16 _msgType,
bytes calldata _extraOptions
) public view virtual returns (bytes memory) {
bytes memory enforced = enforcedOptions[_eid][_msgType];
// No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.
if (enforced.length == 0) return _extraOptions;
// No caller options, return enforced
if (_extraOptions.length == 0) return enforced;
// @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.
if (_extraOptions.length >= 2) {
_assertOptionsType3(_extraOptions);
// @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.
return bytes.concat(enforced, _extraOptions[2:]);
}
// No valid set of options was found.
revert InvalidOptions(_extraOptions);
}
/**
* @dev Internal function to assert that options are of type 3.
* @param _options The options to be checked.
*/
function _assertOptionsType3(bytes memory _options) internal pure virtual {
uint16 optionsType;
assembly {
optionsType := mload(add(_options, 2))
}
if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IPreCrime } from "./interfaces/IPreCrime.sol";
import { IOAppPreCrimeSimulator, InboundPacket, Origin } from "./interfaces/IOAppPreCrimeSimulator.sol";
/**
* @title OAppPreCrimeSimulator
* @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.
*/
abstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {
// The address of the preCrime implementation.
address public preCrime;
/**
* @dev Retrieves the address of the OApp contract.
* @return The address of the OApp contract.
*
* @dev The simulator contract is the base contract for the OApp by default.
* @dev If the simulator is a separate contract, override this function.
*/
function oApp() external view virtual returns (address) {
return address(this);
}
/**
* @dev Sets the preCrime contract address.
* @param _preCrime The address of the preCrime contract.
*/
function setPreCrime(address _preCrime) public virtual onlyOwner {
preCrime = _preCrime;
emit PreCrimeSet(_preCrime);
}
/**
* @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.
* @param _packets An array of InboundPacket objects representing received packets to be delivered.
*
* @dev WARNING: MUST revert at the end with the simulation results.
* @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,
* WITHOUT actually executing them.
*/
function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {
for (uint256 i = 0; i < _packets.length; i++) {
InboundPacket calldata packet = _packets[i];
// Ignore packets that are not from trusted peers.
if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;
// @dev Because a verifier is calling this function, it doesnt have access to executor params:
// - address _executor
// - bytes calldata _extraData
// preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().
// They are instead stubbed to default values, address(0) and bytes("")
// @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,
// which would cause the revert to be ignored.
this.lzReceiveSimulate{ value: packet.value }(
packet.origin,
packet.guid,
packet.message,
packet.executor,
packet.extraData
);
}
// @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().
revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());
}
/**
* @dev Is effectively an internal function because msg.sender must be address(this).
* Allows resetting the call stack for 'internal' calls.
* @param _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @param _guid The unique identifier of the packet.
* @param _message The message payload of the packet.
* @param _executor The executor address for the packet.
* @param _extraData Additional data for the packet.
*/
function lzReceiveSimulate(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) external payable virtual {
// @dev Ensure ONLY can be called 'internally'.
if (msg.sender != address(this)) revert OnlySelf();
_lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);
}
/**
* @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.
* @param _origin The origin information.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address from the src chain.
* - nonce: The nonce of the LayerZero message.
* @param _guid The GUID of the LayerZero message.
* @param _message The LayerZero message.
* @param _executor The address of the off-chain executor.
* @param _extraData Arbitrary data passed by the msg executor.
*
* @dev Enables the preCrime simulator to mock sending lzReceive() messages,
* routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.
*/
function _lzReceiveSimulate(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) internal virtual;
/**
* @dev checks if the specified peer is considered 'trusted' by the OApp.
* @param _eid The endpoint Id to check.
* @param _peer The peer to check.
* @return Whether the peer passed is considered 'trusted' by the OApp.
*/
function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";
/**
* @title OAppSender
* @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
*/
abstract contract OAppSender is OAppCore {
using SafeERC20 for IERC20;
// Custom error messages
error NotEnoughNative(uint256 msgValue);
error LzTokenUnavailable();
// @dev The version of the OAppSender implementation.
// @dev Version is bumped when changes are made to this contract.
uint64 internal constant SENDER_VERSION = 1;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
* ie. this is a SEND only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
*/
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
return (SENDER_VERSION, 0);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
* @return fee The calculated MessagingFee for the message.
* - nativeFee: The native fee for the message.
* - lzTokenFee: The LZ token fee for the message.
*/
function _quote(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
bool _payInLzToken
) internal view virtual returns (MessagingFee memory fee) {
return
endpoint.quote(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
address(this)
);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _fee The calculated LayerZero fee for the message.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess fee values sent to the endpoint.
* @return receipt The receipt for the sent message.
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function _lzSend(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
MessagingFee memory _fee,
address _refundAddress
) internal virtual returns (MessagingReceipt memory receipt) {
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
uint256 messageValue = _payNative(_fee.nativeFee);
if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);
return
// solhint-disable-next-line check-send-result
endpoint.send{ value: messageValue }(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
_refundAddress
);
}
/**
* @dev Internal function to pay the native fee associated with the message.
* @param _nativeFee The native fee to be paid.
* @return nativeFee The amount of native currency paid.
*
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
* this will need to be overridden because msg.value would contain multiple lzFees.
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
*/
function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
return _nativeFee;
}
/**
* @dev Internal function to pay the LZ token fee associated with the message.
* @param _lzTokenFee The LZ token fee to be paid.
*
* @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
* @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
*/
function _payLzToken(uint256 _lzTokenFee) internal virtual {
// @dev Cannot cache the token because it is not immutable in the endpoint.
address lzToken = endpoint.lzToken();
if (lzToken == address(0)) revert LzTokenUnavailable();
// Pay LZ token fee by sending tokens to the endpoint.
IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { IOAppReceiver, Origin } from "./interfaces/IOAppReceiver.sol";
import { OAppCore } from "./OAppCore.sol";
/**
* @title OAppReceiver
* @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.
*/
abstract contract OAppReceiver is IOAppReceiver, OAppCore {
// Custom error message for when the caller is not the registered endpoint/
error OnlyEndpoint(address addr);
// @dev The version of the OAppReceiver implementation.
// @dev Version is bumped when changes are made to this contract.
uint64 internal constant RECEIVER_VERSION = 2;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.
* ie. this is a RECEIVE only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.
*/
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
return (0, RECEIVER_VERSION);
}
/**
* @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
* @dev _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @dev _message The lzReceive payload.
* @param _sender The sender address.
* @return isSender Is a valid sender.
*
* @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.
* @dev The default sender IS the OAppReceiver implementer.
*/
function isComposeMsgSender(
Origin calldata /*_origin*/,
bytes calldata /*_message*/,
address _sender
) public view virtual returns (bool) {
return _sender == address(this);
}
/**
* @notice Checks if the path initialization is allowed based on the provided origin.
* @param origin The origin information containing the source endpoint and sender address.
* @return Whether the path has been initialized.
*
* @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.
* @dev This defaults to assuming if a peer has been set, its initialized.
* Can be overridden by the OApp if there is other logic to determine this.
*/
function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {
return peers[origin.srcEid] == origin.sender;
}
/**
* @notice Retrieves the next nonce for a given source endpoint and sender address.
* @dev _srcEid The source endpoint ID.
* @dev _sender The sender address.
* @return nonce The next nonce.
*
* @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.
* @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.
* @dev This is also enforced by the OApp.
* @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.
*/
function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {
return 0;
}
/**
* @dev Entry point for receiving messages or packets from the endpoint.
* @param _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @param _guid The unique identifier for the received LayerZero message.
* @param _message The payload of the received message.
* @param _executor The address of the executor for the received message.
* @param _extraData Additional arbitrary data provided by the corresponding executor.
*
* @dev Entry point for receiving msg/packet from the LayerZero endpoint.
*/
function lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) public payable virtual {
// Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.
if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);
// Ensure that the sender matches the expected peer for the source endpoint.
if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);
// Call the internal OApp implementation of lzReceive.
_lzReceive(_origin, _guid, _message, _executor, _extraData);
}
/**
* @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.
*/
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) internal virtual;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";
/**
* @title OAppCore
* @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
*/
abstract contract OAppCore is IOAppCore, Ownable {
// The LayerZero endpoint associated with the given OApp
ILayerZeroEndpointV2 public immutable endpoint;
// Mapping to store peers associated with corresponding endpoints
mapping(uint32 eid => bytes32 peer) public peers;
/**
* @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
* @param _endpoint The address of the LOCAL Layer Zero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*
* @dev The delegate typically should be set as the owner of the contract.
*/
constructor(address _endpoint, address _delegate) {
endpoint = ILayerZeroEndpointV2(_endpoint);
if (_delegate == address(0)) revert InvalidDelegate();
endpoint.setDelegate(_delegate);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
_setPeer(_eid, _peer);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
peers[_eid] = _peer;
emit PeerSet(_eid, _peer);
}
/**
* @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
* ie. the peer is set to bytes32(0).
* @param _eid The endpoint ID.
* @return peer The address of the peer associated with the specified endpoint.
*/
function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
bytes32 peer = peers[_eid];
if (peer == bytes32(0)) revert NoPeer(_eid);
return peer;
}
/**
* @notice Sets the delegate address for the OApp.
* @param _delegate The address of the delegate to be set.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
*/
function setDelegate(address _delegate) public onlyOwner {
endpoint.setDelegate(_delegate);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity >=0.5.0;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 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.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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 ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @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.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
}
}
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return ternary(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 {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
int256 mask = n >> 255;
// A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
return uint256((n + mask) ^ mask);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Struct representing enforced option parameters.
*/
struct EnforcedOptionParam {
uint32 eid; // Endpoint ID
uint16 msgType; // Message Type
bytes options; // Additional options
}
/**
* @title IOAppOptionsType3
* @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.
*/
interface IOAppOptionsType3 {
// Custom error message for invalid options
error InvalidOptions(bytes options);
// Event emitted when enforced options are set
event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);
/**
* @notice Sets enforced options for specific endpoint and message type combinations.
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
*/
function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;
/**
* @notice Combines options for a given endpoint and message type.
* @param _eid The endpoint ID.
* @param _msgType The OApp message type.
* @param _extraOptions Additional options passed by the caller.
* @return options The combination of caller specified options AND enforced options.
*/
function combineOptions(
uint32 _eid,
uint16 _msgType,
bytes calldata _extraOptions
) external view returns (bytes memory options);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
struct PreCrimePeer {
uint32 eid;
bytes32 preCrime;
bytes32 oApp;
}
// TODO not done yet
interface IPreCrime {
error OnlyOffChain();
// for simulate()
error PacketOversize(uint256 max, uint256 actual);
error PacketUnsorted();
error SimulationFailed(bytes reason);
// for preCrime()
error SimulationResultNotFound(uint32 eid);
error InvalidSimulationResult(uint32 eid, bytes reason);
error CrimeFound(bytes crime);
function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);
function simulate(
bytes[] calldata _packets,
uint256[] calldata _packetMsgValues
) external payable returns (bytes memory);
function buildSimulationResult() external view returns (bytes memory);
function preCrime(
bytes[] calldata _packets,
uint256[] calldata _packetMsgValues,
bytes[] calldata _simulations
) external;
function version() external view returns (uint64 major, uint8 minor);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.
// solhint-disable-next-line no-unused-import
import { InboundPacket, Origin } from "../libs/Packet.sol";
/**
* @title IOAppPreCrimeSimulator Interface
* @dev Interface for the preCrime simulation functionality in an OApp.
*/
interface IOAppPreCrimeSimulator {
// @dev simulation result used in PreCrime implementation
error SimulationResult(bytes result);
error OnlySelf();
/**
* @dev Emitted when the preCrime contract address is set.
* @param preCrimeAddress The address of the preCrime contract.
*/
event PreCrimeSet(address preCrimeAddress);
/**
* @dev Retrieves the address of the preCrime contract implementation.
* @return The address of the preCrime contract.
*/
function preCrime() external view returns (address);
/**
* @dev Retrieves the address of the OApp contract.
* @return The address of the OApp contract.
*/
function oApp() external view returns (address);
/**
* @dev Sets the preCrime contract address.
* @param _preCrime The address of the preCrime contract.
*/
function setPreCrime(address _preCrime) external;
/**
* @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.
* @param _packets An array of LayerZero InboundPacket objects representing received packets.
*/
function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;
/**
* @dev checks if the specified peer is considered 'trusted' by the OApp.
* @param _eid The endpoint Id to check.
* @param _peer The peer to check.
* @return Whether the peer passed is considered 'trusted' by the OApp.
*/
function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
struct Origin {
uint32 srcEid;
bytes32 sender;
uint64 nonce;
}
interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
event PacketDelivered(Origin origin, address receiver);
event LzReceiveAlert(
address indexed receiver,
address indexed executor,
Origin origin,
bytes32 guid,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
event LzTokenSet(address token);
event DelegateSet(address sender, address delegate);
function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
function send(
MessagingParams calldata _params,
address _refundAddress
) external payable returns (MessagingReceipt memory);
function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;
// oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
function setLzToken(address _lzToken) external;
function lzToken() external view returns (address);
function nativeToken() external view returns (address);
function setDelegate(address _delegate) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ILayerZeroReceiver, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol";
interface IOAppReceiver is ILayerZeroReceiver {
/**
* @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
* @param _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @param _message The lzReceive payload.
* @param _sender The sender address.
* @return isSender Is a valid sender.
*
* @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.
* @dev The default sender IS the OAppReceiver implementer.
*/
function isComposeMsgSender(
Origin calldata _origin,
bytes calldata _message,
address _sender
) external view returns (bool isSender);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @title IOAppCore
*/
interface IOAppCore {
// Custom error messages
error OnlyPeer(uint32 eid, bytes32 sender);
error NoPeer(uint32 eid);
error InvalidEndpointCall();
error InvalidDelegate();
// Event emitted when a peer (OApp) is set for a corresponding endpoint
event PeerSet(uint32 eid, bytes32 peer);
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*/
function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);
/**
* @notice Retrieves the LayerZero endpoint associated with the OApp.
* @return iEndpoint The LayerZero endpoint as an interface.
*/
function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);
/**
* @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
* @param _eid The endpoint ID.
* @return peer The peer address (OApp instance) associated with the corresponding endpoint.
*/
function peers(uint32 _eid) external view returns (bytes32 peer);
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*/
function setPeer(uint32 _eid, bytes32 _peer) external;
/**
* @notice Sets the delegate address for the OApp Core.
* @param _delegate The address of the delegate to be set.
*/
function setDelegate(address _delegate) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
/**
* @title InboundPacket
* @dev Structure representing an inbound packet received by the contract.
*/
struct InboundPacket {
Origin origin; // Origin information of the packet.
uint32 dstEid; // Destination endpointId of the packet.
address receiver; // Receiver address for the packet.
bytes32 guid; // Unique identifier of the packet.
uint256 value; // msg.value of the packet.
address executor; // Executor address for the packet.
bytes message; // Message payload of the packet.
bytes extraData; // Additional arbitrary data for the packet.
}
/**
* @title PacketDecoder
* @dev Library for decoding LayerZero packets.
*/
library PacketDecoder {
using PacketV1Codec for bytes;
/**
* @dev Decode an inbound packet from the given packet data.
* @param _packet The packet data to decode.
* @return packet An InboundPacket struct representing the decoded packet.
*/
function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {
packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());
packet.dstEid = _packet.dstEid();
packet.receiver = _packet.receiverB20();
packet.guid = _packet.guid();
packet.message = _packet.message();
}
/**
* @dev Decode multiple inbound packets from the given packet data and associated message values.
* @param _packets An array of packet data to decode.
* @param _packetMsgValues An array of associated message values for each packet.
* @return packets An array of InboundPacket structs representing the decoded packets.
*/
function decode(
bytes[] calldata _packets,
uint256[] memory _packetMsgValues
) internal pure returns (InboundPacket[] memory packets) {
packets = new InboundPacket[](_packets.length);
for (uint256 i = 0; i < _packets.length; i++) {
bytes calldata packet = _packets[i];
packets[i] = PacketDecoder.decode(packet);
// @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.
packets[i].value = _packetMsgValues[i];
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
struct SetConfigParam {
uint32 eid;
uint32 configType;
bytes config;
}
interface IMessageLibManager {
struct Timeout {
address lib;
uint256 expiry;
}
event LibraryRegistered(address newLib);
event DefaultSendLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
event SendLibrarySet(address sender, uint32 eid, address newLib);
event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
function registerLibrary(address _lib) external;
function isRegisteredLibrary(address _lib) external view returns (bool);
function getRegisteredLibraries() external view returns (address[] memory);
function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
function defaultSendLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _timeout) external;
function defaultReceiveLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
function isSupportedEid(uint32 _eid) external view returns (bool);
function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
/// ------------------- OApp interfaces -------------------
function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _gracePeriod) external;
function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
function getConfig(
address _oapp,
address _lib,
uint32 _eid,
uint32 _configType
) external view returns (bytes memory config);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingComposer {
event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
event LzComposeAlert(
address indexed from,
address indexed to,
address indexed executor,
bytes32 guid,
uint16 index,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
function composeQueue(
address _from,
address _to,
bytes32 _guid,
uint16 _index
) external view returns (bytes32 messageHash);
function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingChannel {
event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
function eid() external view returns (uint32);
// this is an emergency function if a message cannot be verified for some reasons
// required to provide _nextNonce to avoid race condition
function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
function inboundPayloadHash(
address _receiver,
uint32 _srcEid,
bytes32 _sender,
uint64 _nonce
) external view returns (bytes32);
function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingContext {
function isSendingMessage() external view returns (bool);
function getSendContext() external view returns (uint32 dstEid, address sender);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { Origin } from "./ILayerZeroEndpointV2.sol";
interface ILayerZeroReceiver {
function allowInitializePath(Origin calldata _origin) external view returns (bool);
function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);
function lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) external payable;
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import { Packet } from "../../interfaces/ISendLib.sol";
import { AddressCast } from "../../libs/AddressCast.sol";
library PacketV1Codec {
using AddressCast for address;
using AddressCast for bytes32;
uint8 internal constant PACKET_VERSION = 1;
// header (version + nonce + path)
// version
uint256 private constant PACKET_VERSION_OFFSET = 0;
// nonce
uint256 private constant NONCE_OFFSET = 1;
// path
uint256 private constant SRC_EID_OFFSET = 9;
uint256 private constant SENDER_OFFSET = 13;
uint256 private constant DST_EID_OFFSET = 45;
uint256 private constant RECEIVER_OFFSET = 49;
// payload (guid + message)
uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)
uint256 private constant MESSAGE_OFFSET = 113;
function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {
encodedPacket = abi.encodePacked(
PACKET_VERSION,
_packet.nonce,
_packet.srcEid,
_packet.sender.toBytes32(),
_packet.dstEid,
_packet.receiver,
_packet.guid,
_packet.message
);
}
function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {
return
abi.encodePacked(
PACKET_VERSION,
_packet.nonce,
_packet.srcEid,
_packet.sender.toBytes32(),
_packet.dstEid,
_packet.receiver
);
}
function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {
return abi.encodePacked(_packet.guid, _packet.message);
}
function header(bytes calldata _packet) internal pure returns (bytes calldata) {
return _packet[0:GUID_OFFSET];
}
function version(bytes calldata _packet) internal pure returns (uint8) {
return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));
}
function nonce(bytes calldata _packet) internal pure returns (uint64) {
return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));
}
function srcEid(bytes calldata _packet) internal pure returns (uint32) {
return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));
}
function sender(bytes calldata _packet) internal pure returns (bytes32) {
return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);
}
function senderAddressB20(bytes calldata _packet) internal pure returns (address) {
return sender(_packet).toAddress();
}
function dstEid(bytes calldata _packet) internal pure returns (uint32) {
return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));
}
function receiver(bytes calldata _packet) internal pure returns (bytes32) {
return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);
}
function receiverB20(bytes calldata _packet) internal pure returns (address) {
return receiver(_packet).toAddress();
}
function guid(bytes calldata _packet) internal pure returns (bytes32) {
return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);
}
function message(bytes calldata _packet) internal pure returns (bytes calldata) {
return bytes(_packet[MESSAGE_OFFSET:]);
}
function payload(bytes calldata _packet) internal pure returns (bytes calldata) {
return bytes(_packet[GUID_OFFSET:]);
}
function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {
return keccak256(payload(_packet));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { MessagingFee } from "./ILayerZeroEndpointV2.sol";
import { IMessageLib } from "./IMessageLib.sol";
struct Packet {
uint64 nonce;
uint32 srcEid;
address sender;
uint32 dstEid;
bytes32 receiver;
bytes32 guid;
bytes message;
}
interface ISendLib is IMessageLib {
function send(
Packet calldata _packet,
bytes calldata _options,
bool _payInLzToken
) external returns (MessagingFee memory, bytes memory encodedPacket);
function quote(
Packet calldata _packet,
bytes calldata _options,
bool _payInLzToken
) external view returns (MessagingFee memory);
function setTreasury(address _treasury) external;
function withdrawFee(address _to, uint256 _amount) external;
function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
library AddressCast {
error AddressCast_InvalidSizeForAddress();
error AddressCast_InvalidAddress();
function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {
if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();
result = bytes32(_addressBytes);
unchecked {
uint256 offset = 32 - _addressBytes.length;
result = result >> (offset * 8);
}
}
function toBytes32(address _address) internal pure returns (bytes32 result) {
result = bytes32(uint256(uint160(_address)));
}
function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {
if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();
result = new bytes(_size);
unchecked {
uint256 offset = 256 - _size * 8;
assembly {
mstore(add(result, 32), shl(offset, _addressBytes32))
}
}
}
function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {
result = address(uint160(uint256(_addressBytes32)));
}
function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {
if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();
result = address(bytes20(_addressBytes));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { SetConfigParam } from "./IMessageLibManager.sol";
enum MessageLibType {
Send,
Receive,
SendAndReceive
}
interface IMessageLib is IERC165 {
function setConfig(address _oapp, SetConfigParam[] calldata _config) external;
function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);
function isSupportedEid(uint32 _eid) external view returns (bool);
// message libs of same major version are compatible
function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);
function messageLibType() external view returns (MessageLibType);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"@layerzerolabs/onft-evm/=lib/devtools/packages/onft-evm/",
"@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/",
"@layerzerolabs/lz-evm-protocol-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/protocol/",
"@layerzerolabs/lz-evm-messagelib-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/messagelib/",
"solidity-bytes-utils/=lib/solidity-bytes-utils/",
"LayerZero-v2/=lib/LayerZero-v2/",
"devtools/=lib/devtools/packages/toolbox-foundry/src/",
"ds-test/=lib/LayerZero-v2/lib/forge-std/lib/ds-test/src/"
],
"optimizer": {
"enabled": true,
"runs": 800
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"_lzEndpoint","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AttemptingToSetExistingMintPrice","type":"error"},{"inputs":[],"name":"CanOnlyFuseBaseTokens","type":"error"},{"inputs":[],"name":"CanOnlyUnfuseFusedTokens","type":"error"},{"inputs":[],"name":"CannotTransferSoulboundToken","type":"error"},{"inputs":[],"name":"ERC721EnumerableForbiddenBatchMint","type":"error"},{"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":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ERC721OutOfBoundsIndex","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"GiftMintingLimitReached","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidDelegate","type":"error"},{"inputs":[],"name":"InvalidEndpointCall","type":"error"},{"inputs":[],"name":"InvalidGiftMintPrice","type":"error"},{"inputs":[{"internalType":"bytes","name":"options","type":"bytes"}],"name":"InvalidOptions","type":"error"},{"inputs":[],"name":"InvalidRange","type":"error"},{"inputs":[],"name":"InvalidReceiver","type":"error"},{"inputs":[],"name":"LzTokenUnavailable","type":"error"},{"inputs":[],"name":"MintingLimitReached","type":"error"},{"inputs":[],"name":"MintingPeriodOver","type":"error"},{"inputs":[],"name":"MissingComposedMessage","type":"error"},{"inputs":[{"internalType":"uint32","name":"eid","type":"uint32"}],"name":"NoPeer","type":"error"},{"inputs":[{"internalType":"uint256","name":"msgValue","type":"uint256"}],"name":"NotEnoughNative","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"OnlyEndpoint","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"OnlyNFTOwner","type":"error"},{"inputs":[{"internalType":"uint32","name":"eid","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"}],"name":"OnlyPeer","type":"error"},{"inputs":[],"name":"OnlySelf","type":"error"},{"inputs":[],"name":"OnlyTokenOwnerCanBurnTheToken","type":"error"},{"inputs":[],"name":"OnlyTokenOwnerCanFuseTokens","type":"error"},{"inputs":[],"name":"OnlyTokenOwnerCanUnfuseTokens","type":"error"},{"inputs":[],"name":"OutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SameTokenUnderlyingAssets","type":"error"},{"inputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"name":"SimulationResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TokensCanOnlyBeDepositedByNftMint","type":"error"},{"inputs":[],"name":"TokensCanOnlyBeRetrievedByNftBurn","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UnfuseTokenBeforeBurning","type":"error"},{"inputs":[],"name":"UnsupportedToken","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":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint32","name":"eid","type":"uint32"},{"internalType":"uint16","name":"msgType","type":"uint16"},{"internalType":"bytes","name":"options","type":"bytes"}],"indexed":false,"internalType":"struct EnforcedOptionParam[]","name":"_enforcedOptions","type":"tuple[]"}],"name":"EnforcedOptionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Contract","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Contract","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"FeesRetrieved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousMintPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMintPrice","type":"uint256"}],"name":"GiftMintPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintPrice","type":"uint256"}],"name":"GiftMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Contract","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousMintPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMintPrice","type":"uint256"}],"name":"MintPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousCutoffBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCutoffBlock","type":"uint256"}],"name":"MintingCutoffBlockUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousFeeBasisPoints","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFeeBasisPoints","type":"uint256"}],"name":"MintingFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"inspector","type":"address"}],"name":"MsgInspectorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NewSoulboundToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"guid","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"srcEid","type":"uint32"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ONFTReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"guid","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"dstEid","type":"uint32"},{"indexed":true,"internalType":"address","name":"fromAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ONFTSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"eid","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"peer","type":"bytes32"}],"name":"PeerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"preCrimeAddress","type":"address"}],"name":"PreCrimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Contract","type":"address"},{"indexed":true,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceivedERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"underlyingToken1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingToken2","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingToken3","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingToken4","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premiumTokenId","type":"uint256"}],"name":"TokenFused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"underlyingToken1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingToken2","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingToken3","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingToken4","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premiumTokenId","type":"uint256"}],"name":"TokenUnfused","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Contract","type":"address"},{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferredERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"MAX_BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SEND","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SEND_AND_COMPOSE","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WFRAX_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"srcEid","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct Origin","name":"origin","type":"tuple"}],"name":"allowInitializePath","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"approvalRequired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"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":"address","name":"_erc20Contract","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"erc20","type":"address"}],"name":"baseAssetTokenUris","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"erc20","type":"address"}],"name":"collectedFees","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_eid","type":"uint32"},{"internalType":"uint16","name":"_msgType","type":"uint16"},{"internalType":"bytes","name":"_extraOptions","type":"bytes"}],"name":"combineOptions","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endpoint","outputs":[{"internalType":"contract ILayerZeroEndpointV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"eid","type":"uint32"},{"internalType":"uint16","name":"msgType","type":"uint16"}],"name":"enforcedOptions","outputs":[{"internalType":"bytes","name":"enforcedOption","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"erc20","type":"address"}],"name":"erc20Balances","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"erc20TransferOutNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId1","type":"uint256"},{"internalType":"uint256","name":"_tokenId2","type":"uint256"},{"internalType":"uint256","name":"_tokenId3","type":"uint256"},{"internalType":"uint256","name":"_tokenId4","type":"uint256"}],"name":"fuseTokens","outputs":[{"internalType":"uint256","name":"premiumTokenId_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGiftMintingPriceWithFee","outputs":[{"internalType":"uint256","name":"mintPrice_","type":"uint256"},{"internalType":"uint256","name":"fee_","type":"uint256"},{"internalType":"uint256","name":"totalPrice_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"}],"name":"getMintingPriceWithFee","outputs":[{"internalType":"uint256","name":"mintPrice_","type":"uint256"},{"internalType":"uint256","name":"fee_","type":"uint256"},{"internalType":"uint256","name":"totalPrice_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupportedErc20s","outputs":[{"internalType":"address[]","name":"erc20Contracts_","type":"address[]"},{"internalType":"uint256[]","name":"mintPricesOut_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getUnderlyingBalances","outputs":[{"internalType":"address[]","name":"erc20Contracts_","type":"address[]"},{"internalType":"uint256[]","name":"balances_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_premiumTokenId","type":"uint256"}],"name":"getUnderlyingTokenIds","outputs":[{"internalType":"uint256","name":"tokenId1_","type":"uint256"},{"internalType":"uint256","name":"tokenId2_","type":"uint256"},{"internalType":"uint256","name":"tokenId3_","type":"uint256"},{"internalType":"uint256","name":"tokenId4_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"giftMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"giftMintingLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"giftMintingPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"components":[{"internalType":"uint32","name":"srcEid","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct Origin","name":"","type":"tuple"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"address","name":"_sender","type":"address"}],"name":"isComposeMsgSender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isNonTransferrable","outputs":[{"internalType":"bool","name":"nonTransferable","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_eid","type":"uint32"},{"internalType":"bytes32","name":"_peer","type":"bytes32"}],"name":"isPeer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"isTransferable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"srcEid","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct Origin","name":"_origin","type":"tuple"},{"internalType":"bytes32","name":"_guid","type":"bytes32"},{"internalType":"bytes","name":"_message","type":"bytes"},{"internalType":"address","name":"_executor","type":"address"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"lzReceive","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint32","name":"srcEid","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct Origin","name":"origin","type":"tuple"},{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct InboundPacket[]","name":"_packets","type":"tuple[]"}],"name":"lzReceiveAndRevert","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"srcEid","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct Origin","name":"_origin","type":"tuple"},{"internalType":"bytes32","name":"_guid","type":"bytes32"},{"internalType":"bytes","name":"_message","type":"bytes"},{"internalType":"address","name":"_executor","type":"address"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"lzReceiveSimulate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"erc20","type":"address"}],"name":"mintPrices","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintingCutoffBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintingFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintingLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"msgInspector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextGiftTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nextNonce","outputs":[{"internalType":"uint64","name":"nonce","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextPremiumTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oApp","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oAppVersion","outputs":[{"internalType":"uint64","name":"senderVersion","type":"uint64"},{"internalType":"uint64","name":"receiverVersion","type":"uint64"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"onftVersion","outputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"},{"internalType":"uint64","name":"version","type":"uint64"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"}],"name":"paidMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"eid","type":"uint32"}],"name":"peers","outputs":[{"internalType":"bytes32","name":"peer","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"preCrime","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"onftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"_sendParam","type":"tuple"},{"internalType":"bool","name":"_payInLzToken","type":"bool"}],"name":"quoteSend","outputs":[{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"msgFee","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_firstTokenId","type":"uint256"},{"internalType":"uint256","name":"_lastTokenId","type":"uint256"}],"name":"refreshBaseTokenUris","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_firstTokenId","type":"uint256"},{"internalType":"uint256","name":"_lastTokenId","type":"uint256"}],"name":"refreshGiftTokenUris","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_firstTokenId","type":"uint256"},{"internalType":"uint256","name":"_lastTokenId","type":"uint256"}],"name":"refreshPremiumTokenUris","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"retrieveCollectedFees","outputs":[],"stateMutability":"nonpayable","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":[{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"onftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"_sendParam","type":"tuple"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"_fee","type":"tuple"},{"internalType":"address","name":"_refundAddress","type":"address"}],"name":"send","outputs":[{"components":[{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"fee","type":"tuple"}],"internalType":"struct MessagingReceipt","name":"msgReceipt","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"},{"internalType":"string","name":"_uri","type":"string"}],"name":"setBaseAssetTokenUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"}],"name":"setDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"eid","type":"uint32"},{"internalType":"uint16","name":"msgType","type":"uint16"},{"internalType":"bytes","name":"options","type":"bytes"}],"internalType":"struct EnforcedOptionParam[]","name":"_enforcedOptions","type":"tuple[]"}],"name":"setEnforcedOptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setGiftTokenUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_msgInspector","type":"address"}],"name":"setMsgInspector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_eid","type":"uint32"},{"internalType":"bytes32","name":"_peer","type":"bytes32"}],"name":"setPeer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_preCrime","type":"address"}],"name":"setPreCrime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setPremiumTokenUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"string","name":"_tokenUri","type":"string"}],"name":"soulboundMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"supportedErc20s","outputs":[{"internalType":"address","name":"erc20","type":"address"}],"stateMutability":"view","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":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenTypes","outputs":[{"internalType":"enum Fraxiversary.TokenType","name":"tokenType","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNumberOfSupportedErc20s","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferERC20ToToken","outputs":[],"stateMutability":"pure","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"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferHeldERC20FromToken","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferOutNonces","outputs":[{"internalType":"uint256","name":"transferOutNonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"underlyingAssets","outputs":[{"internalType":"address","name":"assets","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"underlyingTokenIds","outputs":[{"internalType":"uint256","name":"underlyingTokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_premiumTokenId","type":"uint256"}],"name":"unfuseTokens","outputs":[{"internalType":"uint256","name":"tokenId1_","type":"uint256"},{"internalType":"uint256","name":"tokenId2_","type":"uint256"},{"internalType":"uint256","name":"tokenId3_","type":"uint256"},{"internalType":"uint256","name":"tokenId4_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Contract","type":"address"},{"internalType":"uint256","name":"_mintPrice","type":"uint256"}],"name":"updateBaseAssetMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPrice","type":"uint256"}],"name":"updateGiftMintingPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newCutoffBlock","type":"uint256"}],"name":"updateMintingCutoffBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFeeBasisPoints","type":"uint256"}],"name":"updateMintingFeeBasisPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"string","name":"_uri","type":"string"}],"name":"updateSpecificTokenUri","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a08060405234610390575f60408261637c8038038091610020828561053d565b8339810103126103905761003f602061003884610560565b9301610560565b916040519261004f60408561053d565b600c84526b46726178697665727361727960a01b60208501526040519361007760408661053d565b600685526546524158355960d01b60208601528051906001600160401b03821161046e5781906100a75f54610574565b601f81116104f0575b50602090601f831160011461048d575f92610482575b50508160011b915f199060031b1c1916175f555b83516001600160401b03811161046e576100f5600154610574565b601f8111610415575b50602094601f82116001146103b2579481929394955f926103a7575b50508160011b915f199060031b1c1916176001555b6001600160a01b03821690811561039457600b8054610100600160a81b03198116600895861b610100600160a81b031617909155604051938391901c6001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36001600160a01b03166080819052803b15610390576024835f8193819563ca5eb5e160e01b845260048401525af1801561038557610372575b50612ee0601e5561c350601f556802b5e3af16b1880000602055612ee0601b5561f230601c55601960215562171240430180431161035e5760207f386567463867667172434a7a34676d645643305878724e38774f3944755751009260019260225561023e602454610574565b601f811161033c575b50607f602455602481527f56736865797a314d434f3836795a6e544665466f5f5f473667754d71374f4500838383207f68747470733a2f2f617277656176652e6e65742f6776326768657854346c334c815501556102a6602554610574565b601f811161031a575b50607f60255560258152207f68747470733a2f2f617277656176652e6e65742f2d707943775a4269536e343981550155604051615db990816105c38239608051818181610bae01528181611ed001528181613529015281816135f90152818161481101526158b50152f35b6025825282822061033691601f0160051c8101906002016105ac565b5f6102af565b6024825282822061035891601f0160051c8101906002016105ac565b5f610247565b634e487b7160e01b82526011600452602482fd5b61037e91505f9061053d565b5f5f6101d1565b6040513d5f823e3d90fd5b5f80fd5b631e4fbdf760e01b5f525f60045260245ffd5b015190505f8061011a565b601f1982169560015f52805f20915f5b8881106103fd575083600195969798106103e5575b505050811b0160015561012f565b01515f1960f88460031b161c191690555f80806103d7565b919260206001819286850151815501940192016103c2565b60015f5261045e907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c81019160208510610464575b601f0160051c01906105ac565b5f6100fe565b9091508190610451565b634e487b7160e01b5f52604160045260245ffd5b015190505f806100c6565b5f8080528281209350601f198516905b8181106104d857509084600195949392106104c0575b505050811b015f556100da565b01515f1960f88460031b161c191690555f80806104b3565b9293602060018192878601518155019501930161049d565b5f8052610537907f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563601f850160051c8101916020861061046457601f0160051c01906105ac565b5f6100b0565b601f909101601f19168101906001600160401b0382119082101761046e57604052565b51906001600160a01b038216820361039057565b90600182811c921680156105a2575b602083101461058e57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610583565b8181106105b7575050565b5f81556001016105ac56fe60806040526004361015610011575f80fd5b5f3560e01c806283fc9d1461062957806301ffc9a71461062457806306fdde031461061f578063081812fc1461061a578063095ea7b3146106155780630a3f6fda14610610578063111ecdad1461060b57806313137d651461060657806313a8ae8c1461060157806317442b70146105fc57806318160ddd146105f75780631a0c131c146105f25780631cead9a7146105ed5780631f5e1334146105e857806321eb730b146105e357806323b872dd146105de5780632d7ee3b8146105d95780632e49ac6d146105d45780632eea49d9146105cf5780632f745c59146105ca578063335c9701146105c557806333f6832a146105c05780633400288b146105bb5780633811a5e5146105b65780633bd7a51c146105b15780633f4ba83a146105ac57806342842e0e146105a757806342966c68146105a257806349072f121461059d5780634f6ccce714610598578063501ecded14610593578063523d7c161461058e57806352ae28791461045357806354af740c146105895780635535d46114610584578063582f706f1461057f5780635a0dfe4d1461057a5780635a5dceef146105755780635c975abb146105705780635e280f111461056b5780635f71c3bf146105665780636352211e1461056157806364cae70d1461055c57806369ac032d146105575780636c2c9611146105525780636fc1b31e1461054d57806370a0823114610548578063715018a61461054357806375794a3c1461053e5780637779fa00146105395780637819a585146104df5780637caab700146105345780637d25a05e1461052f57806382413eac1461052a5780638456cb5914610525578063866ef715146105205780638da5cb5b1461051b57806390de2d531461051657806391a6262f14610511578063933e06671461050c57806395d89b411461050757806395ef7415146105025780639ca4df96146104fd5780639f68b964146104f8578063a1eb9aaf146104f3578063a22cb465146104ee578063a2e4a7b8146104e9578063a72f5dd8146104e4578063a869de0a146104df578063ab1d27a4146104da578063b21a33e4146104d5578063b27e3d80146104d0578063b5bf95f9146104cb578063b7144f6e146104c6578063b731ea0a146104c1578063b88d4fde146104bc578063b93a2254146104b7578063b98bd070146104b2578063bb0b6a53146104ad578063bb4a5fa7146104a8578063bc70b354146104a3578063bd815db01461049e578063c6414e7b14610499578063c87b56dd14610494578063ca5eb5e11461048f578063d045a0dc1461048a578063d424388514610485578063de5e54c114610480578063e95a71ae1461047b578063e985e9c514610476578063ebc9ed2114610471578063f2a137211461046c578063f2fde38b14610467578063f43f7bba14610462578063f4ea93d81461045d578063f7f4b2eb14610458578063fc0c546a146104535763ff7bd03d1461044e575f80fd5b613b41565b611a3f565b613b05565b613ae9565b613a9d565b6139e6565b6139a5565b6138bf565b613874565b61377a565b613734565b6136a2565b613662565b6135c9565b6135aa565b61345e565b613303565b6132ae565b613291565b613258565b613184565b613069565b61300c565b612fe6565b612fca565b612f73565b612d72565b612d57565b612d3a565b6126c1565b612d13565b612cc2565b612bdb565b612aa2565b612a88565b612a0f565b6129ca565b612925565b612908565b6128c4565b6128a7565b61287e565b612830565b6127d7565b612778565b612752565b6126eb565b612689565b61266c565b612602565b6125d3565b612567565b612480565b612454565b611ffa565b611fdc565b611ef4565b611eb1565b611e8f565b611d31565b611ceb565b611cce565b611c53565b611a92565b6119ac565b61198d565b611902565b6118e5565b6117ac565b611777565b611711565b6116f0565b6116bd565b61163d565b6115f9565b611569565b6114ef565b61148a565b611396565b61104f565b611038565b610e88565b610e22565b610de6565b610dc9565b610dac565b610d8b565b610d37565b610b96565b610ac2565b610a40565b6108bd565b61087d565b6107aa565b61066e565b34610658576020366003190112610658576004355f526018602052602060ff60405f2054166040519015158152f35b5f80fd5b6001600160e01b031981160361065857565b34610658576020366003190112610658576106d860043561068e8161065c565b63ffffffff60e01b16632483248360e11b8114908115610718575b8115610707575b81156106f6575b81156106ed575b81156106dc575b5060405190151581529081906020820190565b0390f35b6311f0c6d360e11b1490505f6106c5565b801591506106be565b6391a6262f60e01b811491506106b7565b631be1f1d760e21b811491506106b0565b905063780e9d6360e01b81148015610731575b906106a9565b506380ac58cd60e01b81148015610758575b8061072b57506301ffc9a760e01b811461072b565b50635b5e139f60e01b8114610743565b5f91031261065857565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060206107a7928181520190610772565b90565b34610658575f366003190112610658576040515f5f546107c981611b7f565b808452906001811690811561085957506001146107fd575b6106d8836107f1818503826112cd565b60405191829182610796565b5f8080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563939250905b80821061083f575090915081016020016107f16107e1565b919260018160209254838588010152019101909291610827565b60ff191660208086019190915291151560051b840190910191506107f190506107e1565b3461065857602036600319011261065857602061089b600435613b86565b6001600160a01b0360405191168152f35b6001600160a01b0381160361065857565b34610658576040366003190112610658576004356108da816108ac565b602435906108e78261444a565b331515806109b2575b80610972575b61095f578261095d93610942926001600160a01b0380861691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a45f52600460205260405f2090565b906001600160a01b03166001600160a01b0319825416179055565b005b63a9fbf51f60e01b5f523360045260245ffd5b5060ff6109aa33610994846001600160a01b03165f52600560205260405f2090565b906001600160a01b03165f5260205260405f2090565b5416156108f6565b50336001600160a01b03821614156108f0565b604081016040825282518091526020606083019301905f5b818110610a21575050506020818303910152602080835192838152019201905f5b818110610a0b5750505090565b82518452602093840193909201916001016109fe565b82516001600160a01b03168552602094850194909201916001016109dd565b34610658575f36600319011261065857601d54610a5c81613c1f565b610a6582613c1f565b915f5b818110610a805750506106d8604051928392836109c5565b806001915f5260136020526001600160a01b0360405f20541680610aa48387613c5e565b525f52601060205260405f2054610abb8287613c5e565b5201610a68565b34610658575f3660031901126106585760206001600160a01b03600f5416604051908152f35b606090600319011261065857600490565b9181601f840112156106585782359167ffffffffffffffff8311610658576020838186019501011161065857565b90600319820160e0811261065857606013610658576004916064359160843567ffffffffffffffff81116106585782610b6291600401610af9565b9290929160a435610b72816108ac565b9160c4359067ffffffffffffffff821161065857610b9291600401610af9565b9091565b610b9f36610b27565b50505092336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001603610d24578035610bde8161162f565b610be78161447d565b906020830135809203610cff57505083602011610658576001600160a01b03823516610c138584615794565b94610c25610c218260401090565b1590565b610cf057610cc96001600160a01b0393610cc4610ca1610c85610c6c610ceb967f7883fa30ea56937810e36990b0bbb8d629d0cf59f68baf8431ff657cebe7eef59a6157a3565b604060208251601f1981018285015283010191016144ad565b9190610c9085613c72565b50610c9b8c896157bf565b8b614c96565b610cb38a5f52601860205260405f2090565b9060ff801983541691151516179055565b613c72565b956040519384931696836020909392919363ffffffff60408201951681520152565b0390a3005b631e071f7b60e01b5f5260045ffd5b80610d0e63ffffffff9261162f565b63309afaf360e21b5f521660045260245260445ffd5b6391ac5e4f60e01b5f523360045260245ffd5b34610658575f36600319011261065857602054612710610d5960215483613c90565b0490818101808211610d86576106d890604051938493846040919493926060820195825260208201520152565b613c7c565b34610658575f36600319011261065857604080516001815260026020820152f35b34610658575f366003190112610658576020600854604051908152f35b34610658575f366003190112610658576020601b54604051908152f35b34610658576020366003190112610658576001600160a01b03600435610e0b816108ac565b165f526012602052602060405f2054604051908152f35b34610658575f36600319011261065857602060405160018152f35b908160c09103126106585790565b610e869092919260408060808301958051845267ffffffffffffffff6020820151166020850152015191019060208091805184520151910152565b565b60803660031901126106585760043567ffffffffffffffff811161065857610eb4903690600401610e3d565b60403660231901126106585760643590610ecd826108ac565b610ed5613cd6565b50610edf8161454e565b9290604083013593610ef1843561162f565b610efa8561444a565b6001600160a01b038116803314159081610fda575b5080610fbf575b610fa85791859391610f2e6106d897610f4795615873565b610f3786613c72565b91610f4136613cfc565b9261478c565b917f986156872b2ee0022b9585231dbbfde457f87f8a16b6c45e1a81c54c4ad8351f610f99610f77855193613c72565b936040519182913396836020909392919363ffffffff60408201951681520152565b0390a360405191829182610e4b565b8563177e802f60e01b5f523360045260245260445ffd5b50336001600160a01b03610fd288613b86565b161415610f16565b90505f52600560205260ff6110033360405f20906001600160a01b03165f5260205260405f2090565b5416155f610f0f565b606090600319011261065857600435611024816108ac565b90602435611031816108ac565b9060443590565b346106585761095d6110493661100c565b91613d24565b346106585760203660031901126106585760043561106c8161444a565b6001600160a01b033391160361126d57600261109a611093835f52601960205260405f2090565b5460ff1690565b6110a3816115ef565b0361125e576110ca6110bd825f52601760205260405f2090565b5f805260205260405f2090565b546106d86110f16110e3845f52601760205260405f2090565b60015f5260205260405f2090565b5492611116611108825f52601760205260405f2090565b60025f5260205260405f2090565b5461113a61112c835f52601760205260405f2090565b60035f5260205260405f2090565b549161114581614b78565b61116461115a825f52601960205260405f2090565b805460ff19169055565b5f61117a6110bd835f52601760205260405f2090565b555f6111916110e3835f52601760205260405f2090565b555f6111a8611108835f52601760205260405f2090565b555f6111bf61112c835f52601760205260405f2090565b556111cb308633614ad0565b506111d7308733614ad0565b506111e3308333614ad0565b506111ef308433614ad0565b50604080518681526020810188905290810183905260608101849052608081019190915233907fd980190ee4051835382cf91aa287f46742f3e33b24b5775cbc6c7b7692f6e1999060a090a2604051948594859094939260609260808301968352602083015260408201520152565b6310f0a8ed60e21b5f5260045ffd5b630fe6876960e21b5f5260045ffd5b634e487b7160e01b5f52604160045260245ffd5b6060810190811067ffffffffffffffff8211176112ac57604052565b61127c565b6040810190811067ffffffffffffffff8211176112ac57604052565b90601f8019910116810190811067ffffffffffffffff8211176112ac57604052565b60405190610e8660a0836112cd565b67ffffffffffffffff81116112ac57601f01601f191660200190565b929192611326826112fe565b9161133460405193846112cd565b829481845281830111610658578281602093845f960137010152565b9080601f83011215610658578160206107a79335910161131a565b6020600319820112610658576004359067ffffffffffffffff8211610658576107a791600401611350565b34610658576113a43661136b565b6113ac614c6c565b805167ffffffffffffffff81116112ac576113d1816113cc602454611b7f565b613d9c565b602091601f821160011461140f576113ff925f9183611404575b50508160011b915f199060031b1c19161790565b602455005b015190505f806113eb565b60245f52601f198216927f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec4915f5b8581106114725750836001951061145a575b505050811b01602455005b01515f1960f88460031b161c191690555f808061144f565b9192602060018192868501518155019401920161143d565b34610658576020366003190112610658577fef38f84b2df3a68028b4b01a4234d8a062db34f9184c55c8ca7c99777657be126004356114c7614c6c565b602254816022556114ea6040519283928360209093929193604081019481520152565b0390a1005b346106585760403660031901126106585760043561150c816108ac565b6001600160a01b0360243591611521816140e6565b83101561155357165f52600660205260405f20905f526020526106d860405f2054604051918291829190602083019252565b63295f44f760e21b5f521660045260245260445ffd5b346106585760403660031901126106585760043560243567ffffffffffffffff81116106585761159d903690600401611350565b6115a5614c6c565b815f52601960205260ff60405f20541660058110156115db57156115cc5761095d91614c96565b63677510db60e11b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b600511156115db57565b34610658576020366003190112610658576004355f52601960205260ff60405f20541660405160058210156115db576020918152f35b63ffffffff81160361065857565b34610658576040366003190112610658577f238399d427b947898edb290f5ff0f9109849b1c3ba196a42e35f00c50a54b98b60043561167b8161162f565b60243590611687614c6c565b63ffffffff81165f52600c6020528160405f20556114ea604051928392836020909392919363ffffffff60408201951681520152565b34610658576020366003190112610658576004355f52601360205260206001600160a01b0360405f205416604051908152f35b34610658575f3660031901126106585760206040516002603f609a1b018152f35b34610658575f36600319011261065857611729614c6c565b600b5460ff8116156117685760ff1916600b557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b638dfc202b60e01b5f5260045ffd5b346106585761095d6117883661100c565b90604051926117986020856112cd565b5f84526117a6838383613d24565b3361529b565b34610658576020366003190112610658576004356117d86117cc8261444a565b6001600160a01b031690565b33036118d65760026117f5611093835f52601960205260405f2090565b6117fe816115ef565b146118c75761181d611818825f52601460205260405f2090565b613e4f565b611837611832835f52601460205260405f2090565b613ea0565b5f5b81518110156118a957806118a361186261185560019486613c5e565b516001600160a01b031690565b611899611877875f52601660205260405f2090565b6118846118558689613c5e565b6001600160a01b03165f5260205260405f2090565b5490863391614dcd565b01611839565b61095d61115a846118b981614ea2565b5f52601960205260405f2090565b639b2b689d60e01b5f5260045ffd5b637e9ce1f160e11b5f5260045ffd5b34610658575f366003190112610658576020601e54604051908152f35b34610658576020366003190112610658576004356008548110156119525760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30154604051908152602090f35b63295f44f760e21b5f525f60045260245260445ffd5b604090600319011261065857600435611980816108ac565b906024356107a7816108ac565b346106585761095d61199e36611968565b906119a7614c6c565b613ec1565b34610658576020366003190112610658576004356119c8614c6c565b670de0b6b3a7640000811115611a305760205490808214611a2257602081815560408051938452908301919091527f961a8c1a9f6deb049129c6604411f01cff3f6ca788a5b61860a80411982381fd9190819081016114ea565b62cd406d60e71b5f5260045ffd5b637579f1fb60e11b5f5260045ffd5b34610658575f366003190112610658576020604051308152f35b90604060031983011261065857600435611a72816108ac565b916024359067ffffffffffffffff8211610658576107a791600401611350565b3461065857611aa036611a59565b611aa8614c6c565b611abf601c5491611ab98385614ee9565b82614c96565b805f52601860205260405f20600160ff19825416179055805f526019602052611af260405f20600360ff19825416179055565b601c549160018301809311610d8657611b0d6106d893601c55565b7fcf53c6969408dccd2090c6cfc96c732645840f810d24fc44cd41fa80583a52df6001600160a01b0360405192169180611b4c85829190602083019252565b0390a26040519081529081906020820190565b6024359061ffff8216820361065857565b359061ffff8216820361065857565b90600182811c92168015611bad575b6020831014611b9957565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611b8e565b5f9291815491611bc683611b7f565b8083529260018116908115611c1b5750600114611be257505050565b5f9081526020812093945091925b838310611c01575060209250010190565b600181602092949394548385870101520191019190611bf0565b915050602093945060ff929192191683830152151560051b010190565b90610e86611c4c9260405193848092611bb7565b03836112cd565b34610658576040366003190112610658576106d8611cb3611cba611ca7600435611c7c8161162f565b63ffffffff611c89611b5f565b91165f52600e60205260405f209061ffff165f5260205260405f2090565b60405192838092611bb7565b03826112cd565b604051918291602083526020830190610772565b34610658575f366003190112610658576020602154604051908152f35b34610658576040366003190112610658576020611d27600435611d0d8161162f565b6024359063ffffffff165f52600c60205260405f20541490565b6040519015158152f35b3461065857604036600319011261065857600435602435611d50614c6c565b818110611e8057601a54811015611e7157815b81811115611da757506040805192835260208301919091527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190819081016114ea565b806001611dc061109382945f52601960205260405f2090565b611dc9816115ef565b03611e6a57611dfe611deb611de6835f52601460205260405f2090565b612c96565b90546001600160a01b039160031b1c1690565b6001600160a01b038116151580611e4b575b611e1f575b5001915b91611d63565b611ab9611e40611e45926001600160a01b03165f52601160205260405f2090565b611c38565b5f611e15565b50611e6281610994845f52601660205260405f2090565b541515611e10565b0191611e19565b632d0483c560e21b5f5260045ffd5b63561ce9bb60e01b5f5260045ffd5b34610658575f36600319011261065857602060ff600b54166040519015158152f35b34610658575f3660031901126106585760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461065857611f023661136b565b611f0a614c6c565b805167ffffffffffffffff81116112ac57611f2f81611f2a602554611b7f565b613ddf565b602091601f8211600114611f6157611f5c925f91836114045750508160011b915f199060031b1c19161790565b602555005b60255f52601f198216927f401968ff42a154441da5f6c4c935ac46b8671f0e062baaa62a7545ba53bb6e4c915f5b858110611fc457508360019510611fac575b505050811b01602555005b01515f1960f88460031b161c191690555f8080611fa1565b91926020600181928685015181550194019201611f8f565b3461065857602036600319011261065857602061089b60043561444a565b34610658576080366003190112610658576044356064356004356024356120208261444a565b6001600160a01b0316331480159061243f575b801561242a575b8015612415575b61240657600161205c611093845f52601960205260405f2090565b612065816115ef565b148015906123de575b80156123b6575b801561238e575b61237f57612098611deb611de6845f52601460205260405f2090565b6001600160a01b036120bb6117cc611deb611de6865f52601460205260405f2090565b911614801561233b575b80156122f7575b80156122b3575b801561226f575b801561222b575b61221c576106d8937f970d89a1c58e731ff4e5dde020e440de9f7b2a5607ab648765eba29117be46d091611b4c601c54809661211e338830614ad0565b5061212a338530614ad0565b50612136338630614ad0565b50612142338230614ad0565b5061214d8233614ee9565b61215e61215861370e565b83614c96565b866121746110bd845f52601760205260405f2090565b558361218b6110e3845f52601760205260405f2090565b55846121a2611108845f52601760205260405f2090565b55806121b961112c845f52601760205260405f2090565b556121dc6121cf835f52601960205260405f2090565b805460ff19166002179055565b6121ef6121ea601c54613ca3565b601c55565b6040519485943398869192608093969594919660a084019784526020840152604083015260608201520152565b63ceb0e71d60e01b5f5260045ffd5b50612244611deb611de6865f52601460205260405f2090565b6001600160a01b036122676117cc611deb611de6885f52601460205260405f2090565b9116146120e1565b50612288611deb611de6835f52601460205260405f2090565b6001600160a01b036122ab6117cc611deb611de6885f52601460205260405f2090565b9116146120da565b506122cc611deb611de6835f52601460205260405f2090565b6001600160a01b036122ef6117cc611deb611de6895f52601460205260405f2090565b9116146120d3565b50612310611deb611de6845f52601460205260405f2090565b6001600160a01b036123336117cc611deb611de6885f52601460205260405f2090565b9116146120cc565b50612354611deb611de6845f52601460205260405f2090565b6001600160a01b036123776117cc611deb611de6895f52601460205260405f2090565b9116146120c5565b6323742a3d60e21b5f5260045ffd5b5060016123a6611093855f52601960205260405f2090565b6123af816115ef565b141561207c565b5060016123ce611093865f52601960205260405f2090565b6123d7816115ef565b1415612075565b5060016123f6611093835f52601960205260405f2090565b6123ff816115ef565b141561206e565b6318b5288760e31b5f5260045ffd5b50336124236117cc8561444a565b1415612041565b50336124386117cc8661444a565b141561203a565b503361244d6117cc8361444a565b1415612033565b3461065857602036600319011261065857612470600435613f48565b906106d8604051928392836109c5565b34610658576001600160a01b0361249636611a59565b91906124a0614c6c565b165f52601160205260405f20815167ffffffffffffffff81116112ac576124d1816124cb8454611b7f565b84613e17565b602092601f821160011461250457612500929382915f926114045750508160011b915f199060031b1c19161790565b9055005b601f19821693612517845f5260205f2090565b915f5b86811061254f5750836001959610612537575b505050811b019055005b01515f1960f88460031b161c191690555f808061252d565b9192602060018192868501518155019401920161251a565b34610658576020366003190112610658577ff0be4f1e87349231d80c36b33f9e8639658eeaf474014dee15a3e6a4d441419760206001600160a01b036004356125af816108ac565b6125b7614c6c565b16806001600160a01b0319600f541617600f55604051908152a1005b346106585760203660031901126106585760206125fa6004356125f5816108ac565b6140e6565b604051908152f35b34610658575f3660031901126106585761261a614c6c565b5f6001600160a01b03600b5474ffffffffffffffffffffffffffffffffffffffff00198116600b5560081c167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610658575f366003190112610658576020601a54604051908152f35b3461065857604036600319011261065857600435602435905f52601760205260405f20905f52602052602060405f2054604051908152f35b34610658576020366003190112610658576004355f526015602052602060405f2054604051908152f35b3461065857602036600319011261065857600435612707614c6c565b6127108111611e715760218054908290556040805191825260208201929092527fc24d648b8b29d6566f302551df9152ced5a44785e31f38d1d047c8168dd3451991819081016114ea565b346106585760403660031901126106585761276e60043561162f565b60206040515f8152f35b346106585760a03660031901126106585761279236610ae8565b5060643567ffffffffffffffff8111610658576127b3903690600401610af9565b505060206084356127c3816108ac565b604051906001600160a01b03309116148152f35b34610658575f366003190112610658576127ef614c6c565b6127f76159aa565b600160ff19600b541617600b557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b34610658576040366003190112610658576020612875600435612852816108ac565b6024355f526016835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b34610658575f3660031901126106585760206001600160a01b03600b5460081c16604051908152f35b34610658575f366003190112610658576020601f54604051908152f35b34610658576060366003190112610658576004356128e36024356108ac565b6128ee6044356108ac565b5f526018602052602060ff60405f20541615604051908152f35b34610658575f366003190112610658576020602254604051908152f35b34610658575f366003190112610658576040515f60015461294581611b7f565b8084529060018116908115610859575060011461296c576106d8836107f1818503826112cd565b60015f9081527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6939250905b8082106129b0575090915081016020016107f16107e1565b919260018160209254838588010152019101909291612998565b346106585760403660031901126106585760206128756024356004356129ef826108ac565b5f526016835260405f20906001600160a01b03165f5260205260405f2090565b34610658576020366003190112610658576004355f81815260176020818152604080842084805280835281852054600186528286205460028752928620549690955292909152926106d891612a639061112c565b5490604051948594859094939260609260808301968352602083015260408201520152565b34610658575f3660031901126106585760206040515f8152f35b3461065857602036600319011261065857600435612abf816108ac565b6022544311612bc257601b54612ada601e54601f5490613cb1565b811015612bb3576106d891612af6612af183613ca3565b601b55565b612b036020543384615031565b612b1d612b18835f52601460205260405f2090565b614117565b612b3f612b32835f52601960205260405f2090565b805460ff19166004179055565b612b498282614ee9565b612b54612158613721565b6020547fad44b48ec853921e6665d22a88bc31c245479a93dea33a944cc894a12e9785e36001600160a01b0360405193169280612ba03394878360209093929193604081019481520152565b0390a36040519081529081906020820190565b638564324160e01b5f5260045ffd5b63e1be3ef160e01b5f5260045ffd5b8015150361065857565b3461065857604036600319011261065857600435612bf8816108ac565b602435612c0481612bd1565b6001600160a01b038216918215612c6f5781610cb3612c3f92335f52600560205260405f20906001600160a01b03165f5260205260405f2090565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b82630b61174360e31b5f5260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b805415612ca8575f5260205f20905f90565b612c82565b8054821015612ca8575f5260205f2001905f90565b3461065857604036600319011261065857600435602435905f52601460205260405f2090815481101561065857612d036001600160a01b0391602093612cad565b90549060031b1c16604051908152f35b34610658575f36600319011261065857604080516311f0c6d360e11b815260016020820152f35b34610658575f366003190112610658576020601c54604051908152f35b34610658575f36600319011261065857602060405160028152f35b3461065857604036600319011261065857600435612d8f816108ac565b602435612d9a614c6c565b612db5826001600160a01b03165f52601060205260405f2090565b5491818314611a225781612dda826001600160a01b03165f52601060205260405f2090565b558215612f48575b8115612e2e575b6040805193845260208401929092526001600160a01b0316917fb25ad585c6056b2cbc03e32592585455393544dd9097ed06d01ff7238ff9fd5591819081015b0390a2005b601d54905f196001600160a01b0382165f5b848110612f12575b50505f198114612f03577fb25ad585c6056b2cbc03e32592585455393544dd9097ed06d01ff7238ff9fd5593612ebf612e2992610942612eb0612ea3612e956001600160a01b039961419b565b5f52601360205260405f2090565b546001600160a01b031690565b915f52601360205260405f2090565b612ed2612ecd601d5461419b565b601d55565b612ef9612ee9601d545f52601360205260405f2090565b6001600160a01b03198154169055565b9350509050612de9565b63350b944160e11b5f5260045ffd5b816001600160a01b03612f30612ea3845f52601360205260405f2090565b1614612f3e57600101612e40565b9150505f80612e48565b612f6081610942601d545f52601360205260405f2090565b612f6e612ecd601d54613ca3565b612de2565b346106585760a036600319011261065857612f8f6004356108ac565b612f9a6044356108ac565b60843567ffffffffffffffff811161065857612fba903690600401611350565b50636fd4b3b160e11b5f5260045ffd5b34610658575f3660031901126106585760208054604051908152f35b34610658575f3660031901126106585760206001600160a01b03600d5416604051908152f35b3461065857608036600319011261065857600435613029816108ac565b60243590613036826108ac565b6044356064359267ffffffffffffffff84116106585761305d61095d943690600401611350565b926117a6838383613d24565b3461065857604036600319011261065857600435602435613088614c6c565b601e548210611e7157818110611e8057601b54811015611e7157815b818111156130e857506040805192835260208301919091527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190819081016114ea565b806131146131006001935f52601660205260405f2090565b6002603f609a1b015f5260205260405f2090565b54613120575b016130a4565b61312b611ab9613721565b61311a565b9060206003198301126106585760043567ffffffffffffffff81116106585760040182601f820112156106585780359267ffffffffffffffff8411610658576020808301928560051b010111610658579190565b346106585761319236613130565b9061319b614c6c565b6131a482613ba8565b906131b260405192836112cd565b828252602082019260051b8101903682116106585780935b8285106131da5761095d8461549e565b843567ffffffffffffffff8111610658578201606081360312610658576040519161320483611290565b813561320f8161162f565b835261321d60208301611b70565b602084015260408201359267ffffffffffffffff841161065857613248602094938594369101611350565b60408201528152019401936131ca565b346106585760203660031901126106585763ffffffff60043561327a8161162f565b165f52600c602052602060405f2054604051908152f35b34610658575f366003190112610658576020601d54604051908152f35b34610658576060366003190112610658576004356132cb8161162f565b6132d3611b5f565b6044359067ffffffffffffffff8211610658576106d8926132fb611cba933690600401610af9565b929091614238565b61330c36613130565b5f5b81811061338057604051638e9e709960e01b81525f81600481335afa801561337b57613355915f91613359575b50604051638351eea760e01b815291829160048301610796565b0390fd5b61337591503d805f833e61336d81836112cd565b810190614405565b8261333b565b6143c4565b61338b8183856142da565b906133b7610c2161339b84613c72565b60208501359063ffffffff165f52600c60205260405f20541490565b6134555760c08201359160a08101356133d46101008301836142fd565b9490916133e360e08501614330565b936133f26101208201826142fd565b959095303b15610658575f9661341f926040519a8b9889978897633411683760e21b89526004890161434c565b0391305af191821561337b5760019261343b575b505b0161330e565b806134495f61344f936112cd565b80610768565b5f613433565b60019150613435565b346106585760403660031901126106585760043567ffffffffffffffff811161065857604061349461351d923690600401610e3d565b6135026024356134a381612bd1565b6134ab613cbe565b506134b58361454e565b909335936134c28561162f565b6134ca613cbe565b506134d48561447d565b6134eb6134df6112ef565b63ffffffff9097168752565b602086015285850152606084015215156080830152565b815180938192631bb8518b60e31b835230906004840161471f565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa801561337b576106d8915f9161357b575b5060405191829182815181526020918201519181019190915260400190565b61359d915060403d6040116135a3575b61359581836112cd565b810190615593565b5f61355c565b503d61358b565b34610658576020366003190112610658576106d8611cba6004356155a7565b34610658575f6020366003190112610658576004356135e7816108ac565b6135ef614c6c565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b15610658576001600160a01b0360245f9283604051958694859363ca5eb5e160e01b85521660048401525af1801561337b57613656575080f35b61095d91505f906112cd565b61366b36610b27565b505050923033036136935783602011610658576001600160a01b03823516610c138584615794565b63029a949d60e31b5f5260045ffd5b34610658576020366003190112610658577fd48d879cef83a1c0bdda516f27b13ddb1b3f8bbac1c9e1511bb2a659c242776060206001600160a01b036004356136ea816108ac565b6136f2614c6c565b16806001600160a01b0319600d541617600d55604051908152a1005b60405190610e8682611c4c816025611bb7565b60405190610e8682611c4c816024611bb7565b34610658576020366003190112610658576001600160a01b03600435613759816108ac565b165f5260116020526106d8611cb3611cba60405f2060405192838092611bb7565b3461065857602036600319011261065857600435613797816108ac565b6022544311612bc257601a54601e54811015613865576137c8826001600160a01b03165f52601060205260405f2090565b5415612f0357613855611ab9611e406106d8946137ec6137e78661443c565b601a55565b6137f73386836155e1565b6138128161380d875f52601460205260405f2090565b61415b565b613834613827865f52601960205260405f2090565b805460ff19166001179055565b61383e8533614ee9565b6001600160a01b03165f52601160205260405f2090565b6040519081529081906020820190565b636414c23f60e01b5f5260045ffd5b3461065857602060ff6138b36001600160a01b0361389136611968565b91165f526005845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b34610658576040366003190112610658576004356024356138de614c6c565b818110611e80576138f4601e54601f5490613cb1565b8210611e7157601c54811015611e7157815b8181111561394a57506040805192835260208301919091527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190819081016114ea565b806139626110bd6001935f52601760205260405f2090565b5415801590613987575b613977575b01613906565b613982611ab961370e565b613971565b5061399d6110e3825f52601760205260405f2090565b54151561396c565b34610658576020366003190112610658576001600160a01b036004356139ca816108ac565b165f52601060205260405f2054612710610d5960215483613c90565b3461065857602036600319011261065857600435613a03816108ac565b613a0b614c6c565b6001600160a01b038116908115613a8a576001600160a01b039074ffffffffffffffffffffffffffffffffffffffff00600b549160081b1674ffffffffffffffffffffffffffffffffffffffff0019821617600b5560081c167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b631e4fbdf760e01b5f525f60045260245ffd5b3461065857608036600319011261065857613ab96004356108ac565b60643567ffffffffffffffff811161065857613ad9903690600401611350565b50633a72840160e21b5f5260045ffd5b34610658575f3660031901126106585760206040516127108152f35b34610658576020366003190112610658576001600160a01b03600435613b2a816108ac565b165f526010602052602060405f2054604051908152f35b34610658576060366003190112610658576020613b5d36610ae8565b63ffffffff8135613b6d8161162f565b165f52600c82528160405f205491013560405191148152f35b613b8f8161444a565b505f5260046020526001600160a01b0360405f20541690565b67ffffffffffffffff81116112ac5760051b60200190565b60405190613bcf6020836112cd565b5f808352366020840137565b60408051909190613bec83826112cd565b6001815291601f1901366020840137565b60405160a09190613c0e83826112cd565b6004815291601f1901366020840137565b90613c2982613ba8565b613c3660405191826112cd565b8281528092613c47601f1991613ba8565b0190602036910137565b805115612ca85760200190565b8051821015612ca85760209160051b010190565b356107a78161162f565b634e487b7160e01b5f52601160045260245ffd5b81810292918115918404141715610d8657565b9060018201809211610d8657565b91908201809211610d8657565b60405190613ccb826112b1565b5f6020838281520152565b60405190613ce382611290565b815f81525f60208201526040613cf7613cbe565b910152565b60409060231901126106585760405190613d15826112b1565b60243582526044356020830152565b91906001600160a01b03811615613d73576001600160a01b03613d4a8192843391614ad0565b9316921691808303613d5b57505050565b6364283d7b60e01b5f5260045260245260445260645ffd5b633250574960e11b5f525f60045260245ffd5b818110613d91575050565b5f8155600101613d86565b90601f8211613da9575050565b610e869160245f5260205f20906020601f840160051c83019310613dd5575b601f0160051c0190613d86565b9091508190613dc8565b90601f8211613dec575050565b610e869160255f5260205f20906020601f840160051c83019310613dd557601f0160051c0190613d86565b9190601f8111613e2657505050565b610e86925f5260205f20906020601f840160051c83019310613dd557601f0160051c0190613d86565b90604051918281549182825260208201905f5260205f20925f5b818110613e7e575050610e86925003836112cd565b84546001600160a01b0316835260019485019487945060209093019201613e69565b80545f825580613eae575050565b610e86915f5260205f2090810190613d86565b6001600160a01b0316805f52601260205260405f2054918215613f4357815f5260126020525f6040812055613ef7838284614ead565b15613f34576040519283526001600160a01b0316917f294b789b8defca4b7d7312b11f70ca28a93587f39fa40bdde2106b5ee3f94b7790602090a3565b6312171d8360e31b5f5260045ffd5b505050565b90613f5e611093835f52601960205260405f2090565b613f67816115ef565b156115cc576003613f83611093845f52601960205260405f2090565b613f8c816115ef565b146140d3576001613fa8611093845f52601960205260405f2090565b613fb1816115ef565b1480156140ac575b61406057613fc5613bfd565b613fcd613bfd565b925f5b60048110613fde5750509190565b8061404e614007600193613ffa865f52601760205260405f2090565b905f5260205260405f2090565b54610994614023611deb611de6845f52601460205260405f2090565b9161404083614032878b613c5e565b906001600160a01b03169052565b5f52601660205260405f2090565b546140598288613c5e565b5201613fd0565b614068613bdb565b61409d614073613bdb565b9361099461408f611deb611de6845f52601460205260405f2090565b916140408361403287613c51565b546140a784613c51565b529190565b5060046140c4611093845f52601960205260405f2090565b6140cd816115ef565b14613fb9565b90506140dd613bc0565b906107a7613bc0565b6001600160a01b03168015614104575f52600360205260405f205490565b6322718ad960e21b5f525f60045260245ffd5b8054680100000000000000008110156112ac5761413991600182018155612cad565b81549060031b906001600160a01b036002603f609a1b01831b921b1916179055565b8054680100000000000000008110156112ac5761417d91600182018155612cad565b6001600160a01b0380839493549260031b9316831b921b1916179055565b5f19810191908211610d8657565b909291928360021161065857831161065857600201916001190190565b805191908290602001825e015f815290565b6141f29392610e86928160405196879460208601906141c6565b918237015f815203601f1981018452836112cd565b908060209392818452848401375f828201840152601f01601f1916010190565b9160206107a7938181520191614207565b611ca7611cb39263ffffffff6142699397959697165f52600e60205260405f209061ffff165f5260205260405f2090565b8051156142cc5783156142c657600284101561429d575050613355604051928392639a6d49cd60e01b845260048401614227565b9092806142c0916107a7946142bb6142b636848461131a565b61555b565b6141a9565b916141d8565b92509050565b5091906107a791369161131a565b9190811015612ca85760051b8101359061013e1981360301821215610658570190565b903590601e1981360301821215610658570180359067ffffffffffffffff82116106585760200191813603831361065857565b356107a7816108ac565b67ffffffffffffffff81160361065857565b926107a7979596946001600160a01b039367ffffffffffffffff60406143b09563ffffffff813561437c8161162f565b168952602081013560208a015201356143948161433a565b166040870152606086015260e0608086015260e0850191614207565b941660a082015260c0818503910152614207565b6040513d5f823e3d90fd5b9291926143db826112fe565b916143e960405193846112cd565b829481845281830111610658578281602093845f96015e010152565b6020818303126106585780519067ffffffffffffffff821161065857019080601f830112156106585781516107a7926020016143cf565b5f198114610d865760010190565b805f5260026020526001600160a01b0360405f20541690811561446b575090565b637e27328960e01b5f5260045260245ffd5b63ffffffff16805f52600c60205260405f205490811561449b575090565b63f6ff4fb760e01b5f5260045260245ffd5b919060408382031261065857825167ffffffffffffffff811161065857830181601f820112156106585760209181836144e8935191016143cf565b9201516107a781612bd1565b9061450c602091949394604084526040840190610772565b931515910152565b9081602091031261065857516107a781612bd1565b90916145406107a793604084526040840190610772565b916020818403910152610772565b60208101359182156146a6576040820135926145a461456c856155a7565b614581611093875f52601860205260405f2090565b918291614596604051948592602084016144f4565b03601f1981018452836112cd565b8061467e575b61466f576145e1946145bb92615810565b93909283945f14614667576002905b6132fb6145d682613c72565b9160608101906142fd565b916001600160a01b036145fc600f546001600160a01b031690565b1690838261460957505050565b60405163043a78eb60e01b81529260209284928391829161462d9160048401614529565b03915afa801561337b5761463f575b50565b61463c9060203d602011614660575b61465881836112cd565b810190614514565b503d61464e565b6001906145ca565b631b67d22160e01b5f5260045ffd5b506001600160a01b0382166001600160a01b0361469d6117cc8861444a565b911614156145aa565b631e4ec46b60e01b5f5260045ffd5b9190826040910312610658576040516146cd816112b1565b6020808294805184520151910152565b90608082820312610658576147179060408051936146fa85611290565b80518552602081015161470c8161433a565b6020860152016146b5565b604082015290565b906001600160a01b036020919493946040845263ffffffff8151166040850152828101516060850152608061477b614765604084015160a08489015260e0880190610772565b6060840151878203603f190160a0890152610772565b910151151560c08501529416910152565b6147e96080949261480596946147a0613cd6565b5060206147ad8651615889565b95019182518061486d575b506147c28561447d565b92511515926147d26134df6112ef565b602086015260408501526060840152151585830152565b6040518095819482936302637a4560e41b84526004840161471f565b03916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af190811561337b575f91614844575090565b6107a7915060803d608011614866575b61485e81836112cd565b8101906146dd565b503d614854565b614876906158a6565b5f6147b8565b60ff602354161580614978575b61466f576107a7906148996159aa565b5f818152600260205260409020546001600160a01b031692816001600160a01b038216614967575b50506001600160a01b0383168015825f82159384614937575b6148f082610942855f52600260205260405f2090565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a415614928575061492381615b1c565b615bbc565b15615bbc576149238184615a98565b61494083615772565b61495b886001600160a01b03165f52600360205260405f2090565b821981540190556148da565b6149719185615cab565b5f816148c1565b50805f52601860205260ff60405f205416614889565b60ff602354161580614aba575b61466f576149a76159aa565b5f828152600260205260409020546001600160a01b0316918215838115614a8a575b6001600160a01b038416918215908115614a63575b6149f486610942875f52600260205260405f2090565b8484847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a415614a4d57614a2984615b1c565b15614a3a5750506107a79150615bbc565b03614a4457505090565b6107a791615b78565b818314614a2957614a5e8487615a98565b614a29565b614a7e866001600160a01b03165f52600360205260405f2090565b600181540190556149de565b614a9383615772565b614aae856001600160a01b03165f52600360205260405f2090565b80545f190190556149c9565b50815f52601860205260ff60405f20541661499b565b9060ff602354161580614b62575b61466f57614aea6159aa565b5f818152600260205260409020546001600160a01b031692816001600160a01b038216614b51575b50506001600160a01b0383168015908115614a8a576001600160a01b038416918215908115614a63576149f486610942875f52600260205260405f2090565b614b5b9185615cab565b5f81614b12565b50805f52601860205260ff60405f205416614ade565b60ff602354161580614c56575b61466f57614b916159aa565b5f818152600260205260408120546001600160a01b031680159182159084908383614c26575b614bcd82610942855f52600260205260405f2090565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a48215614c125750614c0183615b1c565b614c0a83615bbc565b5061446b5750565b15614c0157614c218382615a98565b614c01565b614c2f83615772565b614c4a856001600160a01b03165f52600360205260405f2090565b82198154019055614bb7565b50805f52601860205260ff60405f205416614b85565b6001600160a01b03600b5460081c163303614c8357565b63118cdaa760e01b5f523360045260245ffd5b919091805f52600a60205260405f20835167ffffffffffffffff81116112ac57614cc4816124cb8454611b7f565b6020601f8211600114614d3d5781614d38949392614d19927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce797985f926114045750508160011b915f199060031b1c19161790565b90555b6040518181528390602090a16040519081529081906020820190565b0390a1565b601f19821695614d50845f5260205f2090565b965f5b818110614db55750966001928492614d389796957ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7999a10614d9d575b505050811b019055614d1c565b01515f1960f88460031b161c191690555f8080614d90565b83830151895560019098019760209384019301614d53565b90916001600160a01b03821691835f52601660205284614e018260405f20906001600160a01b03165f5260205260405f2090565b5410614e9357614e1d90610994855f52601660205260405f2090565b805490858203918211610d865755614e3d835f52601560205260405f2090565b614e47815461443c565b9055614e57610c21858385614ead565b613f34576040519384526001600160a01b0316927ff08722dda679d80a9dd519f28c27aeaa8d437512336f6fa9aea15e4e1f95690890602090a4565b631e9acf1760e31b5f5260045ffd5b61463c90339061487c565b6107a7926001600160a01b036040519363a9059cbb60e01b6020860152166024840152604483015260448252614ee46064836112cd565b615963565b604051602092614ef984836112cd565b5f82526001600160a01b038316908115613d73576001600160a01b03614f1f828661498e565b1661500f57833b614f32575b5050505050565b918495939491614f5b935f604051809681958294630a85bd0160e11b845284336004860161523e565b03925af15f9181614fe0575b50614fa0575050614f7661526c565b8051929083614f9b57633250574960e11b5f526001600160a01b03831660045260245ffd5b019050fd5b9092506001600160e01b03191663757a42ff60e11b01614fc557505f80808080614f2b565b633250574960e11b5f526001600160a01b031660045260245ffd5b615001919250853d8711615008575b614ff981836112cd565b810190615229565b905f614f67565b503d614fef565b6339e3563760e11b5f525f60045260245ffd5b90816020910312610658575190565b91909161504b61504360215484613c90565b612710900490565b926150568484613cb1565b604051636eb1769f60e11b81526001600160a01b03831660048201523060248201526020816044816002603f609a1b015afa801561337b5782915f9161520a575b50106151fb576040516370a0823160e01b81526001600160a01b03831660048201526020816024816002603f609a1b015afa801561337b5782915f916151cc575b5010614e9357610c216150f49130846002603f609a1b016159c5565b613f34576151b4936002603f609a1b019383857f684ce28ace37552c6bfb98b7cceda8ed55327078eafb5dfb31218e08563827636001600160a01b036151c79661516b846109947ff228de527fc1b9843baac03b9a04565473a263375950e63435d4138464386f469b5f52601660205260405f2090565b615176878254613cb1565b9055615193846001600160a01b03165f52601260205260405f2090565b61519e888254613cb1565b9055604051958652169889949081906020820190565b0390a46040519081529081906020820190565b0390a3565b6151ee915060203d6020116151f4575b6151e681836112cd565b810190615022565b5f6150d8565b503d6151dc565b6313be252b60e01b5f5260045ffd5b615223915060203d6020116151f4576151e681836112cd565b5f615097565b9081602091031261065857516107a78161065c565b90926001600160a01b03608093816107a7979616845216602083015260408201528160608201520190610772565b3d15615296573d9061527d826112fe565b9161528b60405193846112cd565b82523d5f602084013e565b606090565b939193843b6152ab575050505050565b6152cc6020936040519586948594630a85bd0160e11b86526004860161523e565b03815f6001600160a01b0387165af15f918161533f575b5061531d57506152f161526c565b805191908261531657633250574960e11b5f526001600160a01b03821660045260245ffd5b6020915001fd5b6001600160e01b03191663757a42ff60e11b01614fc557505f80808080614f2b565b61535991925060203d60201161500857614ff981836112cd565b905f6152e3565b919091825167ffffffffffffffff81116112ac57615382816124cb8454611b7f565b6020601f82116001146153b55781906153b19394955f926114045750508160011b915f199060031b1c19161790565b9055565b601f198216906153c8845f5260205f2090565b915f5b818110615402575095836001959697106153ea575b505050811b019055565b01515f1960f88460031b161c191690555f80806153e0565b9192602060018192868b0151815501940192016153cb565b602081016020825282518091526040820191602060408360051b8301019401925f915b83831061544c57505050505090565b909192939460208061548f600193603f19868203018752606060408b5163ffffffff815116845261ffff8682015116868501520151918160408201520190610772565b9701930193019193929061543d565b5f5b815181101561552957806154c360406154bb60019486613c5e565b51015161555b565b61552360406154d28386613c5e565b51015163ffffffff6154e48487613c5e565b5151165f52600e60205261551e60405f2061550e6020615504878a613c5e565b51015161ffff1690565b61ffff165f5260205260405f2090565b615360565b016154a0565b50614d387fbe4864a8e820971c0247f5992e2da559595f7bf076a21cb5928d443d2a13b674916040519182918261541a565b600361ffff6002830151160361556e5750565b604051639a6d49cd60e01b815260206004820152908190613355906024830190610772565b90604082820312610658576107a7916146b5565b6155b08161444a565b505f52600a602052611cb36155ce60405f2060405192838092611bb7565b5f6040516155dd6020826112cd565b5290565b9190916001600160a01b03811691825f52601060205260405f205461560b61504360215483613c90565b906156168282613cb1565b604051636eb1769f60e11b81526001600160a01b03851660048201523060248201526020816044818a5afa801561337b5782915f91615753575b50106151fb576040516370a0823160e01b81526001600160a01b03851660048201526020816024818a5afa801561337b5782915f91615734575b5010614e9357610c2161569f913086896159c5565b613f345785857f684ce28ace37552c6bfb98b7cceda8ed55327078eafb5dfb31218e08563827636001600160a01b036151c796615193896157107ff228de527fc1b9843baac03b9a04565473a263375950e63435d4138464386f469b6109946151b49f5f52601660205260405f2090565b61571b898254613cb1565b90556001600160a01b03165f52601260205260405f2090565b61574d915060203d6020116151f4576151e681836112cd565b5f61568a565b61576c915060203d6020116151f4576151e681836112cd565b5f615650565b615784905f52600460205260405f2090565b80546001600160a01b0319169055565b90604011610658576020013590565b9080604011610658576107a7913691603f19019060400161131a565b815f5260026020526001600160a01b0360405f2054166157fd57906157f091600160ff19602354161760235561498e565b5060ff1960235416602355565b50636025413b60e01b5f5260045260245ffd5b825115801593906158545790615843615851926040519485936020850152604084015233606084015260808301906141c6565b03601f1981018352826112cd565b91565b50906040519160208301526040820152604081526158516060826112cd565b6157f091600160ff19602354161760235561487c565b8034036158935790565b6304fb820960e51b5f523460045260245ffd5b60405163393f876560e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169190602081600481865afa801561337b575f90615921575b6001600160a01b0391501691821561591257610e86923390615a07565b6329b99a9560e11b5f5260045ffd5b506020813d60201161595b575b8161593b602093836112cd565b81010312610658576001600160a01b039051615956816108ac565b6158f5565b3d915061592e565b905f602091828151910182855af1903d5f519083615982575b50505090565b919250906159a057506001600160a01b03163b15155b5f808061597c565b6001915014615998565b60ff600b54166159b657565b63d93c066560e01b5f5260045ffd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648201929092526107a791614ee48260848101614596565b6040516323b872dd60e01b60208083019182526001600160a01b0394851660248401529490931660448201526064810194909452925f9190615a4c8160848101615843565b519082855af1156143c4575f513d615a8f57506001600160a01b0381163b155b615a735750565b6001600160a01b0390635274afe760e01b5f521660045260245ffd5b60011415615a6c565b5f91615aee91615aa7816140e6565b9180855260076020526001600160a01b0360408620549216855260066020526040852091838103615af1575b5084526007602052836040812055905f5260205260405f2090565b55565b838652826020526040862054818752836020528060408820558652600760205260408620555f615ad3565b600854815f5260096020528060405f2055680100000000000000008110156112ac5760018101600855600854811015612ca85760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b90615b82826140e6565b5f19810192908311610d86576001600160a01b03165f52600660205260405f20825f526020528060405f20555f52600760205260405f2055565b6008545f19810191818311610d8657805f5260096020528160405f2054931015612ca85760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee282015491831015612ca8577ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee383018290555f91825260096020526040808320939093558152908120556008548015615c97575f19810190600854821015612ca85760085f8181527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee29092019190915555565b634e487b7160e01b5f52603160045260245ffd5b919091615cc96001600160a01b0384168015159081615d1157501590565b615cd257505050565b6001600160a01b0316615cf157637e27328960e01b5f5260045260245ffd5b63177e802f60e01b5f526001600160a01b0390911660045260245260445ffd5b6001600160a01b03841680821492508215615d50575b508115615d3357501590565b9050835f5260046020526001600160a01b0360405f205416141590565b9091505f52600560205260ff615d7a8660405f20906001600160a01b03165f5260205260405f2090565b5416905f615d2756fea26469706673582212207dcb15461067a727dde050264f041d9fda466983b311e0769ef86bc98ded819c64736f6c634300081e0033000000000000000000000000cff4cb3e2f8f8b899d53ff9570d224253140fdda0000000000000000000000001a44076050125825900e736c501f859c50fe728c
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806283fc9d1461062957806301ffc9a71461062457806306fdde031461061f578063081812fc1461061a578063095ea7b3146106155780630a3f6fda14610610578063111ecdad1461060b57806313137d651461060657806313a8ae8c1461060157806317442b70146105fc57806318160ddd146105f75780631a0c131c146105f25780631cead9a7146105ed5780631f5e1334146105e857806321eb730b146105e357806323b872dd146105de5780632d7ee3b8146105d95780632e49ac6d146105d45780632eea49d9146105cf5780632f745c59146105ca578063335c9701146105c557806333f6832a146105c05780633400288b146105bb5780633811a5e5146105b65780633bd7a51c146105b15780633f4ba83a146105ac57806342842e0e146105a757806342966c68146105a257806349072f121461059d5780634f6ccce714610598578063501ecded14610593578063523d7c161461058e57806352ae28791461045357806354af740c146105895780635535d46114610584578063582f706f1461057f5780635a0dfe4d1461057a5780635a5dceef146105755780635c975abb146105705780635e280f111461056b5780635f71c3bf146105665780636352211e1461056157806364cae70d1461055c57806369ac032d146105575780636c2c9611146105525780636fc1b31e1461054d57806370a0823114610548578063715018a61461054357806375794a3c1461053e5780637779fa00146105395780637819a585146104df5780637caab700146105345780637d25a05e1461052f57806382413eac1461052a5780638456cb5914610525578063866ef715146105205780638da5cb5b1461051b57806390de2d531461051657806391a6262f14610511578063933e06671461050c57806395d89b411461050757806395ef7415146105025780639ca4df96146104fd5780639f68b964146104f8578063a1eb9aaf146104f3578063a22cb465146104ee578063a2e4a7b8146104e9578063a72f5dd8146104e4578063a869de0a146104df578063ab1d27a4146104da578063b21a33e4146104d5578063b27e3d80146104d0578063b5bf95f9146104cb578063b7144f6e146104c6578063b731ea0a146104c1578063b88d4fde146104bc578063b93a2254146104b7578063b98bd070146104b2578063bb0b6a53146104ad578063bb4a5fa7146104a8578063bc70b354146104a3578063bd815db01461049e578063c6414e7b14610499578063c87b56dd14610494578063ca5eb5e11461048f578063d045a0dc1461048a578063d424388514610485578063de5e54c114610480578063e95a71ae1461047b578063e985e9c514610476578063ebc9ed2114610471578063f2a137211461046c578063f2fde38b14610467578063f43f7bba14610462578063f4ea93d81461045d578063f7f4b2eb14610458578063fc0c546a146104535763ff7bd03d1461044e575f80fd5b613b41565b611a3f565b613b05565b613ae9565b613a9d565b6139e6565b6139a5565b6138bf565b613874565b61377a565b613734565b6136a2565b613662565b6135c9565b6135aa565b61345e565b613303565b6132ae565b613291565b613258565b613184565b613069565b61300c565b612fe6565b612fca565b612f73565b612d72565b612d57565b612d3a565b6126c1565b612d13565b612cc2565b612bdb565b612aa2565b612a88565b612a0f565b6129ca565b612925565b612908565b6128c4565b6128a7565b61287e565b612830565b6127d7565b612778565b612752565b6126eb565b612689565b61266c565b612602565b6125d3565b612567565b612480565b612454565b611ffa565b611fdc565b611ef4565b611eb1565b611e8f565b611d31565b611ceb565b611cce565b611c53565b611a92565b6119ac565b61198d565b611902565b6118e5565b6117ac565b611777565b611711565b6116f0565b6116bd565b61163d565b6115f9565b611569565b6114ef565b61148a565b611396565b61104f565b611038565b610e88565b610e22565b610de6565b610dc9565b610dac565b610d8b565b610d37565b610b96565b610ac2565b610a40565b6108bd565b61087d565b6107aa565b61066e565b34610658576020366003190112610658576004355f526018602052602060ff60405f2054166040519015158152f35b5f80fd5b6001600160e01b031981160361065857565b34610658576020366003190112610658576106d860043561068e8161065c565b63ffffffff60e01b16632483248360e11b8114908115610718575b8115610707575b81156106f6575b81156106ed575b81156106dc575b5060405190151581529081906020820190565b0390f35b6311f0c6d360e11b1490505f6106c5565b801591506106be565b6391a6262f60e01b811491506106b7565b631be1f1d760e21b811491506106b0565b905063780e9d6360e01b81148015610731575b906106a9565b506380ac58cd60e01b81148015610758575b8061072b57506301ffc9a760e01b811461072b565b50635b5e139f60e01b8114610743565b5f91031261065857565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060206107a7928181520190610772565b90565b34610658575f366003190112610658576040515f5f546107c981611b7f565b808452906001811690811561085957506001146107fd575b6106d8836107f1818503826112cd565b60405191829182610796565b5f8080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563939250905b80821061083f575090915081016020016107f16107e1565b919260018160209254838588010152019101909291610827565b60ff191660208086019190915291151560051b840190910191506107f190506107e1565b3461065857602036600319011261065857602061089b600435613b86565b6001600160a01b0360405191168152f35b6001600160a01b0381160361065857565b34610658576040366003190112610658576004356108da816108ac565b602435906108e78261444a565b331515806109b2575b80610972575b61095f578261095d93610942926001600160a01b0380861691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a45f52600460205260405f2090565b906001600160a01b03166001600160a01b0319825416179055565b005b63a9fbf51f60e01b5f523360045260245ffd5b5060ff6109aa33610994846001600160a01b03165f52600560205260405f2090565b906001600160a01b03165f5260205260405f2090565b5416156108f6565b50336001600160a01b03821614156108f0565b604081016040825282518091526020606083019301905f5b818110610a21575050506020818303910152602080835192838152019201905f5b818110610a0b5750505090565b82518452602093840193909201916001016109fe565b82516001600160a01b03168552602094850194909201916001016109dd565b34610658575f36600319011261065857601d54610a5c81613c1f565b610a6582613c1f565b915f5b818110610a805750506106d8604051928392836109c5565b806001915f5260136020526001600160a01b0360405f20541680610aa48387613c5e565b525f52601060205260405f2054610abb8287613c5e565b5201610a68565b34610658575f3660031901126106585760206001600160a01b03600f5416604051908152f35b606090600319011261065857600490565b9181601f840112156106585782359167ffffffffffffffff8311610658576020838186019501011161065857565b90600319820160e0811261065857606013610658576004916064359160843567ffffffffffffffff81116106585782610b6291600401610af9565b9290929160a435610b72816108ac565b9160c4359067ffffffffffffffff821161065857610b9291600401610af9565b9091565b610b9f36610b27565b50505092336001600160a01b037f0000000000000000000000001a44076050125825900e736c501f859c50fe728c1603610d24578035610bde8161162f565b610be78161447d565b906020830135809203610cff57505083602011610658576001600160a01b03823516610c138584615794565b94610c25610c218260401090565b1590565b610cf057610cc96001600160a01b0393610cc4610ca1610c85610c6c610ceb967f7883fa30ea56937810e36990b0bbb8d629d0cf59f68baf8431ff657cebe7eef59a6157a3565b604060208251601f1981018285015283010191016144ad565b9190610c9085613c72565b50610c9b8c896157bf565b8b614c96565b610cb38a5f52601860205260405f2090565b9060ff801983541691151516179055565b613c72565b956040519384931696836020909392919363ffffffff60408201951681520152565b0390a3005b631e071f7b60e01b5f5260045ffd5b80610d0e63ffffffff9261162f565b63309afaf360e21b5f521660045260245260445ffd5b6391ac5e4f60e01b5f523360045260245ffd5b34610658575f36600319011261065857602054612710610d5960215483613c90565b0490818101808211610d86576106d890604051938493846040919493926060820195825260208201520152565b613c7c565b34610658575f36600319011261065857604080516001815260026020820152f35b34610658575f366003190112610658576020600854604051908152f35b34610658575f366003190112610658576020601b54604051908152f35b34610658576020366003190112610658576001600160a01b03600435610e0b816108ac565b165f526012602052602060405f2054604051908152f35b34610658575f36600319011261065857602060405160018152f35b908160c09103126106585790565b610e869092919260408060808301958051845267ffffffffffffffff6020820151166020850152015191019060208091805184520151910152565b565b60803660031901126106585760043567ffffffffffffffff811161065857610eb4903690600401610e3d565b60403660231901126106585760643590610ecd826108ac565b610ed5613cd6565b50610edf8161454e565b9290604083013593610ef1843561162f565b610efa8561444a565b6001600160a01b038116803314159081610fda575b5080610fbf575b610fa85791859391610f2e6106d897610f4795615873565b610f3786613c72565b91610f4136613cfc565b9261478c565b917f986156872b2ee0022b9585231dbbfde457f87f8a16b6c45e1a81c54c4ad8351f610f99610f77855193613c72565b936040519182913396836020909392919363ffffffff60408201951681520152565b0390a360405191829182610e4b565b8563177e802f60e01b5f523360045260245260445ffd5b50336001600160a01b03610fd288613b86565b161415610f16565b90505f52600560205260ff6110033360405f20906001600160a01b03165f5260205260405f2090565b5416155f610f0f565b606090600319011261065857600435611024816108ac565b90602435611031816108ac565b9060443590565b346106585761095d6110493661100c565b91613d24565b346106585760203660031901126106585760043561106c8161444a565b6001600160a01b033391160361126d57600261109a611093835f52601960205260405f2090565b5460ff1690565b6110a3816115ef565b0361125e576110ca6110bd825f52601760205260405f2090565b5f805260205260405f2090565b546106d86110f16110e3845f52601760205260405f2090565b60015f5260205260405f2090565b5492611116611108825f52601760205260405f2090565b60025f5260205260405f2090565b5461113a61112c835f52601760205260405f2090565b60035f5260205260405f2090565b549161114581614b78565b61116461115a825f52601960205260405f2090565b805460ff19169055565b5f61117a6110bd835f52601760205260405f2090565b555f6111916110e3835f52601760205260405f2090565b555f6111a8611108835f52601760205260405f2090565b555f6111bf61112c835f52601760205260405f2090565b556111cb308633614ad0565b506111d7308733614ad0565b506111e3308333614ad0565b506111ef308433614ad0565b50604080518681526020810188905290810183905260608101849052608081019190915233907fd980190ee4051835382cf91aa287f46742f3e33b24b5775cbc6c7b7692f6e1999060a090a2604051948594859094939260609260808301968352602083015260408201520152565b6310f0a8ed60e21b5f5260045ffd5b630fe6876960e21b5f5260045ffd5b634e487b7160e01b5f52604160045260245ffd5b6060810190811067ffffffffffffffff8211176112ac57604052565b61127c565b6040810190811067ffffffffffffffff8211176112ac57604052565b90601f8019910116810190811067ffffffffffffffff8211176112ac57604052565b60405190610e8660a0836112cd565b67ffffffffffffffff81116112ac57601f01601f191660200190565b929192611326826112fe565b9161133460405193846112cd565b829481845281830111610658578281602093845f960137010152565b9080601f83011215610658578160206107a79335910161131a565b6020600319820112610658576004359067ffffffffffffffff8211610658576107a791600401611350565b34610658576113a43661136b565b6113ac614c6c565b805167ffffffffffffffff81116112ac576113d1816113cc602454611b7f565b613d9c565b602091601f821160011461140f576113ff925f9183611404575b50508160011b915f199060031b1c19161790565b602455005b015190505f806113eb565b60245f52601f198216927f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec4915f5b8581106114725750836001951061145a575b505050811b01602455005b01515f1960f88460031b161c191690555f808061144f565b9192602060018192868501518155019401920161143d565b34610658576020366003190112610658577fef38f84b2df3a68028b4b01a4234d8a062db34f9184c55c8ca7c99777657be126004356114c7614c6c565b602254816022556114ea6040519283928360209093929193604081019481520152565b0390a1005b346106585760403660031901126106585760043561150c816108ac565b6001600160a01b0360243591611521816140e6565b83101561155357165f52600660205260405f20905f526020526106d860405f2054604051918291829190602083019252565b63295f44f760e21b5f521660045260245260445ffd5b346106585760403660031901126106585760043560243567ffffffffffffffff81116106585761159d903690600401611350565b6115a5614c6c565b815f52601960205260ff60405f20541660058110156115db57156115cc5761095d91614c96565b63677510db60e11b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b600511156115db57565b34610658576020366003190112610658576004355f52601960205260ff60405f20541660405160058210156115db576020918152f35b63ffffffff81160361065857565b34610658576040366003190112610658577f238399d427b947898edb290f5ff0f9109849b1c3ba196a42e35f00c50a54b98b60043561167b8161162f565b60243590611687614c6c565b63ffffffff81165f52600c6020528160405f20556114ea604051928392836020909392919363ffffffff60408201951681520152565b34610658576020366003190112610658576004355f52601360205260206001600160a01b0360405f205416604051908152f35b34610658575f3660031901126106585760206040516002603f609a1b018152f35b34610658575f36600319011261065857611729614c6c565b600b5460ff8116156117685760ff1916600b557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b638dfc202b60e01b5f5260045ffd5b346106585761095d6117883661100c565b90604051926117986020856112cd565b5f84526117a6838383613d24565b3361529b565b34610658576020366003190112610658576004356117d86117cc8261444a565b6001600160a01b031690565b33036118d65760026117f5611093835f52601960205260405f2090565b6117fe816115ef565b146118c75761181d611818825f52601460205260405f2090565b613e4f565b611837611832835f52601460205260405f2090565b613ea0565b5f5b81518110156118a957806118a361186261185560019486613c5e565b516001600160a01b031690565b611899611877875f52601660205260405f2090565b6118846118558689613c5e565b6001600160a01b03165f5260205260405f2090565b5490863391614dcd565b01611839565b61095d61115a846118b981614ea2565b5f52601960205260405f2090565b639b2b689d60e01b5f5260045ffd5b637e9ce1f160e11b5f5260045ffd5b34610658575f366003190112610658576020601e54604051908152f35b34610658576020366003190112610658576004356008548110156119525760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30154604051908152602090f35b63295f44f760e21b5f525f60045260245260445ffd5b604090600319011261065857600435611980816108ac565b906024356107a7816108ac565b346106585761095d61199e36611968565b906119a7614c6c565b613ec1565b34610658576020366003190112610658576004356119c8614c6c565b670de0b6b3a7640000811115611a305760205490808214611a2257602081815560408051938452908301919091527f961a8c1a9f6deb049129c6604411f01cff3f6ca788a5b61860a80411982381fd9190819081016114ea565b62cd406d60e71b5f5260045ffd5b637579f1fb60e11b5f5260045ffd5b34610658575f366003190112610658576020604051308152f35b90604060031983011261065857600435611a72816108ac565b916024359067ffffffffffffffff8211610658576107a791600401611350565b3461065857611aa036611a59565b611aa8614c6c565b611abf601c5491611ab98385614ee9565b82614c96565b805f52601860205260405f20600160ff19825416179055805f526019602052611af260405f20600360ff19825416179055565b601c549160018301809311610d8657611b0d6106d893601c55565b7fcf53c6969408dccd2090c6cfc96c732645840f810d24fc44cd41fa80583a52df6001600160a01b0360405192169180611b4c85829190602083019252565b0390a26040519081529081906020820190565b6024359061ffff8216820361065857565b359061ffff8216820361065857565b90600182811c92168015611bad575b6020831014611b9957565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611b8e565b5f9291815491611bc683611b7f565b8083529260018116908115611c1b5750600114611be257505050565b5f9081526020812093945091925b838310611c01575060209250010190565b600181602092949394548385870101520191019190611bf0565b915050602093945060ff929192191683830152151560051b010190565b90610e86611c4c9260405193848092611bb7565b03836112cd565b34610658576040366003190112610658576106d8611cb3611cba611ca7600435611c7c8161162f565b63ffffffff611c89611b5f565b91165f52600e60205260405f209061ffff165f5260205260405f2090565b60405192838092611bb7565b03826112cd565b604051918291602083526020830190610772565b34610658575f366003190112610658576020602154604051908152f35b34610658576040366003190112610658576020611d27600435611d0d8161162f565b6024359063ffffffff165f52600c60205260405f20541490565b6040519015158152f35b3461065857604036600319011261065857600435602435611d50614c6c565b818110611e8057601a54811015611e7157815b81811115611da757506040805192835260208301919091527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190819081016114ea565b806001611dc061109382945f52601960205260405f2090565b611dc9816115ef565b03611e6a57611dfe611deb611de6835f52601460205260405f2090565b612c96565b90546001600160a01b039160031b1c1690565b6001600160a01b038116151580611e4b575b611e1f575b5001915b91611d63565b611ab9611e40611e45926001600160a01b03165f52601160205260405f2090565b611c38565b5f611e15565b50611e6281610994845f52601660205260405f2090565b541515611e10565b0191611e19565b632d0483c560e21b5f5260045ffd5b63561ce9bb60e01b5f5260045ffd5b34610658575f36600319011261065857602060ff600b54166040519015158152f35b34610658575f3660031901126106585760206040516001600160a01b037f0000000000000000000000001a44076050125825900e736c501f859c50fe728c168152f35b3461065857611f023661136b565b611f0a614c6c565b805167ffffffffffffffff81116112ac57611f2f81611f2a602554611b7f565b613ddf565b602091601f8211600114611f6157611f5c925f91836114045750508160011b915f199060031b1c19161790565b602555005b60255f52601f198216927f401968ff42a154441da5f6c4c935ac46b8671f0e062baaa62a7545ba53bb6e4c915f5b858110611fc457508360019510611fac575b505050811b01602555005b01515f1960f88460031b161c191690555f8080611fa1565b91926020600181928685015181550194019201611f8f565b3461065857602036600319011261065857602061089b60043561444a565b34610658576080366003190112610658576044356064356004356024356120208261444a565b6001600160a01b0316331480159061243f575b801561242a575b8015612415575b61240657600161205c611093845f52601960205260405f2090565b612065816115ef565b148015906123de575b80156123b6575b801561238e575b61237f57612098611deb611de6845f52601460205260405f2090565b6001600160a01b036120bb6117cc611deb611de6865f52601460205260405f2090565b911614801561233b575b80156122f7575b80156122b3575b801561226f575b801561222b575b61221c576106d8937f970d89a1c58e731ff4e5dde020e440de9f7b2a5607ab648765eba29117be46d091611b4c601c54809661211e338830614ad0565b5061212a338530614ad0565b50612136338630614ad0565b50612142338230614ad0565b5061214d8233614ee9565b61215e61215861370e565b83614c96565b866121746110bd845f52601760205260405f2090565b558361218b6110e3845f52601760205260405f2090565b55846121a2611108845f52601760205260405f2090565b55806121b961112c845f52601760205260405f2090565b556121dc6121cf835f52601960205260405f2090565b805460ff19166002179055565b6121ef6121ea601c54613ca3565b601c55565b6040519485943398869192608093969594919660a084019784526020840152604083015260608201520152565b63ceb0e71d60e01b5f5260045ffd5b50612244611deb611de6865f52601460205260405f2090565b6001600160a01b036122676117cc611deb611de6885f52601460205260405f2090565b9116146120e1565b50612288611deb611de6835f52601460205260405f2090565b6001600160a01b036122ab6117cc611deb611de6885f52601460205260405f2090565b9116146120da565b506122cc611deb611de6835f52601460205260405f2090565b6001600160a01b036122ef6117cc611deb611de6895f52601460205260405f2090565b9116146120d3565b50612310611deb611de6845f52601460205260405f2090565b6001600160a01b036123336117cc611deb611de6885f52601460205260405f2090565b9116146120cc565b50612354611deb611de6845f52601460205260405f2090565b6001600160a01b036123776117cc611deb611de6895f52601460205260405f2090565b9116146120c5565b6323742a3d60e21b5f5260045ffd5b5060016123a6611093855f52601960205260405f2090565b6123af816115ef565b141561207c565b5060016123ce611093865f52601960205260405f2090565b6123d7816115ef565b1415612075565b5060016123f6611093835f52601960205260405f2090565b6123ff816115ef565b141561206e565b6318b5288760e31b5f5260045ffd5b50336124236117cc8561444a565b1415612041565b50336124386117cc8661444a565b141561203a565b503361244d6117cc8361444a565b1415612033565b3461065857602036600319011261065857612470600435613f48565b906106d8604051928392836109c5565b34610658576001600160a01b0361249636611a59565b91906124a0614c6c565b165f52601160205260405f20815167ffffffffffffffff81116112ac576124d1816124cb8454611b7f565b84613e17565b602092601f821160011461250457612500929382915f926114045750508160011b915f199060031b1c19161790565b9055005b601f19821693612517845f5260205f2090565b915f5b86811061254f5750836001959610612537575b505050811b019055005b01515f1960f88460031b161c191690555f808061252d565b9192602060018192868501518155019401920161251a565b34610658576020366003190112610658577ff0be4f1e87349231d80c36b33f9e8639658eeaf474014dee15a3e6a4d441419760206001600160a01b036004356125af816108ac565b6125b7614c6c565b16806001600160a01b0319600f541617600f55604051908152a1005b346106585760203660031901126106585760206125fa6004356125f5816108ac565b6140e6565b604051908152f35b34610658575f3660031901126106585761261a614c6c565b5f6001600160a01b03600b5474ffffffffffffffffffffffffffffffffffffffff00198116600b5560081c167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610658575f366003190112610658576020601a54604051908152f35b3461065857604036600319011261065857600435602435905f52601760205260405f20905f52602052602060405f2054604051908152f35b34610658576020366003190112610658576004355f526015602052602060405f2054604051908152f35b3461065857602036600319011261065857600435612707614c6c565b6127108111611e715760218054908290556040805191825260208201929092527fc24d648b8b29d6566f302551df9152ced5a44785e31f38d1d047c8168dd3451991819081016114ea565b346106585760403660031901126106585761276e60043561162f565b60206040515f8152f35b346106585760a03660031901126106585761279236610ae8565b5060643567ffffffffffffffff8111610658576127b3903690600401610af9565b505060206084356127c3816108ac565b604051906001600160a01b03309116148152f35b34610658575f366003190112610658576127ef614c6c565b6127f76159aa565b600160ff19600b541617600b557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b34610658576040366003190112610658576020612875600435612852816108ac565b6024355f526016835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b34610658575f3660031901126106585760206001600160a01b03600b5460081c16604051908152f35b34610658575f366003190112610658576020601f54604051908152f35b34610658576060366003190112610658576004356128e36024356108ac565b6128ee6044356108ac565b5f526018602052602060ff60405f20541615604051908152f35b34610658575f366003190112610658576020602254604051908152f35b34610658575f366003190112610658576040515f60015461294581611b7f565b8084529060018116908115610859575060011461296c576106d8836107f1818503826112cd565b60015f9081527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6939250905b8082106129b0575090915081016020016107f16107e1565b919260018160209254838588010152019101909291612998565b346106585760403660031901126106585760206128756024356004356129ef826108ac565b5f526016835260405f20906001600160a01b03165f5260205260405f2090565b34610658576020366003190112610658576004355f81815260176020818152604080842084805280835281852054600186528286205460028752928620549690955292909152926106d891612a639061112c565b5490604051948594859094939260609260808301968352602083015260408201520152565b34610658575f3660031901126106585760206040515f8152f35b3461065857602036600319011261065857600435612abf816108ac565b6022544311612bc257601b54612ada601e54601f5490613cb1565b811015612bb3576106d891612af6612af183613ca3565b601b55565b612b036020543384615031565b612b1d612b18835f52601460205260405f2090565b614117565b612b3f612b32835f52601960205260405f2090565b805460ff19166004179055565b612b498282614ee9565b612b54612158613721565b6020547fad44b48ec853921e6665d22a88bc31c245479a93dea33a944cc894a12e9785e36001600160a01b0360405193169280612ba03394878360209093929193604081019481520152565b0390a36040519081529081906020820190565b638564324160e01b5f5260045ffd5b63e1be3ef160e01b5f5260045ffd5b8015150361065857565b3461065857604036600319011261065857600435612bf8816108ac565b602435612c0481612bd1565b6001600160a01b038216918215612c6f5781610cb3612c3f92335f52600560205260405f20906001600160a01b03165f5260205260405f2090565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b82630b61174360e31b5f5260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b805415612ca8575f5260205f20905f90565b612c82565b8054821015612ca8575f5260205f2001905f90565b3461065857604036600319011261065857600435602435905f52601460205260405f2090815481101561065857612d036001600160a01b0391602093612cad565b90549060031b1c16604051908152f35b34610658575f36600319011261065857604080516311f0c6d360e11b815260016020820152f35b34610658575f366003190112610658576020601c54604051908152f35b34610658575f36600319011261065857602060405160028152f35b3461065857604036600319011261065857600435612d8f816108ac565b602435612d9a614c6c565b612db5826001600160a01b03165f52601060205260405f2090565b5491818314611a225781612dda826001600160a01b03165f52601060205260405f2090565b558215612f48575b8115612e2e575b6040805193845260208401929092526001600160a01b0316917fb25ad585c6056b2cbc03e32592585455393544dd9097ed06d01ff7238ff9fd5591819081015b0390a2005b601d54905f196001600160a01b0382165f5b848110612f12575b50505f198114612f03577fb25ad585c6056b2cbc03e32592585455393544dd9097ed06d01ff7238ff9fd5593612ebf612e2992610942612eb0612ea3612e956001600160a01b039961419b565b5f52601360205260405f2090565b546001600160a01b031690565b915f52601360205260405f2090565b612ed2612ecd601d5461419b565b601d55565b612ef9612ee9601d545f52601360205260405f2090565b6001600160a01b03198154169055565b9350509050612de9565b63350b944160e11b5f5260045ffd5b816001600160a01b03612f30612ea3845f52601360205260405f2090565b1614612f3e57600101612e40565b9150505f80612e48565b612f6081610942601d545f52601360205260405f2090565b612f6e612ecd601d54613ca3565b612de2565b346106585760a036600319011261065857612f8f6004356108ac565b612f9a6044356108ac565b60843567ffffffffffffffff811161065857612fba903690600401611350565b50636fd4b3b160e11b5f5260045ffd5b34610658575f3660031901126106585760208054604051908152f35b34610658575f3660031901126106585760206001600160a01b03600d5416604051908152f35b3461065857608036600319011261065857600435613029816108ac565b60243590613036826108ac565b6044356064359267ffffffffffffffff84116106585761305d61095d943690600401611350565b926117a6838383613d24565b3461065857604036600319011261065857600435602435613088614c6c565b601e548210611e7157818110611e8057601b54811015611e7157815b818111156130e857506040805192835260208301919091527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190819081016114ea565b806131146131006001935f52601660205260405f2090565b6002603f609a1b015f5260205260405f2090565b54613120575b016130a4565b61312b611ab9613721565b61311a565b9060206003198301126106585760043567ffffffffffffffff81116106585760040182601f820112156106585780359267ffffffffffffffff8411610658576020808301928560051b010111610658579190565b346106585761319236613130565b9061319b614c6c565b6131a482613ba8565b906131b260405192836112cd565b828252602082019260051b8101903682116106585780935b8285106131da5761095d8461549e565b843567ffffffffffffffff8111610658578201606081360312610658576040519161320483611290565b813561320f8161162f565b835261321d60208301611b70565b602084015260408201359267ffffffffffffffff841161065857613248602094938594369101611350565b60408201528152019401936131ca565b346106585760203660031901126106585763ffffffff60043561327a8161162f565b165f52600c602052602060405f2054604051908152f35b34610658575f366003190112610658576020601d54604051908152f35b34610658576060366003190112610658576004356132cb8161162f565b6132d3611b5f565b6044359067ffffffffffffffff8211610658576106d8926132fb611cba933690600401610af9565b929091614238565b61330c36613130565b5f5b81811061338057604051638e9e709960e01b81525f81600481335afa801561337b57613355915f91613359575b50604051638351eea760e01b815291829160048301610796565b0390fd5b61337591503d805f833e61336d81836112cd565b810190614405565b8261333b565b6143c4565b61338b8183856142da565b906133b7610c2161339b84613c72565b60208501359063ffffffff165f52600c60205260405f20541490565b6134555760c08201359160a08101356133d46101008301836142fd565b9490916133e360e08501614330565b936133f26101208201826142fd565b959095303b15610658575f9661341f926040519a8b9889978897633411683760e21b89526004890161434c565b0391305af191821561337b5760019261343b575b505b0161330e565b806134495f61344f936112cd565b80610768565b5f613433565b60019150613435565b346106585760403660031901126106585760043567ffffffffffffffff811161065857604061349461351d923690600401610e3d565b6135026024356134a381612bd1565b6134ab613cbe565b506134b58361454e565b909335936134c28561162f565b6134ca613cbe565b506134d48561447d565b6134eb6134df6112ef565b63ffffffff9097168752565b602086015285850152606084015215156080830152565b815180938192631bb8518b60e31b835230906004840161471f565b03816001600160a01b037f0000000000000000000000001a44076050125825900e736c501f859c50fe728c165afa801561337b576106d8915f9161357b575b5060405191829182815181526020918201519181019190915260400190565b61359d915060403d6040116135a3575b61359581836112cd565b810190615593565b5f61355c565b503d61358b565b34610658576020366003190112610658576106d8611cba6004356155a7565b34610658575f6020366003190112610658576004356135e7816108ac565b6135ef614c6c565b6001600160a01b037f0000000000000000000000001a44076050125825900e736c501f859c50fe728c1690813b15610658576001600160a01b0360245f9283604051958694859363ca5eb5e160e01b85521660048401525af1801561337b57613656575080f35b61095d91505f906112cd565b61366b36610b27565b505050923033036136935783602011610658576001600160a01b03823516610c138584615794565b63029a949d60e31b5f5260045ffd5b34610658576020366003190112610658577fd48d879cef83a1c0bdda516f27b13ddb1b3f8bbac1c9e1511bb2a659c242776060206001600160a01b036004356136ea816108ac565b6136f2614c6c565b16806001600160a01b0319600d541617600d55604051908152a1005b60405190610e8682611c4c816025611bb7565b60405190610e8682611c4c816024611bb7565b34610658576020366003190112610658576001600160a01b03600435613759816108ac565b165f5260116020526106d8611cb3611cba60405f2060405192838092611bb7565b3461065857602036600319011261065857600435613797816108ac565b6022544311612bc257601a54601e54811015613865576137c8826001600160a01b03165f52601060205260405f2090565b5415612f0357613855611ab9611e406106d8946137ec6137e78661443c565b601a55565b6137f73386836155e1565b6138128161380d875f52601460205260405f2090565b61415b565b613834613827865f52601960205260405f2090565b805460ff19166001179055565b61383e8533614ee9565b6001600160a01b03165f52601160205260405f2090565b6040519081529081906020820190565b636414c23f60e01b5f5260045ffd5b3461065857602060ff6138b36001600160a01b0361389136611968565b91165f526005845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b34610658576040366003190112610658576004356024356138de614c6c565b818110611e80576138f4601e54601f5490613cb1565b8210611e7157601c54811015611e7157815b8181111561394a57506040805192835260208301919091527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c9190819081016114ea565b806139626110bd6001935f52601760205260405f2090565b5415801590613987575b613977575b01613906565b613982611ab961370e565b613971565b5061399d6110e3825f52601760205260405f2090565b54151561396c565b34610658576020366003190112610658576001600160a01b036004356139ca816108ac565b165f52601060205260405f2054612710610d5960215483613c90565b3461065857602036600319011261065857600435613a03816108ac565b613a0b614c6c565b6001600160a01b038116908115613a8a576001600160a01b039074ffffffffffffffffffffffffffffffffffffffff00600b549160081b1674ffffffffffffffffffffffffffffffffffffffff0019821617600b5560081c167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b631e4fbdf760e01b5f525f60045260245ffd5b3461065857608036600319011261065857613ab96004356108ac565b60643567ffffffffffffffff811161065857613ad9903690600401611350565b50633a72840160e21b5f5260045ffd5b34610658575f3660031901126106585760206040516127108152f35b34610658576020366003190112610658576001600160a01b03600435613b2a816108ac565b165f526010602052602060405f2054604051908152f35b34610658576060366003190112610658576020613b5d36610ae8565b63ffffffff8135613b6d8161162f565b165f52600c82528160405f205491013560405191148152f35b613b8f8161444a565b505f5260046020526001600160a01b0360405f20541690565b67ffffffffffffffff81116112ac5760051b60200190565b60405190613bcf6020836112cd565b5f808352366020840137565b60408051909190613bec83826112cd565b6001815291601f1901366020840137565b60405160a09190613c0e83826112cd565b6004815291601f1901366020840137565b90613c2982613ba8565b613c3660405191826112cd565b8281528092613c47601f1991613ba8565b0190602036910137565b805115612ca85760200190565b8051821015612ca85760209160051b010190565b356107a78161162f565b634e487b7160e01b5f52601160045260245ffd5b81810292918115918404141715610d8657565b9060018201809211610d8657565b91908201809211610d8657565b60405190613ccb826112b1565b5f6020838281520152565b60405190613ce382611290565b815f81525f60208201526040613cf7613cbe565b910152565b60409060231901126106585760405190613d15826112b1565b60243582526044356020830152565b91906001600160a01b03811615613d73576001600160a01b03613d4a8192843391614ad0565b9316921691808303613d5b57505050565b6364283d7b60e01b5f5260045260245260445260645ffd5b633250574960e11b5f525f60045260245ffd5b818110613d91575050565b5f8155600101613d86565b90601f8211613da9575050565b610e869160245f5260205f20906020601f840160051c83019310613dd5575b601f0160051c0190613d86565b9091508190613dc8565b90601f8211613dec575050565b610e869160255f5260205f20906020601f840160051c83019310613dd557601f0160051c0190613d86565b9190601f8111613e2657505050565b610e86925f5260205f20906020601f840160051c83019310613dd557601f0160051c0190613d86565b90604051918281549182825260208201905f5260205f20925f5b818110613e7e575050610e86925003836112cd565b84546001600160a01b0316835260019485019487945060209093019201613e69565b80545f825580613eae575050565b610e86915f5260205f2090810190613d86565b6001600160a01b0316805f52601260205260405f2054918215613f4357815f5260126020525f6040812055613ef7838284614ead565b15613f34576040519283526001600160a01b0316917f294b789b8defca4b7d7312b11f70ca28a93587f39fa40bdde2106b5ee3f94b7790602090a3565b6312171d8360e31b5f5260045ffd5b505050565b90613f5e611093835f52601960205260405f2090565b613f67816115ef565b156115cc576003613f83611093845f52601960205260405f2090565b613f8c816115ef565b146140d3576001613fa8611093845f52601960205260405f2090565b613fb1816115ef565b1480156140ac575b61406057613fc5613bfd565b613fcd613bfd565b925f5b60048110613fde5750509190565b8061404e614007600193613ffa865f52601760205260405f2090565b905f5260205260405f2090565b54610994614023611deb611de6845f52601460205260405f2090565b9161404083614032878b613c5e565b906001600160a01b03169052565b5f52601660205260405f2090565b546140598288613c5e565b5201613fd0565b614068613bdb565b61409d614073613bdb565b9361099461408f611deb611de6845f52601460205260405f2090565b916140408361403287613c51565b546140a784613c51565b529190565b5060046140c4611093845f52601960205260405f2090565b6140cd816115ef565b14613fb9565b90506140dd613bc0565b906107a7613bc0565b6001600160a01b03168015614104575f52600360205260405f205490565b6322718ad960e21b5f525f60045260245ffd5b8054680100000000000000008110156112ac5761413991600182018155612cad565b81549060031b906001600160a01b036002603f609a1b01831b921b1916179055565b8054680100000000000000008110156112ac5761417d91600182018155612cad565b6001600160a01b0380839493549260031b9316831b921b1916179055565b5f19810191908211610d8657565b909291928360021161065857831161065857600201916001190190565b805191908290602001825e015f815290565b6141f29392610e86928160405196879460208601906141c6565b918237015f815203601f1981018452836112cd565b908060209392818452848401375f828201840152601f01601f1916010190565b9160206107a7938181520191614207565b611ca7611cb39263ffffffff6142699397959697165f52600e60205260405f209061ffff165f5260205260405f2090565b8051156142cc5783156142c657600284101561429d575050613355604051928392639a6d49cd60e01b845260048401614227565b9092806142c0916107a7946142bb6142b636848461131a565b61555b565b6141a9565b916141d8565b92509050565b5091906107a791369161131a565b9190811015612ca85760051b8101359061013e1981360301821215610658570190565b903590601e1981360301821215610658570180359067ffffffffffffffff82116106585760200191813603831361065857565b356107a7816108ac565b67ffffffffffffffff81160361065857565b926107a7979596946001600160a01b039367ffffffffffffffff60406143b09563ffffffff813561437c8161162f565b168952602081013560208a015201356143948161433a565b166040870152606086015260e0608086015260e0850191614207565b941660a082015260c0818503910152614207565b6040513d5f823e3d90fd5b9291926143db826112fe565b916143e960405193846112cd565b829481845281830111610658578281602093845f96015e010152565b6020818303126106585780519067ffffffffffffffff821161065857019080601f830112156106585781516107a7926020016143cf565b5f198114610d865760010190565b805f5260026020526001600160a01b0360405f20541690811561446b575090565b637e27328960e01b5f5260045260245ffd5b63ffffffff16805f52600c60205260405f205490811561449b575090565b63f6ff4fb760e01b5f5260045260245ffd5b919060408382031261065857825167ffffffffffffffff811161065857830181601f820112156106585760209181836144e8935191016143cf565b9201516107a781612bd1565b9061450c602091949394604084526040840190610772565b931515910152565b9081602091031261065857516107a781612bd1565b90916145406107a793604084526040840190610772565b916020818403910152610772565b60208101359182156146a6576040820135926145a461456c856155a7565b614581611093875f52601860205260405f2090565b918291614596604051948592602084016144f4565b03601f1981018452836112cd565b8061467e575b61466f576145e1946145bb92615810565b93909283945f14614667576002905b6132fb6145d682613c72565b9160608101906142fd565b916001600160a01b036145fc600f546001600160a01b031690565b1690838261460957505050565b60405163043a78eb60e01b81529260209284928391829161462d9160048401614529565b03915afa801561337b5761463f575b50565b61463c9060203d602011614660575b61465881836112cd565b810190614514565b503d61464e565b6001906145ca565b631b67d22160e01b5f5260045ffd5b506001600160a01b0382166001600160a01b0361469d6117cc8861444a565b911614156145aa565b631e4ec46b60e01b5f5260045ffd5b9190826040910312610658576040516146cd816112b1565b6020808294805184520151910152565b90608082820312610658576147179060408051936146fa85611290565b80518552602081015161470c8161433a565b6020860152016146b5565b604082015290565b906001600160a01b036020919493946040845263ffffffff8151166040850152828101516060850152608061477b614765604084015160a08489015260e0880190610772565b6060840151878203603f190160a0890152610772565b910151151560c08501529416910152565b6147e96080949261480596946147a0613cd6565b5060206147ad8651615889565b95019182518061486d575b506147c28561447d565b92511515926147d26134df6112ef565b602086015260408501526060840152151585830152565b6040518095819482936302637a4560e41b84526004840161471f565b03916001600160a01b037f0000000000000000000000001a44076050125825900e736c501f859c50fe728c165af190811561337b575f91614844575090565b6107a7915060803d608011614866575b61485e81836112cd565b8101906146dd565b503d614854565b614876906158a6565b5f6147b8565b60ff602354161580614978575b61466f576107a7906148996159aa565b5f818152600260205260409020546001600160a01b031692816001600160a01b038216614967575b50506001600160a01b0383168015825f82159384614937575b6148f082610942855f52600260205260405f2090565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a415614928575061492381615b1c565b615bbc565b15615bbc576149238184615a98565b61494083615772565b61495b886001600160a01b03165f52600360205260405f2090565b821981540190556148da565b6149719185615cab565b5f816148c1565b50805f52601860205260ff60405f205416614889565b60ff602354161580614aba575b61466f576149a76159aa565b5f828152600260205260409020546001600160a01b0316918215838115614a8a575b6001600160a01b038416918215908115614a63575b6149f486610942875f52600260205260405f2090565b8484847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a415614a4d57614a2984615b1c565b15614a3a5750506107a79150615bbc565b03614a4457505090565b6107a791615b78565b818314614a2957614a5e8487615a98565b614a29565b614a7e866001600160a01b03165f52600360205260405f2090565b600181540190556149de565b614a9383615772565b614aae856001600160a01b03165f52600360205260405f2090565b80545f190190556149c9565b50815f52601860205260ff60405f20541661499b565b9060ff602354161580614b62575b61466f57614aea6159aa565b5f818152600260205260409020546001600160a01b031692816001600160a01b038216614b51575b50506001600160a01b0383168015908115614a8a576001600160a01b038416918215908115614a63576149f486610942875f52600260205260405f2090565b614b5b9185615cab565b5f81614b12565b50805f52601860205260ff60405f205416614ade565b60ff602354161580614c56575b61466f57614b916159aa565b5f818152600260205260408120546001600160a01b031680159182159084908383614c26575b614bcd82610942855f52600260205260405f2090565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a48215614c125750614c0183615b1c565b614c0a83615bbc565b5061446b5750565b15614c0157614c218382615a98565b614c01565b614c2f83615772565b614c4a856001600160a01b03165f52600360205260405f2090565b82198154019055614bb7565b50805f52601860205260ff60405f205416614b85565b6001600160a01b03600b5460081c163303614c8357565b63118cdaa760e01b5f523360045260245ffd5b919091805f52600a60205260405f20835167ffffffffffffffff81116112ac57614cc4816124cb8454611b7f565b6020601f8211600114614d3d5781614d38949392614d19927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce797985f926114045750508160011b915f199060031b1c19161790565b90555b6040518181528390602090a16040519081529081906020820190565b0390a1565b601f19821695614d50845f5260205f2090565b965f5b818110614db55750966001928492614d389796957ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7999a10614d9d575b505050811b019055614d1c565b01515f1960f88460031b161c191690555f8080614d90565b83830151895560019098019760209384019301614d53565b90916001600160a01b03821691835f52601660205284614e018260405f20906001600160a01b03165f5260205260405f2090565b5410614e9357614e1d90610994855f52601660205260405f2090565b805490858203918211610d865755614e3d835f52601560205260405f2090565b614e47815461443c565b9055614e57610c21858385614ead565b613f34576040519384526001600160a01b0316927ff08722dda679d80a9dd519f28c27aeaa8d437512336f6fa9aea15e4e1f95690890602090a4565b631e9acf1760e31b5f5260045ffd5b61463c90339061487c565b6107a7926001600160a01b036040519363a9059cbb60e01b6020860152166024840152604483015260448252614ee46064836112cd565b615963565b604051602092614ef984836112cd565b5f82526001600160a01b038316908115613d73576001600160a01b03614f1f828661498e565b1661500f57833b614f32575b5050505050565b918495939491614f5b935f604051809681958294630a85bd0160e11b845284336004860161523e565b03925af15f9181614fe0575b50614fa0575050614f7661526c565b8051929083614f9b57633250574960e11b5f526001600160a01b03831660045260245ffd5b019050fd5b9092506001600160e01b03191663757a42ff60e11b01614fc557505f80808080614f2b565b633250574960e11b5f526001600160a01b031660045260245ffd5b615001919250853d8711615008575b614ff981836112cd565b810190615229565b905f614f67565b503d614fef565b6339e3563760e11b5f525f60045260245ffd5b90816020910312610658575190565b91909161504b61504360215484613c90565b612710900490565b926150568484613cb1565b604051636eb1769f60e11b81526001600160a01b03831660048201523060248201526020816044816002603f609a1b015afa801561337b5782915f9161520a575b50106151fb576040516370a0823160e01b81526001600160a01b03831660048201526020816024816002603f609a1b015afa801561337b5782915f916151cc575b5010614e9357610c216150f49130846002603f609a1b016159c5565b613f34576151b4936002603f609a1b019383857f684ce28ace37552c6bfb98b7cceda8ed55327078eafb5dfb31218e08563827636001600160a01b036151c79661516b846109947ff228de527fc1b9843baac03b9a04565473a263375950e63435d4138464386f469b5f52601660205260405f2090565b615176878254613cb1565b9055615193846001600160a01b03165f52601260205260405f2090565b61519e888254613cb1565b9055604051958652169889949081906020820190565b0390a46040519081529081906020820190565b0390a3565b6151ee915060203d6020116151f4575b6151e681836112cd565b810190615022565b5f6150d8565b503d6151dc565b6313be252b60e01b5f5260045ffd5b615223915060203d6020116151f4576151e681836112cd565b5f615097565b9081602091031261065857516107a78161065c565b90926001600160a01b03608093816107a7979616845216602083015260408201528160608201520190610772565b3d15615296573d9061527d826112fe565b9161528b60405193846112cd565b82523d5f602084013e565b606090565b939193843b6152ab575050505050565b6152cc6020936040519586948594630a85bd0160e11b86526004860161523e565b03815f6001600160a01b0387165af15f918161533f575b5061531d57506152f161526c565b805191908261531657633250574960e11b5f526001600160a01b03821660045260245ffd5b6020915001fd5b6001600160e01b03191663757a42ff60e11b01614fc557505f80808080614f2b565b61535991925060203d60201161500857614ff981836112cd565b905f6152e3565b919091825167ffffffffffffffff81116112ac57615382816124cb8454611b7f565b6020601f82116001146153b55781906153b19394955f926114045750508160011b915f199060031b1c19161790565b9055565b601f198216906153c8845f5260205f2090565b915f5b818110615402575095836001959697106153ea575b505050811b019055565b01515f1960f88460031b161c191690555f80806153e0565b9192602060018192868b0151815501940192016153cb565b602081016020825282518091526040820191602060408360051b8301019401925f915b83831061544c57505050505090565b909192939460208061548f600193603f19868203018752606060408b5163ffffffff815116845261ffff8682015116868501520151918160408201520190610772565b9701930193019193929061543d565b5f5b815181101561552957806154c360406154bb60019486613c5e565b51015161555b565b61552360406154d28386613c5e565b51015163ffffffff6154e48487613c5e565b5151165f52600e60205261551e60405f2061550e6020615504878a613c5e565b51015161ffff1690565b61ffff165f5260205260405f2090565b615360565b016154a0565b50614d387fbe4864a8e820971c0247f5992e2da559595f7bf076a21cb5928d443d2a13b674916040519182918261541a565b600361ffff6002830151160361556e5750565b604051639a6d49cd60e01b815260206004820152908190613355906024830190610772565b90604082820312610658576107a7916146b5565b6155b08161444a565b505f52600a602052611cb36155ce60405f2060405192838092611bb7565b5f6040516155dd6020826112cd565b5290565b9190916001600160a01b03811691825f52601060205260405f205461560b61504360215483613c90565b906156168282613cb1565b604051636eb1769f60e11b81526001600160a01b03851660048201523060248201526020816044818a5afa801561337b5782915f91615753575b50106151fb576040516370a0823160e01b81526001600160a01b03851660048201526020816024818a5afa801561337b5782915f91615734575b5010614e9357610c2161569f913086896159c5565b613f345785857f684ce28ace37552c6bfb98b7cceda8ed55327078eafb5dfb31218e08563827636001600160a01b036151c796615193896157107ff228de527fc1b9843baac03b9a04565473a263375950e63435d4138464386f469b6109946151b49f5f52601660205260405f2090565b61571b898254613cb1565b90556001600160a01b03165f52601260205260405f2090565b61574d915060203d6020116151f4576151e681836112cd565b5f61568a565b61576c915060203d6020116151f4576151e681836112cd565b5f615650565b615784905f52600460205260405f2090565b80546001600160a01b0319169055565b90604011610658576020013590565b9080604011610658576107a7913691603f19019060400161131a565b815f5260026020526001600160a01b0360405f2054166157fd57906157f091600160ff19602354161760235561498e565b5060ff1960235416602355565b50636025413b60e01b5f5260045260245ffd5b825115801593906158545790615843615851926040519485936020850152604084015233606084015260808301906141c6565b03601f1981018352826112cd565b91565b50906040519160208301526040820152604081526158516060826112cd565b6157f091600160ff19602354161760235561487c565b8034036158935790565b6304fb820960e51b5f523460045260245ffd5b60405163393f876560e21b81527f0000000000000000000000001a44076050125825900e736c501f859c50fe728c6001600160a01b03169190602081600481865afa801561337b575f90615921575b6001600160a01b0391501691821561591257610e86923390615a07565b6329b99a9560e11b5f5260045ffd5b506020813d60201161595b575b8161593b602093836112cd565b81010312610658576001600160a01b039051615956816108ac565b6158f5565b3d915061592e565b905f602091828151910182855af1903d5f519083615982575b50505090565b919250906159a057506001600160a01b03163b15155b5f808061597c565b6001915014615998565b60ff600b54166159b657565b63d93c066560e01b5f5260045ffd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648201929092526107a791614ee48260848101614596565b6040516323b872dd60e01b60208083019182526001600160a01b0394851660248401529490931660448201526064810194909452925f9190615a4c8160848101615843565b519082855af1156143c4575f513d615a8f57506001600160a01b0381163b155b615a735750565b6001600160a01b0390635274afe760e01b5f521660045260245ffd5b60011415615a6c565b5f91615aee91615aa7816140e6565b9180855260076020526001600160a01b0360408620549216855260066020526040852091838103615af1575b5084526007602052836040812055905f5260205260405f2090565b55565b838652826020526040862054818752836020528060408820558652600760205260408620555f615ad3565b600854815f5260096020528060405f2055680100000000000000008110156112ac5760018101600855600854811015612ca85760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b90615b82826140e6565b5f19810192908311610d86576001600160a01b03165f52600660205260405f20825f526020528060405f20555f52600760205260405f2055565b6008545f19810191818311610d8657805f5260096020528160405f2054931015612ca85760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee282015491831015612ca8577ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee383018290555f91825260096020526040808320939093558152908120556008548015615c97575f19810190600854821015612ca85760085f8181527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee29092019190915555565b634e487b7160e01b5f52603160045260245ffd5b919091615cc96001600160a01b0384168015159081615d1157501590565b615cd257505050565b6001600160a01b0316615cf157637e27328960e01b5f5260045260245ffd5b63177e802f60e01b5f526001600160a01b0390911660045260245260445ffd5b6001600160a01b03841680821492508215615d50575b508115615d3357501590565b9050835f5260046020526001600160a01b0360405f205416141590565b9091505f52600560205260ff615d7a8660405f20906001600160a01b03165f5260205260405f2090565b5416905f615d2756fea26469706673582212207dcb15461067a727dde050264f041d9fda466983b311e0769ef86bc98ded819c64736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000cff4cb3e2f8f8b899d53ff9570d224253140fdda0000000000000000000000001a44076050125825900e736c501f859c50fe728c
-----Decoded View---------------
Arg [0] : _initialOwner (address): 0xCFf4CB3e2F8F8b899d53ff9570D224253140fdDA
Arg [1] : _lzEndpoint (address): 0x1a44076050125825900e736c501f859c50fE728c
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000cff4cb3e2f8f8b899d53ff9570d224253140fdda
Arg [1] : 0000000000000000000000001a44076050125825900e736c501f859c50fe728c
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$110,510.49
Net Worth in FRAX
110,623.743402
Token Allocations
WFRAX
42.86%
SFRXETH
27.64%
SFRXUSD
16.06%
Others
13.44%
Multichain Portfolio | 35 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.