Feature Tip: Add private address tag to any address under My Name Tag !
Overview
TokenID
4725
Total Transfers
-
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract
Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
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
[{"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
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.