Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 10,452 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Approval For... | 24005251 | 8 days ago | IN | 0 ETH | 0.00002498 | ||||
| Set Approval For... | 23989203 | 11 days ago | IN | 0 ETH | 0.00000571 | ||||
| Set Approval For... | 23989200 | 11 days ago | IN | 0 ETH | 0.00002387 | ||||
| Set Approval For... | 23989198 | 11 days ago | IN | 0 ETH | 0.00000518 | ||||
| Set Approval For... | 23989197 | 11 days ago | IN | 0 ETH | 0.00000543 | ||||
| Set Approval For... | 23856203 | 29 days ago | IN | 0 ETH | 0.00000192 | ||||
| Set Approval For... | 23718546 | 49 days ago | IN | 0 ETH | 0.00004854 | ||||
| Set Approval For... | 23604389 | 65 days ago | IN | 0 ETH | 0.00003891 | ||||
| Set Approval For... | 23604388 | 65 days ago | IN | 0 ETH | 0.00002702 | ||||
| Set Approval For... | 23471763 | 83 days ago | IN | 0 ETH | 0.00003105 | ||||
| Set Approval For... | 23452218 | 86 days ago | IN | 0 ETH | 0.00001795 | ||||
| Set Approval For... | 23373518 | 97 days ago | IN | 0 ETH | 0.00002993 | ||||
| Set Approval For... | 23329965 | 103 days ago | IN | 0 ETH | 0.00007571 | ||||
| Set Approval For... | 23311408 | 106 days ago | IN | 0 ETH | 0.00000373 | ||||
| Set Approval For... | 23179474 | 124 days ago | IN | 0 ETH | 0.00001575 | ||||
| Transfer From | 23160503 | 127 days ago | IN | 0 ETH | 0.00012463 | ||||
| Set Approval For... | 23144657 | 129 days ago | IN | 0 ETH | 0.00005625 | ||||
| Set Approval For... | 23144654 | 129 days ago | IN | 0 ETH | 0.00005601 | ||||
| Set Approval For... | 23144557 | 129 days ago | IN | 0 ETH | 0.00005688 | ||||
| Set Approval For... | 23033404 | 144 days ago | IN | 0 ETH | 0.0002096 | ||||
| Set Approval For... | 23002551 | 149 days ago | IN | 0 ETH | 0.00000724 | ||||
| Set Approval For... | 22939148 | 158 days ago | IN | 0 ETH | 0.00020733 | ||||
| Set Approval For... | 22896351 | 164 days ago | IN | 0 ETH | 0.00039076 | ||||
| Set Approval For... | 22483917 | 221 days ago | IN | 0 ETH | 0.00005192 | ||||
| Set Approval For... | 22432287 | 229 days ago | IN | 0 ETH | 0.00003836 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 15682623 | 1174 days ago | 233.727 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
GMC
Compiler Version
v0.8.15+commit.e14f2714
Optimization Enabled:
Yes with 100000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {OwnableUDS} from "UDS/auth/OwnableUDS.sol";
import {LibCrumbMap} from "../lib/LibCrumbMap.sol";
import {FxERC721MRoot} from "ERC721M/extensions/FxERC721MRoot.sol";
import {ERC20UDS as ERC20} from "UDS/tokens/ERC20UDS.sol";
import "solady/utils/ECDSA.sol";
import "solady/utils/LibString.sol";
error ExceedsLimit();
error TransferFailed();
error TimelockActive();
error IncorrectValue();
error MaxSupplyLocked();
error InvalidSignature();
error InvalidPriceUnits();
error WhitelistNotActive();
error PublicSaleNotActive();
error ContractCallNotAllowed();
/// @title Gangsta Mice City Root
/// @author phaze (https://github.com/0xPhaze)
contract GMC is OwnableUDS, FxERC721MRoot {
using ECDSA for bytes32;
using LibString for uint256;
using LibCrumbMap for LibCrumbMap.CrumbMap;
event SaleStateUpdate();
event FirstLegendaryRaffleEntered(address user);
event SecondLegendaryRaffleEntered(address user);
uint16 public constant MAX_PER_WALLET = 20;
uint256 public constant PURCHASE_LIMIT = 5;
uint256 public constant BRIDGE_RAFFLE_LOCK_DURATION = 24 hours;
uint256 private constant PRICE_UNIT = 0.001 ether;
uint256 private constant GENESIS_CLAIM = 555;
uint256 private immutable DEPLOY_TIMESTAMP;
bool public maxSupplyLocked;
uint16 public supply;
uint16 public maxSupply;
uint32 public mintStart;
uint8 private publicPriceUnits;
uint8 private whitelistPriceUnits;
address private signer;
string private baseURI;
string private postFixURI = ".json";
string private unrevealedURI = "ipfs://QmTv9VoXgkZxFcomTW3kN6CRryUPMfgeUkVekFszcd79gK/";
LibCrumbMap.CrumbMap gangs;
constructor(address checkpointManager, address fxRoot)
FxERC721MRoot("Gangsta Mice City", "GMC", checkpointManager, fxRoot)
{
__Ownable_init();
maxSupply = 6666;
signer = msg.sender;
DEPLOY_TIMESTAMP = block.timestamp;
publicPriceUnits = toPriceUnits(0.049 ether);
whitelistPriceUnits = toPriceUnits(0.039 ether);
}
/* ------------- view ------------- */
function totalSupply() public view override returns (uint256) {
return supply;
}
function publicPrice() public view returns (uint256) {
return toPrice(publicPriceUnits);
}
function whitelistPrice() public view returns (uint256) {
return toPrice(whitelistPriceUnits);
}
function gangOf(uint256 id) public view returns (uint256) {
return gangs.get(id);
}
/* ------------- external ------------- */
function mint(uint256 quantity, bool lock)
external
payable
onlyEOA
requireMintableSupply(quantity)
requireMintableByUser(quantity, MAX_PER_WALLET)
{
unchecked {
if (msg.value != publicPrice() * quantity) revert IncorrectValue();
if (block.timestamp < mintStart + 2 hours || mintStart == 0) revert PublicSaleNotActive();
mintWithPerks(msg.sender, quantity, lock);
}
}
function whitelistMint(
uint256 quantity,
bool lock,
uint256 limit,
bytes calldata signature
) external payable onlyEOA requireMintableSupply(quantity) requireMintableByUser(quantity, limit) {
unchecked {
if (!validSignature(signature, limit)) revert InvalidSignature();
if (mintStart + 2 hours < block.timestamp) revert WhitelistNotActive();
if (msg.value != whitelistPrice() * quantity) revert IncorrectValue();
mintWithPerks(msg.sender, quantity, lock);
}
}
function lockAndTransmit(address from, uint256[] calldata tokenIds) external {
unchecked {
if (tokenIds.length > 20) revert ExceedsLimit();
// don't repeat an unnecessary sload if we can avoid it
if (
tokenIds.length != 0 &&
block.timestamp < DEPLOY_TIMESTAMP + 1 weeks &&
block.timestamp < mintStart + 2 hours
) {
emit SecondLegendaryRaffleEntered(from);
}
_lockAndTransmit(from, tokenIds);
}
}
function unlockAndTransmit(address from, uint256[] calldata tokenIds) external {
if (tokenIds.length > 20) revert ExceedsLimit();
if (block.timestamp < DEPLOY_TIMESTAMP + 1 weeks && block.timestamp < mintStart + BRIDGE_RAFFLE_LOCK_DURATION) {
revert TimelockActive();
}
_unlockAndTransmit(from, tokenIds);
}
/* ------------- private ------------- */
function validSignature(bytes calldata signature, uint256 limit) private view returns (bool) {
bytes32 hash = keccak256(abi.encode(address(this), msg.sender, limit));
address recovered = hash.toEthSignedMessageHash().recover(signature);
return recovered != address(0) && recovered == signer;
}
function toPrice(uint16 priceUnits) private pure returns (uint256) {
unchecked {
return uint256(priceUnits) * PRICE_UNIT;
}
}
function toPriceUnits(uint256 price) private pure returns (uint8) {
unchecked {
uint256 units;
if (price % PRICE_UNIT != 0) revert InvalidPriceUnits();
if ((units = price / PRICE_UNIT) > type(uint8).max) revert InvalidPriceUnits();
return uint8(units);
}
}
function mintWithPerks(
address to,
uint256 quantity,
bool lock
) private {
unchecked {
if (quantity > 2) {
emit FirstLegendaryRaffleEntered(to);
if (supply < 500 + GENESIS_CLAIM) ++quantity;
}
if (lock && block.timestamp < mintStart + 2 hours) emit SecondLegendaryRaffleEntered(to);
if (lock) _mintLockedAndTransmit(to, quantity);
else _mint(to, quantity);
}
}
/* ------------- owner ------------- */
function pause() external onlyOwner {
mintStart = 0;
}
function lockMaxSupply() external onlyOwner {
maxSupplyLocked = true;
}
function setSigner(address addr) external onlyOwner {
signer = addr;
}
function setMaxSupply(uint16 value) external onlyOwner {
if (maxSupplyLocked) revert MaxSupplyLocked();
maxSupply = value;
}
function setMintStart(uint32 time) external onlyOwner {
mintStart = time;
}
function setPublicPrice(uint256 value) external onlyOwner {
publicPriceUnits = toPriceUnits(value);
}
function setBaseURI(string calldata uri) external onlyOwner {
baseURI = uri;
}
function setPostFixURI(string calldata postFix) external onlyOwner {
postFixURI = postFix;
}
function setWhitelistPrice(uint256 value) external onlyOwner {
whitelistPriceUnits = toPriceUnits(value);
}
function setUnrevealedURI(string calldata uri) external onlyOwner {
unrevealedURI = uri;
}
function setGangs(uint256[] calldata chunkIndices, uint256[] calldata chunks) external onlyOwner {
for (uint256 i; i < chunkIndices.length; ++i) gangs.set32BytesChunk(chunkIndices[i], chunks[i]);
}
function airdrop(
address[] calldata users,
uint256 quantity,
bool locked
) external onlyOwner requireMintableSupply(quantity * users.length) {
if (locked) for (uint256 i; i < users.length; ++i) _mintLockedAndTransmit(users[i], quantity);
else for (uint256 i; i < users.length; ++i) _mint(users[i], quantity);
}
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
(bool success, ) = msg.sender.call{value: balance}("");
if (!success) revert TransferFailed();
}
function recoverToken(ERC20 token) external onlyOwner {
uint256 balance = token.balanceOf(address(this));
token.transfer(msg.sender, balance);
}
/* ------------- override ------------- */
function _authorizeTunnelController() internal override onlyOwner {}
function _increaseTotalSupply(uint256 amount) internal override {
supply += uint16(amount);
}
/* ------------- modifier ------------- */
modifier onlyEOA() {
if (tx.origin != msg.sender) revert ContractCallNotAllowed();
_;
}
modifier requireMintableByUser(uint256 quantity, uint256 limit) {
unchecked {
if (quantity > PURCHASE_LIMIT) revert ExceedsLimit();
if (quantity + numMinted(msg.sender) > limit) revert ExceedsLimit();
}
_;
}
modifier requireMintableSupply(uint256 quantity) {
unchecked {
if (quantity + supply > maxSupply) revert ExceedsLimit();
}
_;
}
/* ------------- ERC721 ------------- */
function tokenURI(uint256 id) public view override returns (string memory) {
return
bytes(baseURI).length == 0
? unrevealedURI
: string.concat(baseURI, id.toString(), postFixURI); // prettier-ignore
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Context} from "../utils/Context.sol";
import {Initializable} from "../utils/Initializable.sol";
// ------------- storage
bytes32 constant DIAMOND_STORAGE_OWNABLE = keccak256("diamond.storage.ownable");
function s() pure returns (OwnableDS storage diamondStorage) {
bytes32 slot = DIAMOND_STORAGE_OWNABLE;
assembly { diamondStorage.slot := slot } // prettier-ignore
}
struct OwnableDS {
address owner;
}
// ------------- errors
error CallerNotOwner();
/// @title Ownable (Upgradeable Diamond Storage)
/// @author phaze (https://github.com/0xPhaze/UDS)
/// @dev Requires `__Ownable_init` to be called in proxy
abstract contract OwnableUDS is Context, Initializable {
OwnableDS private __storageLayout; // storage layout for upgrade compatibility checks
event OwnerChanged(address oldOwner, address newOwner);
function __Ownable_init() internal initializer {
s().owner = _msgSender();
}
/* ------------- external ------------- */
function owner() public view returns (address) {
return s().owner;
}
function transferOwnership(address newOwner) external onlyOwner {
s().owner = newOwner;
emit OwnerChanged(_msgSender(), newOwner);
}
/* ------------- modifier ------------- */
modifier onlyOwner() {
if (_msgSender() != s().owner) revert CallerNotOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
using LibCrumbMap for LibCrumbMap.CrumbMap;
/// @notice Efficient crumb map library for mapping integers to crumbs.
/// @author phaze (https://github.com/0xPhaze)
/// @author adapted from Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytemap.sol)
library LibCrumbMap {
struct CrumbMap {
mapping(uint256 => uint256) map;
}
/* ------------- CrumbMap ------------- */
function get(CrumbMap storage crumbMap, uint256 index) internal view returns (uint256 result) {
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, shr(7, index))
result := and(shr(shl(1, and(index, 0x7f)), sload(keccak256(0x00, 0x20))), 0x03)
}
}
function get32BytesChunk(CrumbMap storage crumbMap, uint256 bytesIndex) internal view returns (uint256 result) {
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, bytesIndex)
result := sload(keccak256(0x00, 0x20))
}
}
function set32BytesChunk(
CrumbMap storage crumbMap,
uint256 bytesIndex,
uint256 value
) internal {
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, bytesIndex)
sstore(keccak256(0x00, 0x20), value)
}
}
function set(
CrumbMap storage crumbMap,
uint256 index,
uint256 value
) internal {
require(value < 4);
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, shr(7, index))
let storageSlot := keccak256(0x00, 0x20)
let shift := shl(1, and(index, 0x7f))
// Unset crumb at index and store.
let chunkValue := and(sload(storageSlot), not(shl(shift, 0x03)))
// Set crumb to `value` at index and store.
chunkValue := or(chunkValue, shl(shift, value))
sstore(storageSlot, chunkValue)
}
}
/* ------------- mapping(uint256 => uint256) ------------- */
function get(mapping(uint256 => uint256) storage crumbMap, uint256 index) internal view returns (uint256 result) {
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, shr(7, index))
result := and(shr(shl(1, and(index, 0x7f)), sload(keccak256(0x00, 0x20))), 0x03)
}
}
function get32BytesChunk(mapping(uint256 => uint256) storage crumbMap, uint256 bytesIndex) internal view returns (uint256 result) {
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, bytesIndex)
result := sload(keccak256(0x00, 0x20))
}
}
function set32BytesChunk(
mapping(uint256 => uint256) storage crumbMap,
uint256 bytesIndex,
uint256 value
) internal {
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, bytesIndex)
sstore(keccak256(0x00, 0x20), value)
}
}
function set(
mapping(uint256 => uint256) storage crumbMap,
uint256 index,
uint256 value
) internal {
require(value < 4);
assembly {
mstore(0x20, crumbMap.slot)
mstore(0x00, shr(7, index))
let storageSlot := keccak256(0x00, 0x20)
let shift := shl(1, and(index, 0x7f))
// Unset crumb at index and store.
let chunkValue := and(sload(storageSlot), not(shl(shift, 0x03)))
// Set crumb to `value` at index and store.
chunkValue := or(chunkValue, shl(shift, value))
sstore(storageSlot, chunkValue)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ERC721M} from "../ERC721M.sol";
import {ERC721MQuery} from "./ERC721MQuery.sol";
import {FxERC721Root} from "fx-contracts/FxERC721Root.sol";
/// @title ERC721M FxPortal extension
/// @author phaze (https://github.com/0xPhaze/ERC721M)
abstract contract FxERC721MRoot is FxERC721Root, ERC721M, ERC721MQuery {
constructor(
string memory name,
string memory symbol,
address checkpointManager,
address fxRoot
) ERC721M(name, symbol) FxERC721Root(checkpointManager, fxRoot) {}
/* ------------- virtual ------------- */
function tokenURI(uint256 id) external view virtual override returns (string memory);
function _authorizeTunnelController() internal virtual override;
/* ------------- internal ------------- */
function _mintLockedAndTransmit(address to, uint256 quantity) internal virtual {
_mintLockedAndTransmit(to, quantity, 0);
}
function _mintLockedAndTransmit(
address to,
uint256 quantity,
uint48 auxData
) internal virtual {
uint256 startId = _nextTokenId();
_mintAndLock(to, quantity, true, auxData);
uint256[] memory ids = new uint256[](quantity);
unchecked {
for (uint256 i; i < quantity; ++i) {
ids[i] = startId + i;
}
}
_registerERC721IdsWithChildMem(to, ids);
}
function _lockAndTransmit(address from, uint256[] calldata ids) internal virtual {
unchecked {
for (uint256 i; i < ids.length; ++i) {
_lock(from, ids[i]);
}
}
_registerERC721IdsWithChild(from, ids);
}
// @notice using `_unlockAndTransmit` is simple and easy
// this assumes L1 state as the single source of truth
// messages are always pushed L1 -> L2 without knowing state on L2
// this means that NFTs should not be allowed to be traded/sold on L2
// alternatively `_unlockWithProof` should be implemented requiring
// a MPT inclusion proof.
function _unlockAndTransmit(address from, uint256[] calldata ids) internal virtual {
unchecked {
for (uint256 i; i < ids.length; ++i) _unlock(from, ids[i]);
}
_registerERC721IdsWithChild(address(0), ids);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Context} from "../utils/Context.sol";
import {Initializable} from "../utils/Initializable.sol";
import {EIP712PermitUDS} from "../auth/EIP712PermitUDS.sol";
// ------------- storage
bytes32 constant DIAMOND_STORAGE_ERC20 = keccak256("diamond.storage.erc20");
function s() pure returns (ERC20DS storage diamondStorage) {
bytes32 slot = DIAMOND_STORAGE_ERC20;
assembly { diamondStorage.slot := slot } // prettier-ignore
}
struct ERC20DS {
string name;
string symbol;
uint8 decimals;
uint256 totalSupply;
mapping(address => uint256) balanceOf;
mapping(address => mapping(address => uint256)) allowance;
}
/// @title ERC20 (Upgradeable Diamond Storage)
/// @author phaze (https://github.com/0xPhaze/UDS)
/// @author Modified from Solmate (https://github.com/Rari-Capital/solmate)
abstract contract ERC20UDS is Context, Initializable, EIP712PermitUDS {
ERC20DS private __storageLayout; // storage layout for upgrade compatibility checks
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed operator, uint256 amount);
/* ------------- init ------------- */
function __ERC20_init(
string memory _name,
string memory _symbol,
uint8 _decimals
) internal initializer {
s().name = _name;
s().symbol = _symbol;
s().decimals = _decimals;
}
/* ------------- view ------------- */
function name() external view virtual returns (string memory) {
return s().name;
}
function symbol() external view virtual returns (string memory) {
return s().symbol;
}
function decimals() external view virtual returns (uint8) {
return s().decimals;
}
function totalSupply() external view virtual returns (uint256) {
return s().totalSupply;
}
function balanceOf(address owner) public view virtual returns (uint256) {
return s().balanceOf[owner];
}
function allowance(address owner, address operator) public view virtual returns (uint256) {
return s().allowance[owner][operator];
}
/* ------------- public ------------- */
function approve(address operator, uint256 amount) public virtual returns (bool) {
s().allowance[_msgSender()][operator] = amount;
emit Approval(_msgSender(), operator, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
s().balanceOf[_msgSender()] -= amount;
unchecked {
s().balanceOf[to] += amount;
}
emit Transfer(_msgSender(), to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = s().allowance[from][_msgSender()];
if (allowed != type(uint256).max) s().allowance[from][_msgSender()] = allowed - amount;
s().balanceOf[from] -= amount;
unchecked {
s().balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
// EIP-2612 permit
function permit(
address owner,
address operator,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s_
) public virtual {
_usePermit(owner, operator, value, deadline, v, r, s_);
s().allowance[owner][operator] = value;
emit Approval(owner, operator, value);
}
/* ------------- internal ------------- */
function _mint(address to, uint256 amount) internal virtual {
s().totalSupply += amount;
unchecked {
s().balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
s().balanceOf[from] -= amount;
unchecked {
s().totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
library ECDSA {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The number which `s` must not exceed in order for
/// the signature to be non-malleable.
bytes32 private constant _MALLEABILITY_THRESHOLD =
0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RECOVERY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the `signature`.
///
/// This function does NOT accept EIP-2098 short form signatures.
/// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
/// short form signatures instead.
///
/// WARNING!
/// The `result` will be the zero address upon recovery failure.
/// As such, it is extremely important to ensure that the address which
/// the `result` is compared against is never zero.
function recover(bytes32 hash, bytes calldata signature) internal view returns (address result) {
assembly {
if eq(signature.length, 65) {
// Copy the free memory pointer so that we can restore it later.
let m := mload(0x40)
// Directly copy `r` and `s` from the calldata.
calldatacopy(0x40, signature.offset, 0x40)
// If `s` in lower half order, such that the signature is not malleable.
if iszero(gt(mload(0x60), _MALLEABILITY_THRESHOLD)) {
mstore(0x00, hash)
// Compute `v` and store it in the scratch space.
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40))))
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
0x01, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x40, // Start of output.
0x20 // Size of output.
)
)
// Restore the zero slot.
mstore(0x60, 0)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(sub(0x60, returndatasize()))
}
// Restore the free memory pointer.
mstore(0x40, m)
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
///
/// This function only accepts EIP-2098 short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
///
/// To be honest, I do not recommend using EIP-2098 signatures
/// for simplicity, performance, and security reasons. Most if not
/// all clients support traditional non EIP-2098 signatures by default.
/// As such, this method is intentionally not fully inlined.
/// It is merely included for completeness.
///
/// WARNING!
/// The `result` will be the zero address upon recovery failure.
/// As such, it is extremely important to ensure that the address which
/// the `result` is compared against is never zero.
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal view returns (address result) {
uint8 v;
bytes32 s;
assembly {
s := shr(1, shl(1, vs))
v := add(shr(255, vs), 27)
}
result = recover(hash, v, r, s);
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the signature defined by `v`, `r`, `s`.
///
/// WARNING!
/// The `result` will be the zero address upon recovery failure.
/// As such, it is extremely important to ensure that the address which
/// the `result` is compared against is never zero.
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal view returns (address result) {
assembly {
// Copy the free memory pointer so that we can restore it later.
let m := mload(0x40)
// If `s` in lower half order, such that the signature is not malleable.
if iszero(gt(s, _MALLEABILITY_THRESHOLD)) {
mstore(0x00, hash)
mstore(0x20, v)
mstore(0x40, r)
mstore(0x60, s)
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
0x01, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x40, // Start of output.
0x20 // Size of output.
)
)
// Restore the zero slot.
mstore(0x60, 0)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(sub(0x60, returndatasize()))
}
// Restore the free memory pointer.
mstore(0x40, m)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
assembly {
// Store into scratch space for keccak256.
mstore(0x20, hash)
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32")
// 0x40 - 0x04 = 0x3c
result := keccak256(0x04, 0x3c)
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
assembly {
// We need at most 128 bytes for Ethereum signed message header.
// The max length of the ASCII reprenstation of a uint256 is 78 bytes.
// The length of "\x19Ethereum Signed Message:\n" is 26 bytes (i.e. 0x1a).
// The next multiple of 32 above 78 + 26 is 128 (i.e. 0x80).
// Instead of allocating, we temporarily copy the 128 bytes before the
// start of `s` data to some variables.
let m3 := mload(sub(s, 0x60))
let m2 := mload(sub(s, 0x40))
let m1 := mload(sub(s, 0x20))
// The length of `s` is in bytes.
let sLength := mload(s)
let ptr := add(s, 0x20)
// `end` marks the end of the memory which we will compute the keccak256 of.
let end := add(ptr, sLength)
// Convert the length of the bytes to ASCII decimal representation
// and store it into the memory.
// prettier-ignore
for { let temp := sLength } 1 {} {
ptr := sub(ptr, 1)
mstore8(ptr, add(48, mod(temp, 10)))
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
// Copy the header over to the memory.
mstore(sub(ptr, 0x20), "\x00\x00\x00\x00\x00\x00\x19Ethereum Signed Message:\n")
// Compute the keccak256 of the memory.
result := keccak256(sub(ptr, 0x1a), sub(end, sub(ptr, 0x1a)))
// Restore the previous memory.
mstore(s, sLength)
mstore(sub(s, 0x20), m1)
mstore(sub(s, 0x40), m2)
mstore(sub(s, 0x60), m3)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `length` of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = uint256(int256(-1));
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
let m := add(mload(0x40), 0xa0)
// Update the free memory pointer to allocate.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
assembly {
let start := mload(0x40)
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
let m := add(start, and(add(shl(1, length), 0x62), not(0x1f)))
// Allocate the memory.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for {} 1 {} {
str := sub(str, 2)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
length := sub(length, 1)
// prettier-ignore
if iszero(length) { break }
}
if temp {
// Store the function selector of `HexLengthInsufficient()`.
mstore(0x00, 0x2194895a)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Compute the string's length.
let strLength := add(sub(end, str), 2)
// Move the pointer and write the "0x" prefix.
str := sub(str, 0x20)
mstore(str, 0x3078)
// Move the pointer and write the length.
str := sub(str, 2)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
assembly {
let start := mload(0x40)
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
let m := add(start, 0xa0)
// Allocate the memory.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
str := sub(str, 2)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
// prettier-ignore
if iszero(temp) { break }
}
// Compute the string's length.
let strLength := add(sub(end, str), 2)
// Move the pointer and write the "0x" prefix.
str := sub(str, 0x20)
mstore(str, 0x3078)
// Move the pointer and write the length.
str := sub(str, 2)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
assembly {
let start := mload(0x40)
// We need 0x20 bytes for the length, 0x02 bytes for the prefix,
// and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x02 + 0x28) is 0x60.
str := add(start, 0x60)
// Allocate the memory.
mstore(0x40, str)
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let length := 20
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
str := sub(str, 2)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
length := sub(length, 1)
// prettier-ignore
if iszero(length) { break }
}
// Move the pointer and write the "0x" prefix.
str := sub(str, 32)
mstore(str, 0x3078)
// Move the pointer and write the length.
str := sub(str, 2)
mstore(str, 42)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, all indices of the following operations
// are byte (ASCII) offsets, not UTF character offsets.
/// @dev Returns `subject` all occurances of `search` replaced with `replacement`.
function replace(
string memory subject,
string memory search,
string memory replacement
) internal pure returns (string memory result) {
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 32)) {
h := keccak256(search, searchLength)
}
let m := shl(3, sub(32, and(searchLength, 31)))
let s := mload(search)
// prettier-ignore
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
// prettier-ignore
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
// prettier-ignore
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
// prettier-ignore
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
// Zeroize the slot after the string.
let last := add(add(result, 0x20), k)
mstore(last, 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 31), not(31)))
// Store the length of the result.
mstore(result, k)
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(
string memory subject,
string memory search,
uint256 from
) internal pure returns (uint256 result) {
assembly {
// prettier-ignore
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
// `result = min(from, subjectLength)`.
result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from)))
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(32, and(searchLength, 31)))
let s := mload(add(search, 0x20))
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
if iszero(lt(searchLength, 32)) {
// prettier-ignore
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
}
break
}
// prettier-ignore
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(
string memory subject,
string memory search,
uint256 from
) internal pure returns (uint256 result) {
assembly {
// prettier-ignore
for {} 1 {} {
let searchLength := mload(search)
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) {
from := fromMax
}
if iszero(mload(search)) {
result := from
break
}
result := not(0) // Initialize to `NOT_FOUND`.
let subjectSearchEnd := sub(add(subject, 0x20), 1)
subject := add(add(subject, 0x20), from)
// prettier-ignore
if iszero(gt(subject, subjectSearchEnd)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
// prettier-ignore
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(subjectSearchEnd, 1))
break
}
subject := sub(subject, 1)
// prettier-ignore
if iszero(gt(subject, subjectSearchEnd)) { break }
}
break
}
}
}
/// @dev Returns the index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search) internal pure returns (bool result) {
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
result := and(
iszero(gt(searchLength, mload(subject))),
eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength))
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search) internal pure returns (bool result) {
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times) internal pure returns (string memory result) {
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
// prettier-ignore
for {} 1 {} {
// Copy the `subject` one word at a time.
// prettier-ignore
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
// prettier-ignore
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
// prettier-ignore
if iszero(times) { break }
}
// Zeroize the slot after the string.
mstore(output, 0)
// Store the length.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(
string memory subject,
uint256 start,
uint256 end
) internal pure returns (string memory result) {
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) {
end := subjectLength
}
if iszero(gt(subjectLength, start)) {
start := subjectLength
}
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
// Copy the `subject` one word at a time, backwards.
// prettier-ignore
for { let o := and(add(resultLength, 31), not(31)) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := sub(o, 0x20)
// prettier-ignore
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start) internal pure returns (string memory result) {
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) {
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 32)) {
h := keccak256(search, searchLength)
}
let m := shl(3, sub(32, and(searchLength, 31)))
let s := mload(search)
// prettier-ignore
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
// prettier-ignore
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) {
uint256[] memory indices = indicesOf(subject, delimiter);
assembly {
if mload(indices) {
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(sub(indicesEnd, 0x20), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
// prettier-ignore
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
// prettier-ignore
for { let o := and(add(elementLength, 31), not(31)) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := sub(o, 0x20)
// prettier-ignore
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 63), not(31))))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
// prettier-ignore
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b) internal pure returns (string memory result) {
assembly {
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
// prettier-ignore
for { let o := and(add(mload(a), 32), not(31)) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := sub(o, 0x20)
// prettier-ignore
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, mload(a))
// Copy `b` one word at a time, backwards.
// prettier-ignore
for { let o := and(add(bLength, 32), not(31)) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := sub(o, 0x20)
// prettier-ignore
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 31), not(31)))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result := mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behaviour is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result := mul(
// Load the length and the bytes of `a` and `b`.
or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behaviour is undefined.
function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) {
assembly {
// Grab the free memory pointer.
resultA := mload(0x40)
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(add(a, 0x20), mload(a)), 0)
// Store the return offset.
// Assumes that the string does not start from the scratch space.
mstore(sub(a, 0x20), 0x20)
// End the transaction, returning the string.
return(sub(a, 0x20), add(mload(a), 0x40))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Context
/// @notice Overridable context for meta-transactions
/// @author OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-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;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {s as erc1967ds} from "../proxy/ERC1967Proxy.sol";
// ------------- errors
error ProxyCallRequired();
error AlreadyInitialized();
/// @title Initializable
/// @author phaze (https://github.com/0xPhaze/UDS)
/// @dev functions using the `initializer` modifier are only callable during proxy deployment
/// @dev functions using the `reinitializer` modifier are only callable through a proxy
/// @dev and only before a proxy upgrade migration has completed
/// @dev (only when `upgradeToAndCall`'s `initCalldata` is being executed)
/// @dev allows re-initialization during upgrades
abstract contract Initializable {
address private immutable __implementation = address(this);
/* ------------- modifier ------------- */
modifier initializer() {
if (address(this).code.length != 0) revert AlreadyInitialized();
_;
}
modifier reinitializer() {
if (address(this) == __implementation) revert ProxyCallRequired();
if (erc1967ds().implementation == __implementation) revert AlreadyInitialized();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {EIP712PermitUDS} from "UDS/auth/EIP712PermitUDS.sol";
import {UserDataOps, TokenDataOps} from "./ERC721MLibrary.sol";
// ------------- storage
struct ERC721MStorage {
string name;
string symbol;
uint256 totalSupply;
mapping(address => uint256) userData;
mapping(uint256 => uint256) tokenData;
mapping(uint256 => address) getApproved;
mapping(address => mapping(address => bool)) isApprovedForAll;
}
bytes32 constant DIAMOND_STORAGE_ERC721M_LOCKABLE = keccak256("diamond.storage.erc721m.lockable");
function s() pure returns (ERC721MStorage storage diamondStorage) {
bytes32 slot = DIAMOND_STORAGE_ERC721M_LOCKABLE;
assembly { diamondStorage.slot := slot } // prettier-ignore
}
// ------------- errors
error IncorrectOwner();
error TokenIdUnlocked();
error NonexistentToken();
error MintZeroQuantity();
error MintToZeroAddress();
error TransferFromInvalidTo();
error TransferToZeroAddress();
error CallerNotOwnerNorApproved();
error TransferFromIncorrectOwner();
error TransferToNonERC721Receiver();
/// @title ERC721M (Integrated Token Locking)
/// @author phaze (https://github.com/0xPhaze/ERC721M)
/// @author modified from ERC721A (https://github.com/chiru-labs/ERC721A)
/// @author modified from Solmate (https://github.com/Rari-Capital/solmate)
/// @notice Integrates EIP712Permit
abstract contract ERC721M is EIP712PermitUDS {
using UserDataOps for uint256;
using TokenDataOps for uint256;
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
uint256 constant startingIndex = 1;
constructor(string memory name_, string memory symbol_) {
__ERC721_init(name_, symbol_);
}
/* ------------- init ------------- */
function __ERC721_init(string memory name_, string memory symbol_) internal {
s().name = name_;
s().symbol = symbol_;
}
/* ------------- virtual ------------- */
function tokenURI(uint256 id) external view virtual returns (string memory);
/* ------------- view ------------- */
function name() external view virtual returns (string memory) {
return s().name;
}
function symbol() external view virtual returns (string memory) {
return s().symbol;
}
function balanceOf(address user) public view virtual returns (uint256) {
return s().userData[user].balance();
}
function getApproved(uint256 id) external view virtual returns (address) {
return s().getApproved[id];
}
function isApprovedForAll(address owner, address spender) external view virtual returns (bool) {
return s().isApprovedForAll[owner][spender];
}
function ownerOf(uint256 id) public view virtual returns (address) {
return _tokenDataOf(id).owner();
}
function totalSupply() public view virtual returns (uint256) {
return s().totalSupply;
}
function getAux(uint256 id) public view returns (uint256) {
return _tokenDataOf(id).aux();
}
function getLockStart(uint256 id) public view returns (uint256) {
return _tokenDataOf(id).tokenLockStart();
}
function numMinted(address user) public view virtual returns (uint256) {
return s().userData[user].numMinted();
}
function numLocked(address user) public view virtual returns (uint256) {
return s().userData[user].numLocked();
}
function getLockStart(address user) public view virtual returns (uint256) {
return s().userData[user].userLockStart();
}
function trueOwnerOf(uint256 id) public view virtual returns (address) {
return _tokenDataOf(id).trueOwner();
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/* ------------- public ------------- */
function approve(address spender, uint256 id) public virtual {
address owner = _tokenDataOf(id).owner();
if (msg.sender != owner && !s().isApprovedForAll[owner][msg.sender]) revert CallerNotOwnerNorApproved();
s().getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
s().isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function _isApprovedOrOwner(address from, uint256 id) private view returns (bool) {
return (msg.sender == from || s().isApprovedForAll[from][msg.sender] || s().getApproved[id] == msg.sender);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
if (to == address(this)) revert TransferFromInvalidTo();
if (to == address(0)) revert TransferToZeroAddress();
uint256 tokenData = _tokenDataOf(id);
bool isApprovedOrOwner = (msg.sender == from ||
s().isApprovedForAll[from][msg.sender] ||
s().getApproved[id] == msg.sender);
if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
if (tokenData.owner() != from) revert TransferFromIncorrectOwner();
delete s().getApproved[id];
unchecked {
_ensureTokenDataSet(id + 1, tokenData);
}
s().tokenData[id] = tokenData.setOwner(to).flagNextTokenDataSet();
s().userData[to] = s().userData[to].increaseBalance(1);
s().userData[from] = s().userData[from].decreaseBalance(1);
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
safeTransferFrom(from, to, id, "");
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes memory data
) public virtual {
transferFrom(from, to, id);
if (
to.code.length != 0 &&
IERC721Receiver(to).onERC721Received(msg.sender, from, id, data) !=
IERC721Receiver(to).onERC721Received.selector
) revert TransferToNonERC721Receiver();
}
// EIP-4494 permit; differs from the current EIP
function permit(
address owner,
address operator,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s_
) public virtual {
_usePermit(owner, operator, 1, deadline, v, r, s_);
s().isApprovedForAll[owner][operator] = true;
emit ApprovalForAll(owner, operator, true);
}
/* ------------- internal ------------- */
function _exists(uint256 id) internal view virtual returns (bool) {
return startingIndex <= id && id < _nextTokenId();
}
function _nextTokenId() internal view virtual returns (uint256) {
return startingIndex + totalSupply();
}
function _increaseTotalSupply(uint256 amount) internal virtual {
if (amount != 0) s().totalSupply = _nextTokenId() + amount - 1;
}
function _tokenDataOf(uint256 id) internal view virtual returns (uint256 out) {
if (!_exists(id)) revert NonexistentToken();
unchecked {
uint256 tokenData;
for (uint256 curr = id; ; curr--) {
tokenData = s().tokenData[curr];
if (tokenData != 0) return tokenData;
}
}
}
function _ensureTokenDataSet(uint256 id, uint256 tokenData) internal virtual {
if (!tokenData.nextTokenDataSet() && s().tokenData[id] == 0 && _exists(id)) s().tokenData[id] = tokenData;
}
function _mint(address to, uint256 quantity) internal virtual {
_mintAndLock(to, quantity, false, 0);
}
function _mint(
address to,
uint256 quantity,
uint48 auxData
) internal virtual {
_mintAndLock(to, quantity, false, auxData);
}
function _mintAndLock(
address to,
uint256 quantity,
bool lock
) internal virtual {
_mintAndLock(to, quantity, lock, 0);
}
function _mintAndLock(
address to,
uint256 quantity,
bool lock,
uint48 auxData
) internal virtual {
unchecked {
if (quantity == 0) revert MintZeroQuantity();
if (to == address(0)) revert MintToZeroAddress();
uint256 startTokenId = _nextTokenId();
uint256 tokenData = uint256(uint160(to)).setAux(auxData);
uint256 userData = s().userData[to];
// don't have to care about next token data if only minting one
if (quantity == 1) tokenData = tokenData.flagNextTokenDataSet();
if (lock) {
tokenData = tokenData.setConsecutiveLocked().lock();
userData = userData.increaseNumLocked(quantity).setUserLockStart(block.timestamp);
for (uint256 i; i < quantity; ++i) {
emit Transfer(address(0), to, startTokenId + i);
emit Transfer(to, address(this), startTokenId + i);
}
} else {
for (uint256 i; i < quantity; ++i) {
emit Transfer(address(0), to, startTokenId + i);
}
}
s().userData[to] = userData.increaseNumMinted(quantity).increaseBalance(quantity);
s().tokenData[startTokenId] = tokenData;
_increaseTotalSupply(quantity);
}
}
function _setAux(uint256 id, uint48 aux) internal virtual {
uint256 tokenData = _tokenDataOf(id);
unchecked {
_ensureTokenDataSet(id + 1, tokenData);
}
s().tokenData[id] = tokenData.setAux(aux);
}
function _lock(address from, uint256 id) internal virtual {
uint256 tokenData = _tokenDataOf(id);
bool isApprovedOrOwner = (msg.sender == from ||
s().isApprovedForAll[from][msg.sender] ||
s().getApproved[id] == msg.sender);
if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
if (tokenData.owner() != from) revert IncorrectOwner();
delete s().getApproved[id];
unchecked {
_ensureTokenDataSet(id + 1, tokenData);
}
s().tokenData[id] = tokenData.lock().unsetConsecutiveLocked().flagNextTokenDataSet();
s().userData[from] = s().userData[from].increaseNumLocked(1).setUserLockStart(block.timestamp);
emit Transfer(from, address(this), id);
}
function _unlock(address from, uint256 id) internal virtual {
uint256 tokenData = _tokenDataOf(id);
bool isApprovedOrOwner = (msg.sender == from ||
s().isApprovedForAll[from][msg.sender] ||
s().getApproved[id] == msg.sender);
if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
if (!tokenData.locked()) revert TokenIdUnlocked();
if (tokenData.trueOwner() != from) revert IncorrectOwner();
// if isConsecutiveLocked flag is set, we need to make sure that next tokenData is set
// because tokenData in this case is implicit and needs to carry over
if (tokenData.isConsecutiveLocked()) {
unchecked {
_ensureTokenDataSet(id + 1, tokenData);
tokenData = tokenData.unsetConsecutiveLocked().flagNextTokenDataSet();
}
}
s().tokenData[id] = tokenData.unlock();
s().userData[from] = s().userData[from].decreaseNumLocked(1).setUserLockStart(block.timestamp);
emit Transfer(address(this), from, id);
}
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC721MLibrary.sol";
import {ERC721M, s} from "../ERC721M.sol";
/// @title ERC721M Query Extension
/// @author phaze (https://github.com/0xPhaze/ERC721M)
abstract contract ERC721MQuery is ERC721M {
using UserDataOps for uint256;
using TokenDataOps for uint256;
/* ------------- O(n) read-only ------------- */
function getOwnedIds(address user) external view returns (uint256[] memory) {
return utils.getOwnedIds(s().tokenData, user, startingIndex, totalSupply());
}
function getLockedIds(address user) external view returns (uint256[] memory) {
return utils.getLockedIds(s().tokenData, user, startingIndex, totalSupply());
}
function getUnlockedIds(address user) external view returns (uint256[] memory) {
return utils.getUnlockedIds(s().tokenData, user, startingIndex, totalSupply());
}
function totalNumLocked() external view returns (uint256) {
uint256 data;
uint256 count;
uint256 endIndex = _nextTokenId();
uint256 currentData;
unchecked {
for (uint256 i = startingIndex; i < endIndex; ++i) {
data = s().tokenData[i];
if (data != 0) currentData = data;
if (currentData.locked()) ++count;
}
}
return count;
}
}
/// @title ERC721M Query Utils
/// @author phaze (https://github.com/0xPhaze/ERC721M)
library utils {
using TokenDataOps for uint256;
function getOwnedIds(
mapping(uint256 => uint256) storage tokenDataOf,
address user,
uint256 start,
uint256 collectionSize
) internal view returns (uint256[] memory ids) {
uint256 memPtr;
assembly {
ids := mload(0x40)
memPtr := add(ids, 0x20)
}
unchecked {
uint256 data;
uint256 currentData;
uint256 end = collectionSize + start;
for (uint256 id = start; id < end; ++id) {
data = tokenDataOf[id];
if (data != 0) currentData = data;
if (user == address(uint160(currentData))) {
assembly {
mstore(memPtr, id)
memPtr := add(memPtr, 0x20)
}
}
}
}
assembly {
mstore(ids, shr(5, sub(sub(memPtr, ids), 0x20)))
mstore(0x40, memPtr)
}
}
function getLockedIds(
mapping(uint256 => uint256) storage tokenDataOf,
address user,
uint256 start,
uint256 collectionSize
) internal view returns (uint256[] memory ids) {
uint256 memPtr;
assembly {
ids := mload(0x40)
memPtr := add(ids, 0x20)
}
unchecked {
uint256 data;
uint256 currentData;
uint256 end = collectionSize + start;
for (uint256 id = start; id < end; ++id) {
data = tokenDataOf[id];
if (data != 0) currentData = data;
if (user == address(uint160(currentData)) && currentData.locked()) {
assembly {
mstore(memPtr, id)
memPtr := add(memPtr, 0x20)
}
}
}
}
assembly {
mstore(ids, shr(5, sub(sub(memPtr, ids), 0x20)))
mstore(0x40, memPtr)
}
}
function getUnlockedIds(
mapping(uint256 => uint256) storage tokenDataOf,
address user,
uint256 start,
uint256 collectionSize
) internal view returns (uint256[] memory ids) {
uint256 memPtr;
assembly {
ids := mload(0x40)
memPtr := add(ids, 0x20)
}
unchecked {
uint256 data;
uint256 currentData;
uint256 end = collectionSize + start;
for (uint256 id = start; id < end; ++id) {
data = tokenDataOf[id];
if (data != 0) currentData = data;
if (user == address(uint160(currentData)) && !currentData.locked()) {
assembly {
mstore(memPtr, id)
memPtr := add(memPtr, 0x20)
}
}
}
}
assembly {
mstore(ids, shr(5, sub(sub(memPtr, ids), 0x20)))
mstore(0x40, memPtr)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {FxBaseRootTunnel} from "./base/FxBaseRootTunnel.sol";
bytes4 constant REGISTER_ERC721_IDS_SELECTOR = bytes4(keccak256("registerERC721IdsWithChild(address,uint256[])"));
bytes4 constant DEREGISTER_ERC721_IDS_SELECTOR = bytes4(keccak256("deregisterERC721IdsWithChild(uint256[])"));
/// @title ERC721 FxRootTunnel
/// @author phaze (https://github.com/0xPhaze/fx-contracts)
abstract contract FxERC721Root is FxBaseRootTunnel {
constructor(address checkpointManager, address fxRoot) FxBaseRootTunnel(checkpointManager, fxRoot) {}
/* ------------- virtual ------------- */
function _authorizeTunnelController() internal virtual override;
/* ------------- internal ------------- */
function _registerERC721IdsWithChild(address to, uint256[] calldata ids) internal virtual {
_sendMessageToChild(abi.encodeWithSelector(REGISTER_ERC721_IDS_SELECTOR, to, ids));
}
function _registerERC721IdsWithChildMem(address to, uint256[] memory ids) internal virtual {
_sendMessageToChild(abi.encodeWithSelector(REGISTER_ERC721_IDS_SELECTOR, to, ids));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// ------------- storage
bytes32 constant DIAMOND_STORAGE_EIP_712_PERMIT = keccak256("diamond.storage.eip.712.permit");
function s() pure returns (EIP2612DS storage diamondStorage) {
bytes32 slot = DIAMOND_STORAGE_EIP_712_PERMIT;
assembly { diamondStorage.slot := slot } // prettier-ignore
}
struct EIP2612DS {
mapping(address => uint256) nonces;
}
// ------------- errors
error InvalidSigner();
error DeadlineExpired();
/// @title EIP712Permit (Upgradeable Diamond Storage)
/// @author phaze (https://github.com/0xPhaze/UDS)
/// @author Modified from Solmate (https://github.com/Rari-Capital/solmate)
/// @dev `DOMAIN_SEPARATOR` needs to be re-computed every time
/// @dev for use with a proxy due to `address(this)`
abstract contract EIP712PermitUDS {
EIP2612DS private __storageLayout; // storage layout for upgrade compatibility checks
/* ------------- public ------------- */
function nonces(address owner) public view returns (uint256) {
return s().nonces[owner];
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256("EIP712"),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/* ------------- internal ------------- */
function _usePermit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v_,
bytes32 r_,
bytes32 s_
) internal virtual {
if (deadline < block.timestamp) revert DeadlineExpired();
unchecked {
uint256 nonce = s().nonces[owner]++;
address recovered = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonce,
deadline
)
)
)
),
v_,
r_,
s_
);
if (recovered == address(0) || recovered != owner) revert InvalidSigner();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// ------------- storage
// keccak256("eip1967.proxy.implementation") - 1
bytes32 constant ERC1967_PROXY_STORAGE_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
function s() pure returns (ERC1967UpgradeDS storage diamondStorage) {
assembly { diamondStorage.slot := ERC1967_PROXY_STORAGE_SLOT } // prettier-ignore
}
struct ERC1967UpgradeDS {
address implementation;
}
// ------------- errors
error InvalidUUID();
error NotAContract();
/// @title ERC1967
/// @author phaze (https://github.com/0xPhaze/UDS)
abstract contract ERC1967 {
event Upgraded(address indexed implementation);
function _upgradeToAndCall(address logic, bytes memory data) internal {
if (logic.code.length == 0) revert NotAContract();
if (ERC1822(logic).proxiableUUID() != ERC1967_PROXY_STORAGE_SLOT) revert InvalidUUID();
if (data.length != 0) {
(bool success, ) = logic.delegatecall(data);
if (!success) {
assembly {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
s().implementation = logic;
emit Upgraded(logic);
}
}
/// @title Minimal ERC1967Proxy
/// @author phaze (https://github.com/0xPhaze/UDS)
contract ERC1967Proxy is ERC1967 {
constructor(address logic, bytes memory data) payable {
_upgradeToAndCall(logic, data);
}
fallback() external payable {
assembly {
calldatacopy(0, 0, calldatasize())
let success := delegatecall(gas(), sload(ERC1967_PROXY_STORAGE_SLOT), 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if success {
return(0, returndatasize())
}
revert(0, returndatasize())
}
}
}
/// @title ERC1822
/// @author phaze (https://github.com/0xPhaze/UDS)
abstract contract ERC1822 {
function proxiableUUID() external view virtual returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Library used for bitmap manipulation for ERC721M
/// @author phaze (https://github.com/0xPhaze/ERC721M)
library UserDataOps {
/* ------------- balance: [0, 20) ------------- */
function balance(uint256 userData) internal pure returns (uint256) {
return userData & 0xFFFFF;
}
function increaseBalance(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData + amount;
}
}
function decreaseBalance(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData - amount;
}
}
/* ------------- numMinted: [20, 40) ------------- */
function numMinted(uint256 userData) internal pure returns (uint256) {
return (userData >> 20) & 0xFFFFF;
}
function increaseNumMinted(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData + (amount << 20);
}
}
/* ------------- numLocked: [40, 60) ------------- */
function numLocked(uint256 userData) internal pure returns (uint256) {
return (userData >> 40) & 0xFFFFF;
}
function increaseNumLocked(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData + (amount << 40);
}
}
function decreaseNumLocked(uint256 userData, uint256 amount) internal pure returns (uint256) {
unchecked {
return userData - (amount << 40);
}
}
/* ------------- lockStart: [60, 100) ------------- */
function userLockStart(uint256 userData) internal pure returns (uint256) {
return (userData >> 60) & 0xFFFFFFFFFF;
}
function setUserLockStart(uint256 userData, uint256 timestamp) internal pure returns (uint256) {
return (userData & ~uint256(0xFFFFFFFFFF << 60)) | (timestamp << 60);
}
// /* ------------- aux: [100, 256) ------------- */
// function aux(uint256 userData) internal pure returns (uint256) {
// return (userData >> 100) & 0xFFFFFFFFFF;
// }
// function setAux(uint256 userData, uint256 aux_) internal pure returns (uint256) {
// return (userData & ~((uint256(1) << 100) - 1)) | (aux_ << 100);
// }
}
library TokenDataOps {
/// @dev Big question whether copy should transfer over data, such as,
/// aux data and timestamps
function copy(uint256 tokenData) internal pure returns (uint256) {
return tokenData;
}
// return tokenData & ((uint256(1) << (160 + (((tokenData >> 160) & 1) << 1))) - 1);
/// ^ equivalent code:
// function copy2(uint256 tokenData) internal pure returns (uint256) {
// uint256 copiedData = uint160(tokenData);
// if (isConsecutiveLocked(tokenData)) {
// copiedData = setConsecutiveLocked(copiedData);
// if (locked(tokenData)) copiedData = lock(copiedData);
// }
// return copiedData;
// }
/* ------------- owner: [0, 160) ------------- */
function owner(uint256 tokenData) internal view returns (address) {
return locked(tokenData) ? address(this) : trueOwner(tokenData);
}
function setOwner(uint256 tokenData, address owner_) internal pure returns (uint256) {
return (tokenData & 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000) | uint160(owner_);
}
function trueOwner(uint256 tokenData) internal pure returns (address) {
return address(uint160(tokenData));
}
/* ------------- consecutiveLock: [160, 161) ------------- */
function isConsecutiveLocked(uint256 tokenData) internal pure returns (bool) {
return ((tokenData >> 160) & uint256(1)) != 0;
}
function setConsecutiveLocked(uint256 tokenData) internal pure returns (uint256) {
return tokenData | (uint256(1) << 160);
}
function unsetConsecutiveLocked(uint256 tokenData) internal pure returns (uint256) {
return tokenData & ~(uint256(1) << 160);
}
/* ------------- locked: [161, 162) ------------- */
function locked(uint256 tokenData) internal pure returns (bool) {
return ((tokenData >> 161) & uint256(1)) != 0; // Note: this is not masked and can carry over when calling 'ownerOf'
}
function lock(uint256 tokenData) internal view returns (uint256) {
return setTokenLockStart(tokenData, block.timestamp) | (uint256(1) << 161);
}
function unlock(uint256 tokenData) internal view returns (uint256) {
return setTokenLockStart(tokenData, block.timestamp) & ~(uint256(1) << 161);
}
/* ------------- nextTokenDataSet: [162, 163) ------------- */
function nextTokenDataSet(uint256 tokenData) internal pure returns (bool) {
return ((tokenData >> 162) & uint256(1)) != 0;
}
function flagNextTokenDataSet(uint256 tokenData) internal pure returns (uint256) {
return tokenData | (uint256(1) << 162); // nextTokenDatatSet flag (don't repeat the read/write)
}
/* ------------- lockStart: [168, 208) ------------- */
function tokenLockStart(uint256 tokenData) internal pure returns (uint256) {
return (tokenData >> 168) & 0xFFFFFFFFFF;
}
function setTokenLockStart(uint256 tokenData, uint256 timestamp) internal pure returns (uint256) {
return (tokenData & ~uint256(0xFFFFFFFFFF << 168)) | (timestamp << 168);
}
/* ------------- aux: [208, 256) ------------- */
function aux(uint256 tokenData) internal pure returns (uint256) {
return tokenData >> 208;
}
function setAux(uint256 tokenData, uint256 auxData) internal pure returns (uint256) {
return (tokenData & ~uint256(0xFFFFFFFFFFFF << 208)) | (auxData << 208);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Merkle} from "../lib/Merkle.sol";
import {RLPReader} from "../lib/RLPReader.sol";
import {ExitPayloadReader} from "../lib/ExitPayloadReader.sol";
import {MerklePatriciaProof} from "../lib/MerklePatriciaProof.sol";
// ------------- interfaces
interface IFxStateSender {
function sendMessageToChild(address _receiver, bytes calldata _data) external;
}
interface ICheckpointManager {
function headerBlocks(uint256 headerNumber)
external
view
returns (
bytes32 root,
uint256 start,
uint256 end,
uint256 createdAt,
address proposer
);
}
// ------------- storage
bytes32 constant DIAMOND_STORAGE_FX_BASE_ROOT_TUNNEL = keccak256("diamond.storage.fx.base.root.tunnel");
function s() pure returns (FxBaseRootTunnelDS storage diamondStorage) {
bytes32 slot = DIAMOND_STORAGE_FX_BASE_ROOT_TUNNEL;
assembly { diamondStorage.slot := slot } // prettier-ignore
}
struct FxBaseRootTunnelDS {
address fxChildTunnel;
mapping(bytes32 => bool) processedExits;
}
// ------------- errors
error FxChildUnset();
error InvalidHeader();
error InvalidSelector();
error InvalidReceiptProof();
error InvalidFxChildTunnel();
error ExitAlreadyProcessed();
abstract contract FxBaseRootTunnel {
using RLPReader for RLPReader.RLPItem;
using Merkle for bytes32;
using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.LogTopics;
using ExitPayloadReader for ExitPayloadReader.Receipt;
bytes32 private constant SEND_MESSAGE_EVENT_SELECTOR =
0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;
IFxStateSender public immutable fxRoot;
ICheckpointManager public immutable checkpointManager;
constructor(address checkpointManager_, address fxRoot_) {
checkpointManager = ICheckpointManager(checkpointManager_);
fxRoot = IFxStateSender(fxRoot_);
}
/* ------------- virtual ------------- */
function _authorizeTunnelController() internal virtual;
/* ------------- view ------------- */
function fxChildTunnel() public view virtual returns (address) {
return s().fxChildTunnel;
}
function processedExits(bytes32 exitHash) public view virtual returns (bool) {
return s().processedExits[exitHash];
}
function setFxChildTunnel(address fxChildTunnel_) public virtual {
_authorizeTunnelController();
s().fxChildTunnel = fxChildTunnel_;
}
/* ------------- internal ------------- */
function _sendMessageToChild(bytes memory message) internal virtual {
if (s().fxChildTunnel == address(0)) revert FxChildUnset();
fxRoot.sendMessageToChild(s().fxChildTunnel, message);
}
/**
* @notice receive message from L2 to L1, validated by proof
* @dev This function verifies if the transaction actually happened on child chain
*
* @param proofData RLP encoded data of the reference tx containing following list of fields
* 0 - headerNumber - Checkpoint header block number containing the reference tx
* 1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
* 2 - blockNumber - Block number containing the reference tx on child chain
* 3 - blockTime - Reference tx block time
* 4 - txRoot - Transactions root of block
* 5 - receiptRoot - Receipts root of block
* 6 - receipt - Receipt of the reference transaction
* 7 - receiptProof - Merkle proof of the reference receipt
* 8 - branchMask - 32 bits denoting the path of receipt in merkle tree
* 9 - receiptLogIndex - Log Index to read from the receipt
*/
function _validateAndExtractMessage(bytes memory proofData) internal returns (bytes memory) {
address childTunnel = s().fxChildTunnel;
if (childTunnel == address(0)) revert FxChildUnset();
ExitPayloadReader.ExitPayload memory payload = proofData.toExitPayload();
bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
uint256 blockNumber = payload.getBlockNumber();
// checking if exit has already been processed
// unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
bytes32 exitHash = keccak256(
abi.encodePacked(
blockNumber,
// first 2 nibbles are dropped while generating nibble array
// this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
// so converting to nibble array and then hashing it
MerklePatriciaProof._getNibbleArray(branchMaskBytes),
payload.getReceiptLogIndex()
)
);
if (s().processedExits[exitHash]) revert ExitAlreadyProcessed();
s().processedExits[exitHash] = true;
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
ExitPayloadReader.Log memory log = receipt.getLog();
// check child tunnel
if (childTunnel != log.getEmitter()) revert InvalidFxChildTunnel();
bytes32 receiptRoot = payload.getReceiptRoot();
// verify receipt inclusion
if (!MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot))
revert InvalidReceiptProof();
(bytes32 headerRoot, uint256 startBlock, , , ) = checkpointManager.headerBlocks(payload.getHeaderNumber());
bytes32 leaf = keccak256(
abi.encodePacked(blockNumber, payload.getBlockTime(), payload.getTxRoot(), receiptRoot)
);
if (!leaf.checkMembership(blockNumber - startBlock, headerRoot, payload.getBlockProof()))
revert InvalidHeader();
ExitPayloadReader.LogTopics memory topics = log.getTopics();
if (bytes32(topics.getField(0).toUint()) != SEND_MESSAGE_EVENT_SELECTOR) revert InvalidSelector();
// received message data
bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message
return message;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Merkle {
function checkMembership(
bytes32 leaf,
uint256 index,
bytes32 rootHash,
bytes memory proof
) internal pure returns (bool) {
require(proof.length % 32 == 0, "Invalid proof length");
uint256 proofHeight = proof.length / 32;
// Proof of size n means, height of the tree is n+1.
// In a tree of height n+1, max #leafs possible is 2 ^ n
require(index < 2**proofHeight, "Leaf index is too big");
bytes32 proofElement;
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
assembly {
proofElement := mload(add(proof, i))
}
if (index % 2 == 0) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
index = index / 2;
}
return computedHash == rootHash;
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint256 nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint256 ptr = self.nextPtr; uint256 itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param item RLP encoded bytes */ function rlpLen(RLPItem memory item) internal pure returns (uint256) { return item.len; } /* * @param item RLP encoded bytes */ function payloadLen(RLPItem memory item) internal pure returns (uint256) { return item.len - _payloadOffset(item.memPtr); } /* * @param item RLP encoded list in bytes */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint256 items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /* * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. * @return keccak256 hash of RLP encoded bytes. */ function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { uint256 ptr = item.memPtr; uint256 len = item.len; bytes32 result; assembly { result := keccak256(ptr, len) } return result; } function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 memPtr = item.memPtr + offset; uint256 len = item.len - offset; // data length return (memPtr, len); } /* * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. * @return keccak256 hash of the item payload. */ function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { (uint256 memPtr, uint256 len) = payloadLocation(item); bytes32 result; assembly { result := keccak256(memPtr, len) } return result; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint256 ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint256 result; uint256 memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } return result == 0 ? false : true; } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(uint160(toUint(item))); } function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33); uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; uint256 result; uint256 memPtr = item.memPtr + offset; assembly { result := mload(memPtr) // shfit to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint256) { // one byte prefix require(item.len == 33); uint256 result; uint256 memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; // data length bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint256) { if (item.len == 0) return 0; uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) private pure returns (uint256) { uint256 itemLen; uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) itemLen = 1; else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) private pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy( uint256 src, uint256 dest, uint256 len ) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } if (len == 0) return; // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256**(WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
library ExitPayloadReader {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant WORD_SIZE = 32;
struct ExitPayload {
RLPReader.RLPItem[] data;
}
struct Receipt {
RLPReader.RLPItem[] data;
bytes raw;
uint256 logIndex;
}
struct Log {
RLPReader.RLPItem data;
RLPReader.RLPItem[] list;
}
struct LogTopics {
RLPReader.RLPItem[] data;
}
// copy paste of private copy() from RLPReader to avoid changing of existing contracts
function copy(
uint256 src,
uint256 dest,
uint256 len
) private pure {
if (len == 0) return;
// copy as many word sizes as possible
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
// left over bytes. Mask is used to remove unwanted bytes from the word
uint256 mask = 256**(WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}
}
function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) {
RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList();
return ExitPayload(payloadData);
}
function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[0].toUint();
}
function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[1].toBytes();
}
function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[2].toUint();
}
function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[3].toUint();
}
function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) {
return bytes32(payload.data[4].toUint());
}
function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) {
return bytes32(payload.data[5].toUint());
}
function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) {
receipt.raw = payload.data[6].toBytes();
RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();
if (receiptItem.isList()) {
// legacy tx
receipt.data = receiptItem.toList();
} else {
// pop first byte before parsting receipt
bytes memory typedBytes = receipt.raw;
bytes memory result = new bytes(typedBytes.length - 1);
uint256 srcPtr;
uint256 destPtr;
assembly {
srcPtr := add(33, typedBytes)
destPtr := add(0x20, result)
}
copy(srcPtr, destPtr, result.length);
receipt.data = result.toRlpItem().toList();
}
receipt.logIndex = getReceiptLogIndex(payload);
return receipt;
}
function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[7].toBytes();
}
function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[8].toBytes();
}
function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[8].toUint();
}
function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[9].toUint();
}
// Receipt methods
function toBytes(Receipt memory receipt) internal pure returns (bytes memory) {
return receipt.raw;
}
function getLog(Receipt memory receipt) internal pure returns (Log memory) {
RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
return Log(logData, logData.toList());
}
// Log methods
function getEmitter(Log memory log) internal pure returns (address) {
return RLPReader.toAddress(log.list[0]);
}
function getTopics(Log memory log) internal pure returns (LogTopics memory) {
return LogTopics(log.list[1].toList());
}
function getData(Log memory log) internal pure returns (bytes memory) {
return log.list[2].toBytes();
}
function toRlpBytes(Log memory log) internal pure returns (bytes memory) {
return log.data.toRlpBytes();
}
// LogTopics methods
function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) {
return topics.data[index];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
library MerklePatriciaProof {
/*
* @dev Verifies a merkle patricia proof.
* @param value The terminating value in the trie.
* @param encodedPath The path in the trie leading to value.
* @param rlpParentNodes The rlp encoded stack of nodes.
* @param root The root hash of the trie.
* @return The boolean validity of the proof.
*/
function verify(
bytes memory value,
bytes memory encodedPath,
bytes memory rlpParentNodes,
bytes32 root
) internal pure returns (bool) {
RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
bytes memory currentNode;
RLPReader.RLPItem[] memory currentNodeList;
bytes32 nodeKey = root;
uint256 pathPtr = 0;
bytes memory path = _getNibbleArray(encodedPath);
if (path.length == 0) {
return false;
}
for (uint256 i = 0; i < parentNodes.length; i++) {
if (pathPtr > path.length) {
return false;
}
currentNode = RLPReader.toRlpBytes(parentNodes[i]);
if (nodeKey != keccak256(currentNode)) {
return false;
}
currentNodeList = RLPReader.toList(parentNodes[i]);
if (currentNodeList.length == 17) {
if (pathPtr == path.length) {
if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) {
return true;
} else {
return false;
}
}
uint8 nextPathNibble = uint8(path[pathPtr]);
if (nextPathNibble > 16) {
return false;
}
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble]));
pathPtr += 1;
} else if (currentNodeList.length == 2) {
uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr);
if (pathPtr + traversed == path.length) {
//leaf node
if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) {
return true;
} else {
return false;
}
}
//extension node
if (traversed == 0) {
return false;
}
pathPtr += traversed;
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
} else {
return false;
}
}
return false;
}
function _nibblesToTraverse(
bytes memory encodedPartialPath,
bytes memory path,
uint256 pathPtr
) private pure returns (uint256) {
uint256 len = 0;
// encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
// and slicedPath have elements that are each one hex character (1 nibble)
bytes memory partialPath = _getNibbleArray(encodedPartialPath);
bytes memory slicedPath = new bytes(partialPath.length);
// pathPtr counts nibbles in path
// partialPath.length is a number of nibbles
for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
bytes1 pathNibble = path[i];
slicedPath[i - pathPtr] = pathNibble;
}
if (keccak256(partialPath) == keccak256(slicedPath)) {
len = partialPath.length;
} else {
len = 0;
}
return len;
}
// bytes b must be hp encoded
function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) {
bytes memory nibbles = "";
if (b.length > 0) {
uint8 offset;
uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
if (hpNibble == 1 || hpNibble == 3) {
nibbles = new bytes(b.length * 2 - 1);
bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
nibbles[0] = oddNibble;
offset = 1;
} else {
nibbles = new bytes(b.length * 2 - 2);
offset = 0;
}
for (uint256 i = offset; i < nibbles.length; i++) {
nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
}
}
return nibbles;
}
function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) {
return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10);
}
}{
"remappings": [
"/=src/",
"ERC721M/=lib/ERC721M/src/",
"UDS/=lib/UDS/src/",
"ds-test/=lib/ERC721M/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"futils/=lib/futils/src/",
"fx-contracts/=lib/fx-contracts/src/",
"fx-portal/=lib/ERC721M/lib/fx-portal/contracts/",
"solady/=lib/solady/src/",
"solmate/=lib/solmate/src/",
"upgrade-scripts/=lib/upgrade-scripts/src/"
],
"optimizer": {
"enabled": true,
"runs": 100000
},
"metadata": {
"bytecodeHash": "none"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"checkpointManager","type":"address"},{"internalType":"address","name":"fxRoot","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"CallerNotOwner","type":"error"},{"inputs":[],"name":"CallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ContractCallNotAllowed","type":"error"},{"inputs":[],"name":"DeadlineExpired","type":"error"},{"inputs":[],"name":"ExceedsLimit","type":"error"},{"inputs":[],"name":"FxChildUnset","type":"error"},{"inputs":[],"name":"IncorrectOwner","type":"error"},{"inputs":[],"name":"IncorrectValue","type":"error"},{"inputs":[],"name":"InvalidPriceUnits","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"MaxSupplyLocked","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NonexistentToken","type":"error"},{"inputs":[],"name":"PublicSaleNotActive","type":"error"},{"inputs":[],"name":"TimelockActive","type":"error"},{"inputs":[],"name":"TokenIdUnlocked","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferFromInvalidTo","type":"error"},{"inputs":[],"name":"TransferToNonERC721Receiver","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"WhitelistNotActive","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","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":"address","name":"user","type":"address"}],"name":"FirstLegendaryRaffleEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[],"name":"SaleStateUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"SecondLegendaryRaffleEntered","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":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"BRIDGE_RAFFLE_LOCK_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PER_WALLET","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PURCHASE_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"locked","type":"bool"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpointManager","outputs":[{"internalType":"contract ICheckpointManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxChildTunnel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxRoot","outputs":[{"internalType":"contract IFxStateSender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"gangOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getAux","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getLockStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLockStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLockedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getOwnedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUnlockedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"lockAndTransmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupplyLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintStart","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"numLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"numMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"exitHash","type":"bytes32"}],"name":"processedExits","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20UDS","name":"token","type":"address"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","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":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fxChildTunnel_","type":"address"}],"name":"setFxChildTunnel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"chunkIndices","type":"uint256[]"},{"internalType":"uint256[]","name":"chunks","type":"uint256[]"}],"name":"setGangs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"value","type":"uint16"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"time","type":"uint32"}],"name":"setMintStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"postFix","type":"string"}],"name":"setPostFixURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setPublicPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setUnrevealedURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setWhitelistPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"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":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNumLocked","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":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"trueOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unlockAndTransmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"whitelistMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"whitelistPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
30608052610140604052600561010090815264173539b7b760d91b610120526004906200002d90826200036b565b5060405180606001604052806036815260200162005764603691396005906200005790826200036b565b503480156200006557600080fd5b506040516200579a3803806200579a833981016040819052620000889162000454565b604080518082018252601181527047616e67737461204d696365204369747960781b60208083019190915282518084019093526003835262474d4360e81b908301526001600160a01b0380851660c052831660a0529083838383620000ee8282620001b4565b505050505050620001046200021460201b60201c565b600280546b01000000000000000000000033027fff0000000000000000000000000000000000000000ffffffffffff0000ffffff90911617641a0a0000001790554260e0526200015b66ae153d89fe800062000267565b6002805460ff9290921669010000000000000000000260ff60481b1990921691909117905562000192668a8e4b1a3d800062000267565b6002600a6101000a81548160ff021916908360ff16021790555050506200048c565b7facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599620001e183826200036b565b507facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559a6200020f82826200036b565b505050565b303b15620002345760405162dc149f60e41b815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380546001600160a01b03191633179055565b60008066038d7ea4c680008306156200029357604051632411db2d60e11b815260040160405180910390fd5b5066038d7ea4c68000820460ff811115620002c157604051632411db2d60e11b815260040160405180910390fd5b92915050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620002f257607f821691505b6020821081036200031357634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200020f57600081815260208120601f850160051c81016020861015620003425750805b601f850160051c820191505b8181101562000363578281556001016200034e565b505050505050565b81516001600160401b03811115620003875762000387620002c7565b6200039f81620003988454620002dd565b8462000319565b602080601f831160018114620003d75760008415620003be5750858301515b600019600386901b1c1916600185901b17855562000363565b600085815260208120601f198616915b828110156200040857888601518255948401946001909101908401620003e7565b5085821015620004275787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516001600160a01b03811681146200044f57600080fd5b919050565b600080604083850312156200046857600080fd5b620004738362000437565b9150620004836020840162000437565b90509250929050565b60805160a05160c05160e051615293620004d1600039600081816111b5015261171901526000610b17015260008181610c0201526145910152600050506152936000f3fe6080604052600436106103ad5760003560e01c806388486f86116101e7578063bb58131f1161010d578063de9b771f116100a0578063f2fde38b1161006f578063f2fde38b14610ccc578063fc1a1c3614610cec578063fca76c2614610d1c578063fe2c7fee14610d3157600080fd5b8063de9b771f14610bf0578063e7f371e614610c24578063e985e9c514610c37578063eec1cbb714610cac57600080fd5b8063c90af357116100dc578063c90af35714610b79578063ca9228a114610b99578063d5abeb0114610bb9578063d75e611014610bdb57600080fd5b8063bb58131f14610ae5578063c0857ba014610b05578063c627525514610b39578063c87b56dd14610b5957600080fd5b80639be65a6011610185578063a945bf8011610154578063a945bf8014610a56578063aea4e49e14610a85578063af436af314610aa5578063b88d4fde14610ac557600080fd5b80639be65a60146109dc578063a22cb465146109fc578063a742530814610a1c578063a8d0466c14610a3c57600080fd5b806395e1a8a5116101c157806395e1a8a514610970578063972c4928146109875780639871e2221461099c5780639888eb1b146109bc57600080fd5b806388486f86146109265780638da5cb5b1461094657806395d89b411461095b57600080fd5b80633644e515116102d757806367f68fac1161026a57806370a082311161023957806370a082311461086f578063717d57d31461088f5780637ecebe00146108af5780638456cb591461091157600080fd5b806367f68fac146107ef5780636c19e783146108025780636e8ba803146108225780636e9010381461084257600080fd5b806355f804b3116102a657806355f804b314610740578063607f2d42146107605780636352211e146107af57806364c6cbbd146107cf57600080fd5b80633644e515146106365780633ccfd60b146106eb57806342842e0e1461070057806348613c281461072057600080fd5b806311836f321161034f57806323b872dd1161031e57806323b872dd146105a6578063255e4685146105c6578063257f84ae14610601578063306c76791461061657600080fd5b806311836f321461051a57806318160ddd146105485780631fb161b81461056657806320fc7eb21461058657600080fd5b806306fdde031161038b57806306fdde031461043c578063081812fc1461045e578063095ea7b3146104e55780630f2cdd6c1461050557600080fd5b806301ffc9a7146103b2578063047fc9aa146103e757806306421c2f1461041a575b600080fd5b3480156103be57600080fd5b506103d26103cd3660046146fb565b610d51565b60405190151581526020015b60405180910390f35b3480156103f357600080fd5b5060025461040790610100900461ffff1681565b60405161ffff90911681526020016103de565b34801561042657600080fd5b5061043a610435366004614718565b610e36565b005b34801561044857600080fd5b50610451610f35565b6040516103de91906147b2565b34801561046a57600080fd5b506104c06104793660046147c5565b60009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103de565b3480156104f157600080fd5b5061043a610500366004614800565b610fe6565b34801561051157600080fd5b50610407601481565b34801561052657600080fd5b5061053a6105353660046147c5565b611148565b6040519081526020016103de565b34801561055457600080fd5b50600254610100900461ffff1661053a565b34801561057257600080fd5b5061043a610581366004614878565b61116e565b34801561059257600080fd5b5061053a6105a13660046148cd565b61125e565b3480156105b257600080fd5b5061043a6105c13660046148ea565b6112b0565b3480156105d257600080fd5b506002546105ec9065010000000000900463ffffffff1681565b60405163ffffffff90911681526020016103de565b34801561060d57600080fd5b5061053a611655565b34801561062257600080fd5b5061043a610631366004614878565b6116d9565b34801561064257600080fd5b5061053a604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b3480156106f757600080fd5b5061043a6117af565b34801561070c57600080fd5b5061043a61071b3660046148ea565b6118bd565b34801561072c57600080fd5b5061043a61073b36600461492b565b6118d8565b34801561074c57600080fd5b5061043a61075b3660046149d8565b6119a2565b34801561076c57600080fd5b506103d261077b3660046147c5565b60009081527f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9485602052604090205460ff1690565b3480156107bb57600080fd5b506104c06107ca3660046147c5565b611a35565b3480156107db57600080fd5b5061043a6107ea366004614a1a565b611a43565b61043a6107fd366004614a94565b611b31565b34801561080e57600080fd5b5061043a61081d3660046148cd565b611d12565b34801561082e57600080fd5b5061053a61083d3660046147c5565b611dee565b34801561084e57600080fd5b5061086261085d3660046148cd565b611e09565b6040516103de9190614aff565b34801561087b57600080fd5b5061053a61088a3660046148cd565b611e4d565b34801561089b57600080fd5b5061043a6108aa3660046147c5565b611e9c565b3480156108bb57600080fd5b5061053a6108ca3660046148cd565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604090205490565b34801561091d57600080fd5b5061043a611f48565b34801561093257600080fd5b506104c06109413660046147c5565b611ff8565b34801561095257600080fd5b506104c0612009565b34801561096757600080fd5b50610451612049565b34801561097c57600080fd5b5061053a6201518081565b34801561099357600080fd5b506104c061207a565b3480156109a857600080fd5b506108626109b73660046148cd565b6120a2565b3480156109c857600080fd5b5061053a6109d73660046148cd565b6120e6565b3480156109e857600080fd5b5061043a6109f73660046148cd565b612138565b348015610a0857600080fd5b5061043a610a17366004614b12565b6122e9565b348015610a2857600080fd5b5061053a610a373660046148cd565b61239f565b348015610a4857600080fd5b506002546103d29060ff1681565b348015610a6257600080fd5b5060025466038d7ea4c68000690100000000000000000090910460ff160261053a565b348015610a9157600080fd5b5061043a610aa03660046148cd565b6123f8565b348015610ab157600080fd5b5061053a610ac03660046147c5565b612466565b348015610ad157600080fd5b5061043a610ae0366004614b6f565b61247a565b348015610af157600080fd5b5061043a610b003660046149d8565b6125a5565b348015610b1157600080fd5b506104c07f000000000000000000000000000000000000000000000000000000000000000081565b348015610b4557600080fd5b5061043a610b543660046147c5565b612638565b348015610b6557600080fd5b50610451610b743660046147c5565b6126e4565b348015610b8557600080fd5b5061043a610b94366004614c6d565b6127be565b348015610ba557600080fd5b5061043a610bb4366004614c93565b612884565b348015610bc557600080fd5b50600254610407906301000000900461ffff1681565b348015610be757600080fd5b5061053a600581565b348015610bfc57600080fd5b506104c07f000000000000000000000000000000000000000000000000000000000000000081565b61043a610c32366004614cf2565b612a0e565b348015610c4357600080fd5b506103d2610c52366004614d5c565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832093909416825291909152205460ff1690565b348015610cb857600080fd5b50610862610cc73660046148cd565b612c21565b348015610cd857600080fd5b5061043a610ce73660046148cd565b612c65565b348015610cf857600080fd5b5060025466038d7ea4c680006a010000000000000000000090910460ff160261053a565b348015610d2857600080fd5b5061043a612d8b565b348015610d3d57600080fd5b5061043a610d4c3660046149d8565b612e3e565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610de457507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610e3057507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebc576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460ff1615610ef9576040517fde7c738300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805461ffff9092166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffff909216919091179055565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955998054610f6390614d8a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f8f90614d8a565b8015610fdc5780601f10610fb157610100808354040283529160200191610fdc565b820191906000526020600020905b815481529060010190602001808311610fbf57829003601f168201915b5050505050905090565b6000610ff9610ff483612ed1565b612f7a565b90503373ffffffffffffffffffffffffffffffffffffffff821614801590611071575073ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff16155b156110a8576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60066020908152600782901c60009081529081205460fe600184901b161c600316610e30565b60148111156111a9576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015906111dc57507f000000000000000000000000000000000000000000000000000000000000000062093a800142105b80156111ff5750600254611c2063ffffffff650100000000009092048216011642105b1561124e5760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b611259838383612f9c565b505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460141c620fffff16610e30565b3073ffffffffffffffffffffffffffffffffffffffff8316036112ff576040517fdf5507ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661134c576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061135782612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff861614806113ce575073ffffffffffffffffffffffffffffffffffffffff851660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b8061141b575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080611454576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff1661147483612f7a565b73ffffffffffffffffffffffffffffffffffffffff16146114c1576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556115216001840183612fdd565b61157a7fffffffffffffffffffffffff0000000000000000000000000000000000000000831673ffffffffffffffffffffffffffffffffffffffff8616175b740400000000000000000000000000000000000000001790565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff8781168084527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90925283832080546001019055881680835283832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559251869391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a45050505050565b60008060008061166361306a565b9050600060015b828110156116cf5760008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054945084156116ac578491505b6116bb8260a11c600116151590565b156116c7578360010193505b60010161166a565b5091949350505050565b6014811115611714576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117417f000000000000000000000000000000000000000000000000000000000000000062093a80614e0c565b4210801561176d575060025461176a90620151809065010000000000900463ffffffff16614e0c565b42105b156117a4576040517f7d857b6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611259838383613084565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611835576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040514790600090339083908381818185875af1925050503d8060008114611879576040519150601f19603f3d011682016040523d82523d6000602084013e61187e565b606091505b50509050806118b9576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6112598383836040518060200160405280600081525061247a565b6118e886866001878787876130c6565b73ffffffffffffffffffffffffffffffffffffffff86811660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f60209081526040808320948a168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a28576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003611259828483614e6a565b6000610e30610ff483612ed1565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ac9576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b83811015611b2a57611b1a858583818110611ae957611ae9614f84565b90506020020135848484818110611b0257611b02614f84565b9050602002013560066133f19092919063ffffffff16565b611b2381614fb3565b9050611acc565b5050505050565b323314611b6a576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254829061ffff6301000000820481166101009092041682011115611bbc576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260146005821115611bfa576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611c043361125e565b83011115611c3e576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84611c6260025460ff69010000000000000000009091041666038d7ea4c680000290565b023414611c9b576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254611c2063ffffffff6501000000000090920482160116421080611cd0575060025465010000000000900463ffffffff16155b15611d07576040517fc7d08f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b2a338686613401565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d98576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805473ffffffffffffffffffffffffffffffffffffffff9092166b010000000000000000000000027fff0000000000000000000000000000000000000000ffffffffffffffffffffff909216919091179055565b6000610e30611dfc83612ed1565b60a81c64ffffffffff1690565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001611e4860025461ffff6101009091041690565b6134fd565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f22576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611f2b816135c6565b6002600a6101000a81548160ff021916908360ff16021790555050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff169055565b6000610e3061200683612ed1565b90565b60007f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996001018054610f6390614d8a565b60007f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948461202d565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996004018360016120e160025461ffff6101009091041690565b613650565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460281c620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146121be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa15801561222b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224f9190614feb565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526024810182905290915073ffffffffffffffffffffffffffffffffffffffff83169063a9059cbb906044016020604051808303816000875af11580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112599190615004565b3360008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054603c1c64ffffffffff16610e30565b905090565b6124006136de565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000610e3061247483612ed1565b60d01c90565b6124858484846112b0565b73ffffffffffffffffffffffffffffffffffffffff83163b1580159061256857506040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612500903390899088908890600401615021565b6020604051808303816000875af115801561251f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612543919061506a565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b1561259f576040517ff5cd1f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461262b576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004611259828483614e6a565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146126be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126c7816135c6565b600260096101000a81548160ff021916908360ff16021790555050565b6060600380546126f390614d8a565b15905061272d57600361270583613766565b600460405160200161271993929190615118565b604051602081830303815290604052610e30565b6005805461273a90614d8a565b80601f016020809104026020016040519081016040528092919081815260200182805461276690614d8a565b80156127b35780601f10612788576101008083540402835291602001916127b3565b820191906000526020600020905b81548152906001019060200180831161279657829003601f168201915b505050505092915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612844576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805463ffffffff90921665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff909216919091179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461290a576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612914838361514b565b60025461ffff6301000000820481166101009092041682011115612964576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81156129bb5760005b848110156129b5576129a586868381811061298a5761298a614f84565b905060200201602081019061299f91906148cd565b856137c8565b6129ae81614fb3565b905061296d565b50611b2a565b60005b84811015612a06576129f68686838181106129db576129db614f84565b90506020020160208101906129f091906148cd565b856137d4565b6129ff81614fb3565b90506129be565b505050505050565b323314612a47576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254859061ffff6301000000820481166101009092041682011115612a99576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85846005821115612ad6576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80612ae03361125e565b83011115612b1a576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b258585886137e1565b612b5b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254426501000000000090910463ffffffff908116611c2001161015612bae576040517f04cc9ce200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87612bd360025460ff6a01000000000000000000009091041666038d7ea4c680000290565b023414612c0c576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c17338989613401565b5050505050505050565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001612c6060025461ffff6101009091041690565b6138b3565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ceb576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051338152602081019290925280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e11576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ec4576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005611259828483614e6a565b6000612edc8261392a565b612f12576040517fb1d04f0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000825b60008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090205491508115612f53575092915050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612f16565b6000612f8b8260a11c600116151590565b612f955781610e30565b3092915050565b60005b81811015612fd157612fc984848484818110612fbd57612fbd614f84565b90506020020135613948565b600101612f9f565b50611259838383613cad565b612fec8160a21c600116151590565b158015613024575060008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054155b801561303457506130348261392a565b156118b95760009182527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090912055565b600254600090610100900461ffff166123f3906001614e0c565b60005b818110156130b9576130b1848484848181106130a5576130a5614f84565b90506020020135613d68565b600101613087565b5061125960008383613cad565b42841015613100576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604081208054600180820190925591906131f8604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b604080517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9602082015273ffffffffffffffffffffffffffffffffffffffff808e1692820192909252908b166060820152608081018a905260a0810185905260c0810189905260e001604051602081830303815290604052805190602001206040516020016132b99291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015613335573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615806133af57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b156133e6576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050565b6020928352600091825291902055565b60028211156134705760405173ffffffffffffffffffffffffffffffffffffffff841681527f9065519f7be06a0e88a7d6493f56b85b3714d9efd3de44c904169be8d4589b649060200160405180910390a160025461041f61010090910461ffff161015613470578160010191505b8080156134945750600254611c2063ffffffff650100000000009092048216011642105b156134e35760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b80156134f35761125983836137c8565b61125983836137d4565b60405160208101600080848601865b8181101561358d57600081815260208b905260409020549350831561352f578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614801561357657506135748360a11c600116151590565b155b15613585578085526020850194505b60010161350c565b505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08282030160051c8252604052949350505050565b60008066038d7ea4c6800083061561360a576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5066038d7ea4c68000820460ff811115610e30576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405160208101600080848601865b8181101561358d57600081815260208b9052604090205493508315613682578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480156136c757506136c78360a11c600116151590565b156136d6578085526020850194505b60010161365f565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613764576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a90048061378057508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909101908152919050565b6118b9828260006140cb565b6118b98282600080614169565b60408051306020820152339181019190915260608101829052600090819060800160405160208183030381529060405280519060200120905060006138578686613850856020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b919061449d565b905073ffffffffffffffffffffffffffffffffffffffff8116158015906138a7575060025473ffffffffffffffffffffffffffffffffffffffff8281166b01000000000000000000000090920416145b925050505b9392505050565b60405160208101600080848601865b8181101561358d57600081815260208b90526040902054935083156138e5578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1603613922578085526020850194505b6001016138c2565b600081600111158015610e30575061394061306a565b821092915050565b600061395382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff851614806139ca575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613a17575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613a50576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16613a7083612f7a565b73ffffffffffffffffffffffffffffffffffffffff1614613abd576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055613b1d6001840183612fdd565b613b867ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff4260a81b7fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff851617740200000000000000000000000000000000000000001716611560565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90522054613c3390429065010000000000015b7ffffffffffffffffffffffffffffffffffffffff0000000000fffffffffffffff16603c9190911b1790565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604080822093909355915185923092917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b6112597f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a848484604051602401613ce693929190615188565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261450c565b6000613d7382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff85161480613dea575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613e37575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613e70576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e7f8260a11c600116151590565b613eb5576040517f30c8d84f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84168273ffffffffffffffffffffffffffffffffffffffff1614613f1a576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613f298260a01c600116151590565b15613f6957613f3b8360010183612fdd565b613f667ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff8316611560565b91505b7fffffffffffff0000000000fdffffffffffffffffffffffffffffffffffffffff82167ffffffffffffffffffffffffdffffffffffffffffffffffffffffffffffffffff4260a81b161760008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c905220546140529042907fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000001613c07565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040808220939093559151859230917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b60006140d561306a565b90506140e48484600185614169565b60008367ffffffffffffffff8111156140ff576140ff614b40565b604051908082528060200260200182016040528015614128578160200160208202803683370190505b50905060005b8481101561415e5780830182828151811061414b5761414b614f84565b602090810291909101015260010161412e565b50611b2a8582614657565b826000036141a3576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166141f0576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006141fa61306a565b73ffffffffffffffffffffffffffffffffffffffff861660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604090205491925060d084901b7fffffffffffff0000000000000000000000000000000000000000000000000000161790600186900361428f5774040000000000000000000000000000000000000000821791505b84156143a657740200000000000000000000000000000000000000007fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff740100000000000000000000000000000000000000008417164260a81b171791506142fd42613c07838960281b0190565b905060005b868110156143a0576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a460405184820190309073ffffffffffffffffffffffffffffffffffffffff8b16907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a4600101614302565b50614402565b60005b86811015614400576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46001016143a9565b505b61441686614412838260141b0190565b0190565b73ffffffffffffffffffffffffffffffffffffffff881660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c60209081526040808320939093558582527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d9052208290556144948661468e565b50505050505050565b6000604182036138ac576040516040846040377f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0606051116145025784600052604084013560001a602052602060406080600060015afa5060006060523d6060035191505b6040529392505050565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94845473ffffffffffffffffffffffffffffffffffffffff1661457a576040517fafd0683400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b47204777f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9484546040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526146299173ffffffffffffffffffffffffffffffffffffffff169085906004016151fa565b600060405180830381600087803b15801561464357600080fd5b505af1158015611b2a573d6000803e3d6000fd5b6118b97f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a8383604051602401613ce6929190615231565b80600260018282829054906101000a900461ffff166146ad9190615260565b92506101000a81548161ffff021916908361ffff16021790555050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146146f857600080fd5b50565b60006020828403121561470d57600080fd5b81356138ac816146ca565b60006020828403121561472a57600080fd5b813561ffff811681146138ac57600080fd5b60005b8381101561475757818101518382015260200161473f565b8381111561259f5750506000910152565b6000815180845261478081602086016020860161473c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006138ac6020830184614768565b6000602082840312156147d757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146146f857600080fd5b6000806040838503121561481357600080fd5b823561481e816147de565b946020939093013593505050565b60008083601f84011261483e57600080fd5b50813567ffffffffffffffff81111561485657600080fd5b6020830191508360208260051b850101111561487157600080fd5b9250929050565b60008060006040848603121561488d57600080fd5b8335614898816147de565b9250602084013567ffffffffffffffff8111156148b457600080fd5b6148c08682870161482c565b9497909650939450505050565b6000602082840312156148df57600080fd5b81356138ac816147de565b6000806000606084860312156148ff57600080fd5b833561490a816147de565b9250602084013561491a816147de565b929592945050506040919091013590565b60008060008060008060c0878903121561494457600080fd5b863561494f816147de565b9550602087013561495f816147de565b945060408701359350606087013560ff8116811461497c57600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126149a857600080fd5b50813567ffffffffffffffff8111156149c057600080fd5b60208301915083602082850101111561487157600080fd5b600080602083850312156149eb57600080fd5b823567ffffffffffffffff811115614a0257600080fd5b614a0e85828601614996565b90969095509350505050565b60008060008060408587031215614a3057600080fd5b843567ffffffffffffffff80821115614a4857600080fd5b614a548883890161482c565b90965094506020870135915080821115614a6d57600080fd5b50614a7a8782880161482c565b95989497509550505050565b80151581146146f857600080fd5b60008060408385031215614aa757600080fd5b823591506020830135614ab981614a86565b809150509250929050565b600081518084526020808501945080840160005b83811015614af457815187529582019590820190600101614ad8565b509495945050505050565b6020815260006138ac6020830184614ac4565b60008060408385031215614b2557600080fd5b8235614b30816147de565b91506020830135614ab981614a86565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215614b8557600080fd5b8435614b90816147de565b93506020850135614ba0816147de565b925060408501359150606085013567ffffffffffffffff80821115614bc457600080fd5b818701915087601f830112614bd857600080fd5b813581811115614bea57614bea614b40565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715614c3057614c30614b40565b816040528281528a6020848701011115614c4957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215614c7f57600080fd5b813563ffffffff811681146138ac57600080fd5b60008060008060608587031215614ca957600080fd5b843567ffffffffffffffff811115614cc057600080fd5b614ccc8782880161482c565b909550935050602085013591506040850135614ce781614a86565b939692955090935050565b600080600080600060808688031215614d0a57600080fd5b853594506020860135614d1c81614a86565b935060408601359250606086013567ffffffffffffffff811115614d3f57600080fd5b614d4b88828901614996565b969995985093965092949392505050565b60008060408385031215614d6f57600080fd5b8235614d7a816147de565b91506020830135614ab9816147de565b600181811c90821680614d9e57607f821691505b602082108103614dd7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614e1f57614e1f614ddd565b500190565b601f82111561125957600081815260208120601f850160051c81016020861015614e4b5750805b601f850160051c820191505b81811015612a0657828155600101614e57565b67ffffffffffffffff831115614e8257614e82614b40565b614e9683614e908354614d8a565b83614e24565b6000601f841160018114614ee85760008515614eb25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b2a565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614f375786850135825560209485019460019092019101614f17565b5086821015614f72577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614fe457614fe4614ddd565b5060010190565b600060208284031215614ffd57600080fd5b5051919050565b60006020828403121561501657600080fd5b81516138ac81614a86565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526150606080830184614768565b9695505050505050565b60006020828403121561507c57600080fd5b81516138ac816146ca565b6000815461509481614d8a565b600182811680156150ac57600181146150df5761510e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008416875282151583028701945061510e565b8560005260208060002060005b858110156151055781548a8201529084019082016150ec565b50505082870194505b5050505092915050565b60006151248286615087565b845161513481836020890161473c565b61514081830186615087565b979650505050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561518357615183614ddd565b500290565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015281604082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151dd57600080fd5b8260051b8085606085013760009201606001918252509392505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614768565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614ac4565b600061ffff80831681851680830382111561527d5761527d614ddd565b0194935050505056fea164736f6c634300080f000a697066733a2f2f516d547639566f58676b5a7846636f6d5457336b4e364352727955504d666765556b56656b46737a63643739674b2f00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
Deployed Bytecode
0x6080604052600436106103ad5760003560e01c806388486f86116101e7578063bb58131f1161010d578063de9b771f116100a0578063f2fde38b1161006f578063f2fde38b14610ccc578063fc1a1c3614610cec578063fca76c2614610d1c578063fe2c7fee14610d3157600080fd5b8063de9b771f14610bf0578063e7f371e614610c24578063e985e9c514610c37578063eec1cbb714610cac57600080fd5b8063c90af357116100dc578063c90af35714610b79578063ca9228a114610b99578063d5abeb0114610bb9578063d75e611014610bdb57600080fd5b8063bb58131f14610ae5578063c0857ba014610b05578063c627525514610b39578063c87b56dd14610b5957600080fd5b80639be65a6011610185578063a945bf8011610154578063a945bf8014610a56578063aea4e49e14610a85578063af436af314610aa5578063b88d4fde14610ac557600080fd5b80639be65a60146109dc578063a22cb465146109fc578063a742530814610a1c578063a8d0466c14610a3c57600080fd5b806395e1a8a5116101c157806395e1a8a514610970578063972c4928146109875780639871e2221461099c5780639888eb1b146109bc57600080fd5b806388486f86146109265780638da5cb5b1461094657806395d89b411461095b57600080fd5b80633644e515116102d757806367f68fac1161026a57806370a082311161023957806370a082311461086f578063717d57d31461088f5780637ecebe00146108af5780638456cb591461091157600080fd5b806367f68fac146107ef5780636c19e783146108025780636e8ba803146108225780636e9010381461084257600080fd5b806355f804b3116102a657806355f804b314610740578063607f2d42146107605780636352211e146107af57806364c6cbbd146107cf57600080fd5b80633644e515146106365780633ccfd60b146106eb57806342842e0e1461070057806348613c281461072057600080fd5b806311836f321161034f57806323b872dd1161031e57806323b872dd146105a6578063255e4685146105c6578063257f84ae14610601578063306c76791461061657600080fd5b806311836f321461051a57806318160ddd146105485780631fb161b81461056657806320fc7eb21461058657600080fd5b806306fdde031161038b57806306fdde031461043c578063081812fc1461045e578063095ea7b3146104e55780630f2cdd6c1461050557600080fd5b806301ffc9a7146103b2578063047fc9aa146103e757806306421c2f1461041a575b600080fd5b3480156103be57600080fd5b506103d26103cd3660046146fb565b610d51565b60405190151581526020015b60405180910390f35b3480156103f357600080fd5b5060025461040790610100900461ffff1681565b60405161ffff90911681526020016103de565b34801561042657600080fd5b5061043a610435366004614718565b610e36565b005b34801561044857600080fd5b50610451610f35565b6040516103de91906147b2565b34801561046a57600080fd5b506104c06104793660046147c5565b60009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103de565b3480156104f157600080fd5b5061043a610500366004614800565b610fe6565b34801561051157600080fd5b50610407601481565b34801561052657600080fd5b5061053a6105353660046147c5565b611148565b6040519081526020016103de565b34801561055457600080fd5b50600254610100900461ffff1661053a565b34801561057257600080fd5b5061043a610581366004614878565b61116e565b34801561059257600080fd5b5061053a6105a13660046148cd565b61125e565b3480156105b257600080fd5b5061043a6105c13660046148ea565b6112b0565b3480156105d257600080fd5b506002546105ec9065010000000000900463ffffffff1681565b60405163ffffffff90911681526020016103de565b34801561060d57600080fd5b5061053a611655565b34801561062257600080fd5b5061043a610631366004614878565b6116d9565b34801561064257600080fd5b5061053a604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b3480156106f757600080fd5b5061043a6117af565b34801561070c57600080fd5b5061043a61071b3660046148ea565b6118bd565b34801561072c57600080fd5b5061043a61073b36600461492b565b6118d8565b34801561074c57600080fd5b5061043a61075b3660046149d8565b6119a2565b34801561076c57600080fd5b506103d261077b3660046147c5565b60009081527f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9485602052604090205460ff1690565b3480156107bb57600080fd5b506104c06107ca3660046147c5565b611a35565b3480156107db57600080fd5b5061043a6107ea366004614a1a565b611a43565b61043a6107fd366004614a94565b611b31565b34801561080e57600080fd5b5061043a61081d3660046148cd565b611d12565b34801561082e57600080fd5b5061053a61083d3660046147c5565b611dee565b34801561084e57600080fd5b5061086261085d3660046148cd565b611e09565b6040516103de9190614aff565b34801561087b57600080fd5b5061053a61088a3660046148cd565b611e4d565b34801561089b57600080fd5b5061043a6108aa3660046147c5565b611e9c565b3480156108bb57600080fd5b5061053a6108ca3660046148cd565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604090205490565b34801561091d57600080fd5b5061043a611f48565b34801561093257600080fd5b506104c06109413660046147c5565b611ff8565b34801561095257600080fd5b506104c0612009565b34801561096757600080fd5b50610451612049565b34801561097c57600080fd5b5061053a6201518081565b34801561099357600080fd5b506104c061207a565b3480156109a857600080fd5b506108626109b73660046148cd565b6120a2565b3480156109c857600080fd5b5061053a6109d73660046148cd565b6120e6565b3480156109e857600080fd5b5061043a6109f73660046148cd565b612138565b348015610a0857600080fd5b5061043a610a17366004614b12565b6122e9565b348015610a2857600080fd5b5061053a610a373660046148cd565b61239f565b348015610a4857600080fd5b506002546103d29060ff1681565b348015610a6257600080fd5b5060025466038d7ea4c68000690100000000000000000090910460ff160261053a565b348015610a9157600080fd5b5061043a610aa03660046148cd565b6123f8565b348015610ab157600080fd5b5061053a610ac03660046147c5565b612466565b348015610ad157600080fd5b5061043a610ae0366004614b6f565b61247a565b348015610af157600080fd5b5061043a610b003660046149d8565b6125a5565b348015610b1157600080fd5b506104c07f00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c28781565b348015610b4557600080fd5b5061043a610b543660046147c5565b612638565b348015610b6557600080fd5b50610451610b743660046147c5565b6126e4565b348015610b8557600080fd5b5061043a610b94366004614c6d565b6127be565b348015610ba557600080fd5b5061043a610bb4366004614c93565b612884565b348015610bc557600080fd5b50600254610407906301000000900461ffff1681565b348015610be757600080fd5b5061053a600581565b348015610bfc57600080fd5b506104c07f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a281565b61043a610c32366004614cf2565b612a0e565b348015610c4357600080fd5b506103d2610c52366004614d5c565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832093909416825291909152205460ff1690565b348015610cb857600080fd5b50610862610cc73660046148cd565b612c21565b348015610cd857600080fd5b5061043a610ce73660046148cd565b612c65565b348015610cf857600080fd5b5060025466038d7ea4c680006a010000000000000000000090910460ff160261053a565b348015610d2857600080fd5b5061043a612d8b565b348015610d3d57600080fd5b5061043a610d4c3660046149d8565b612e3e565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610de457507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610e3057507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebc576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460ff1615610ef9576040517fde7c738300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805461ffff9092166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffff909216919091179055565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955998054610f6390614d8a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f8f90614d8a565b8015610fdc5780601f10610fb157610100808354040283529160200191610fdc565b820191906000526020600020905b815481529060010190602001808311610fbf57829003601f168201915b5050505050905090565b6000610ff9610ff483612ed1565b612f7a565b90503373ffffffffffffffffffffffffffffffffffffffff821614801590611071575073ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff16155b156110a8576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60066020908152600782901c60009081529081205460fe600184901b161c600316610e30565b60148111156111a9576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015906111dc57507f00000000000000000000000000000000000000000000000000000000633bf57362093a800142105b80156111ff5750600254611c2063ffffffff650100000000009092048216011642105b1561124e5760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b611259838383612f9c565b505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460141c620fffff16610e30565b3073ffffffffffffffffffffffffffffffffffffffff8316036112ff576040517fdf5507ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661134c576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061135782612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff861614806113ce575073ffffffffffffffffffffffffffffffffffffffff851660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b8061141b575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080611454576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff1661147483612f7a565b73ffffffffffffffffffffffffffffffffffffffff16146114c1576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556115216001840183612fdd565b61157a7fffffffffffffffffffffffff0000000000000000000000000000000000000000831673ffffffffffffffffffffffffffffffffffffffff8616175b740400000000000000000000000000000000000000001790565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff8781168084527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90925283832080546001019055881680835283832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559251869391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a45050505050565b60008060008061166361306a565b9050600060015b828110156116cf5760008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054945084156116ac578491505b6116bb8260a11c600116151590565b156116c7578360010193505b60010161166a565b5091949350505050565b6014811115611714576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117417f00000000000000000000000000000000000000000000000000000000633bf57362093a80614e0c565b4210801561176d575060025461176a90620151809065010000000000900463ffffffff16614e0c565b42105b156117a4576040517f7d857b6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611259838383613084565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611835576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040514790600090339083908381818185875af1925050503d8060008114611879576040519150601f19603f3d011682016040523d82523d6000602084013e61187e565b606091505b50509050806118b9576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6112598383836040518060200160405280600081525061247a565b6118e886866001878787876130c6565b73ffffffffffffffffffffffffffffffffffffffff86811660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f60209081526040808320948a168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a28576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003611259828483614e6a565b6000610e30610ff483612ed1565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ac9576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b83811015611b2a57611b1a858583818110611ae957611ae9614f84565b90506020020135848484818110611b0257611b02614f84565b9050602002013560066133f19092919063ffffffff16565b611b2381614fb3565b9050611acc565b5050505050565b323314611b6a576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254829061ffff6301000000820481166101009092041682011115611bbc576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260146005821115611bfa576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611c043361125e565b83011115611c3e576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84611c6260025460ff69010000000000000000009091041666038d7ea4c680000290565b023414611c9b576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254611c2063ffffffff6501000000000090920482160116421080611cd0575060025465010000000000900463ffffffff16155b15611d07576040517fc7d08f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b2a338686613401565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d98576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805473ffffffffffffffffffffffffffffffffffffffff9092166b010000000000000000000000027fff0000000000000000000000000000000000000000ffffffffffffffffffffff909216919091179055565b6000610e30611dfc83612ed1565b60a81c64ffffffffff1690565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001611e4860025461ffff6101009091041690565b6134fd565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f22576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611f2b816135c6565b6002600a6101000a81548160ff021916908360ff16021790555050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff169055565b6000610e3061200683612ed1565b90565b60007f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996001018054610f6390614d8a565b60007f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948461202d565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996004018360016120e160025461ffff6101009091041690565b613650565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460281c620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146121be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa15801561222b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224f9190614feb565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526024810182905290915073ffffffffffffffffffffffffffffffffffffffff83169063a9059cbb906044016020604051808303816000875af11580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112599190615004565b3360008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054603c1c64ffffffffff16610e30565b905090565b6124006136de565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000610e3061247483612ed1565b60d01c90565b6124858484846112b0565b73ffffffffffffffffffffffffffffffffffffffff83163b1580159061256857506040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612500903390899088908890600401615021565b6020604051808303816000875af115801561251f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612543919061506a565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b1561259f576040517ff5cd1f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461262b576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004611259828483614e6a565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146126be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126c7816135c6565b600260096101000a81548160ff021916908360ff16021790555050565b6060600380546126f390614d8a565b15905061272d57600361270583613766565b600460405160200161271993929190615118565b604051602081830303815290604052610e30565b6005805461273a90614d8a565b80601f016020809104026020016040519081016040528092919081815260200182805461276690614d8a565b80156127b35780601f10612788576101008083540402835291602001916127b3565b820191906000526020600020905b81548152906001019060200180831161279657829003601f168201915b505050505092915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612844576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805463ffffffff90921665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff909216919091179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461290a576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612914838361514b565b60025461ffff6301000000820481166101009092041682011115612964576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81156129bb5760005b848110156129b5576129a586868381811061298a5761298a614f84565b905060200201602081019061299f91906148cd565b856137c8565b6129ae81614fb3565b905061296d565b50611b2a565b60005b84811015612a06576129f68686838181106129db576129db614f84565b90506020020160208101906129f091906148cd565b856137d4565b6129ff81614fb3565b90506129be565b505050505050565b323314612a47576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254859061ffff6301000000820481166101009092041682011115612a99576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85846005821115612ad6576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80612ae03361125e565b83011115612b1a576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b258585886137e1565b612b5b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254426501000000000090910463ffffffff908116611c2001161015612bae576040517f04cc9ce200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87612bd360025460ff6a01000000000000000000009091041666038d7ea4c680000290565b023414612c0c576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c17338989613401565b5050505050505050565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001612c6060025461ffff6101009091041690565b6138b3565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ceb576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051338152602081019290925280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e11576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ec4576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005611259828483614e6a565b6000612edc8261392a565b612f12576040517fb1d04f0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000825b60008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090205491508115612f53575092915050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612f16565b6000612f8b8260a11c600116151590565b612f955781610e30565b3092915050565b60005b81811015612fd157612fc984848484818110612fbd57612fbd614f84565b90506020020135613948565b600101612f9f565b50611259838383613cad565b612fec8160a21c600116151590565b158015613024575060008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054155b801561303457506130348261392a565b156118b95760009182527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090912055565b600254600090610100900461ffff166123f3906001614e0c565b60005b818110156130b9576130b1848484848181106130a5576130a5614f84565b90506020020135613d68565b600101613087565b5061125960008383613cad565b42841015613100576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604081208054600180820190925591906131f8604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b604080517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9602082015273ffffffffffffffffffffffffffffffffffffffff808e1692820192909252908b166060820152608081018a905260a0810185905260c0810189905260e001604051602081830303815290604052805190602001206040516020016132b99291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015613335573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615806133af57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b156133e6576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050565b6020928352600091825291902055565b60028211156134705760405173ffffffffffffffffffffffffffffffffffffffff841681527f9065519f7be06a0e88a7d6493f56b85b3714d9efd3de44c904169be8d4589b649060200160405180910390a160025461041f61010090910461ffff161015613470578160010191505b8080156134945750600254611c2063ffffffff650100000000009092048216011642105b156134e35760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b80156134f35761125983836137c8565b61125983836137d4565b60405160208101600080848601865b8181101561358d57600081815260208b905260409020549350831561352f578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614801561357657506135748360a11c600116151590565b155b15613585578085526020850194505b60010161350c565b505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08282030160051c8252604052949350505050565b60008066038d7ea4c6800083061561360a576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5066038d7ea4c68000820460ff811115610e30576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405160208101600080848601865b8181101561358d57600081815260208b9052604090205493508315613682578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480156136c757506136c78360a11c600116151590565b156136d6578085526020850194505b60010161365f565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613764576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a90048061378057508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909101908152919050565b6118b9828260006140cb565b6118b98282600080614169565b60408051306020820152339181019190915260608101829052600090819060800160405160208183030381529060405280519060200120905060006138578686613850856020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b919061449d565b905073ffffffffffffffffffffffffffffffffffffffff8116158015906138a7575060025473ffffffffffffffffffffffffffffffffffffffff8281166b01000000000000000000000090920416145b925050505b9392505050565b60405160208101600080848601865b8181101561358d57600081815260208b90526040902054935083156138e5578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1603613922578085526020850194505b6001016138c2565b600081600111158015610e30575061394061306a565b821092915050565b600061395382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff851614806139ca575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613a17575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613a50576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16613a7083612f7a565b73ffffffffffffffffffffffffffffffffffffffff1614613abd576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055613b1d6001840183612fdd565b613b867ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff4260a81b7fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff851617740200000000000000000000000000000000000000001716611560565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90522054613c3390429065010000000000015b7ffffffffffffffffffffffffffffffffffffffff0000000000fffffffffffffff16603c9190911b1790565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604080822093909355915185923092917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b6112597f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a848484604051602401613ce693929190615188565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261450c565b6000613d7382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff85161480613dea575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613e37575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613e70576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e7f8260a11c600116151590565b613eb5576040517f30c8d84f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84168273ffffffffffffffffffffffffffffffffffffffff1614613f1a576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613f298260a01c600116151590565b15613f6957613f3b8360010183612fdd565b613f667ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff8316611560565b91505b7fffffffffffff0000000000fdffffffffffffffffffffffffffffffffffffffff82167ffffffffffffffffffffffffdffffffffffffffffffffffffffffffffffffffff4260a81b161760008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c905220546140529042907fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000001613c07565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040808220939093559151859230917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b60006140d561306a565b90506140e48484600185614169565b60008367ffffffffffffffff8111156140ff576140ff614b40565b604051908082528060200260200182016040528015614128578160200160208202803683370190505b50905060005b8481101561415e5780830182828151811061414b5761414b614f84565b602090810291909101015260010161412e565b50611b2a8582614657565b826000036141a3576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166141f0576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006141fa61306a565b73ffffffffffffffffffffffffffffffffffffffff861660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604090205491925060d084901b7fffffffffffff0000000000000000000000000000000000000000000000000000161790600186900361428f5774040000000000000000000000000000000000000000821791505b84156143a657740200000000000000000000000000000000000000007fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff740100000000000000000000000000000000000000008417164260a81b171791506142fd42613c07838960281b0190565b905060005b868110156143a0576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a460405184820190309073ffffffffffffffffffffffffffffffffffffffff8b16907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a4600101614302565b50614402565b60005b86811015614400576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46001016143a9565b505b61441686614412838260141b0190565b0190565b73ffffffffffffffffffffffffffffffffffffffff881660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c60209081526040808320939093558582527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d9052208290556144948661468e565b50505050505050565b6000604182036138ac576040516040846040377f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0606051116145025784600052604084013560001a602052602060406080600060015afa5060006060523d6060035191505b6040529392505050565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94845473ffffffffffffffffffffffffffffffffffffffff1661457a576040517fafd0683400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a21663b47204777f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9484546040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526146299173ffffffffffffffffffffffffffffffffffffffff169085906004016151fa565b600060405180830381600087803b15801561464357600080fd5b505af1158015611b2a573d6000803e3d6000fd5b6118b97f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a8383604051602401613ce6929190615231565b80600260018282829054906101000a900461ffff166146ad9190615260565b92506101000a81548161ffff021916908361ffff16021790555050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146146f857600080fd5b50565b60006020828403121561470d57600080fd5b81356138ac816146ca565b60006020828403121561472a57600080fd5b813561ffff811681146138ac57600080fd5b60005b8381101561475757818101518382015260200161473f565b8381111561259f5750506000910152565b6000815180845261478081602086016020860161473c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006138ac6020830184614768565b6000602082840312156147d757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146146f857600080fd5b6000806040838503121561481357600080fd5b823561481e816147de565b946020939093013593505050565b60008083601f84011261483e57600080fd5b50813567ffffffffffffffff81111561485657600080fd5b6020830191508360208260051b850101111561487157600080fd5b9250929050565b60008060006040848603121561488d57600080fd5b8335614898816147de565b9250602084013567ffffffffffffffff8111156148b457600080fd5b6148c08682870161482c565b9497909650939450505050565b6000602082840312156148df57600080fd5b81356138ac816147de565b6000806000606084860312156148ff57600080fd5b833561490a816147de565b9250602084013561491a816147de565b929592945050506040919091013590565b60008060008060008060c0878903121561494457600080fd5b863561494f816147de565b9550602087013561495f816147de565b945060408701359350606087013560ff8116811461497c57600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126149a857600080fd5b50813567ffffffffffffffff8111156149c057600080fd5b60208301915083602082850101111561487157600080fd5b600080602083850312156149eb57600080fd5b823567ffffffffffffffff811115614a0257600080fd5b614a0e85828601614996565b90969095509350505050565b60008060008060408587031215614a3057600080fd5b843567ffffffffffffffff80821115614a4857600080fd5b614a548883890161482c565b90965094506020870135915080821115614a6d57600080fd5b50614a7a8782880161482c565b95989497509550505050565b80151581146146f857600080fd5b60008060408385031215614aa757600080fd5b823591506020830135614ab981614a86565b809150509250929050565b600081518084526020808501945080840160005b83811015614af457815187529582019590820190600101614ad8565b509495945050505050565b6020815260006138ac6020830184614ac4565b60008060408385031215614b2557600080fd5b8235614b30816147de565b91506020830135614ab981614a86565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215614b8557600080fd5b8435614b90816147de565b93506020850135614ba0816147de565b925060408501359150606085013567ffffffffffffffff80821115614bc457600080fd5b818701915087601f830112614bd857600080fd5b813581811115614bea57614bea614b40565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715614c3057614c30614b40565b816040528281528a6020848701011115614c4957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215614c7f57600080fd5b813563ffffffff811681146138ac57600080fd5b60008060008060608587031215614ca957600080fd5b843567ffffffffffffffff811115614cc057600080fd5b614ccc8782880161482c565b909550935050602085013591506040850135614ce781614a86565b939692955090935050565b600080600080600060808688031215614d0a57600080fd5b853594506020860135614d1c81614a86565b935060408601359250606086013567ffffffffffffffff811115614d3f57600080fd5b614d4b88828901614996565b969995985093965092949392505050565b60008060408385031215614d6f57600080fd5b8235614d7a816147de565b91506020830135614ab9816147de565b600181811c90821680614d9e57607f821691505b602082108103614dd7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614e1f57614e1f614ddd565b500190565b601f82111561125957600081815260208120601f850160051c81016020861015614e4b5750805b601f850160051c820191505b81811015612a0657828155600101614e57565b67ffffffffffffffff831115614e8257614e82614b40565b614e9683614e908354614d8a565b83614e24565b6000601f841160018114614ee85760008515614eb25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b2a565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614f375786850135825560209485019460019092019101614f17565b5086821015614f72577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614fe457614fe4614ddd565b5060010190565b600060208284031215614ffd57600080fd5b5051919050565b60006020828403121561501657600080fd5b81516138ac81614a86565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526150606080830184614768565b9695505050505050565b60006020828403121561507c57600080fd5b81516138ac816146ca565b6000815461509481614d8a565b600182811680156150ac57600181146150df5761510e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008416875282151583028701945061510e565b8560005260208060002060005b858110156151055781548a8201529084019082016150ec565b50505082870194505b5050505092915050565b60006151248286615087565b845161513481836020890161473c565b61514081830186615087565b979650505050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561518357615183614ddd565b500290565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015281604082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151dd57600080fd5b8260051b8085606085013760009201606001918252509392505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614768565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614ac4565b600061ffff80831681851680830382111561527d5761527d614ddd565b0194935050505056fea164736f6c634300080f000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
-----Decoded View---------------
Arg [0] : checkpointManager (address): 0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287
Arg [1] : fxRoot (address): 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287
Arg [1] : 000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.