Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
Value | ||||
---|---|---|---|---|---|---|---|---|---|
0x61012060 | 19072132 | 164 days ago | IN | Create: FacetEthscriptionBridgeV4 | 0 ETH | 0.04323722 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
FacetEthscriptionBridgeV4
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
No with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "solady/src/utils/ECDSA.sol"; import "solady/src/utils/EIP712.sol"; import "solady/src/utils/LibString.sol"; import "solady/src/utils/ERC1967FactoryConstants.sol"; interface ControlledBridge { function controllerTransferEthscriptions(address recipient, bytes32[] calldata ethscriptionId) external; } contract FacetEthscriptionBridgeV4 is EIP712 { using LibString for *; using ECDSA for bytes32; event ethscriptions_protocol_TransferEthscription( address indexed recipient, bytes32 indexed id ); event ethscriptions_protocol_CreateEthscription( address indexed initialOwner, string contentURI ); struct WithdrawRequest { address recipient; bytes32[] ethscriptionIds; bytes32[] depositIds; bytes32 withdrawalId; bytes32 blockHash; uint256 blockNumber; bytes signature; } struct DepositConfirmation { address sender; bytes ethscriptionIds; bytes32 depositId; bytes32 blockHash; uint256 blockNumber; bytes signature; } struct BridgeStorage { mapping(bytes32 => bool) processedWithdrawalIds; bool depositEnabled; bool withdrawEnabled; address adminAddress; address signerAddress; address dumbContractAddress; uint256 cancelBlockNumber; mapping(address => bytes32[]) pendingDeposits; mapping(bytes32 => bool) processedDepositIds; uint256 withdrawDelay; ControlledBridge controlledBridge; mapping(address => bytes32) pendingDepositHash; mapping(bytes32 => mapping(bytes32 => bool)) processedWithdrawalsPerDeposit; uint16 bridgeInLimit; } function s() internal pure returns (BridgeStorage storage cs) { bytes32 position = keccak256("BridgeStorage.contract.storage.v1"); assembly { cs.slot := position } } modifier onlyAdmin() { require(msg.sender == s().adminAddress, "Not admin"); _; } function initialize( address adminAddress ) external { require(msg.sender == ERC1967FactoryConstants.ADDRESS, "Not factory"); require(s().adminAddress == address(0), "Already initialized"); require(adminAddress != address(0), "No zero address"); s().adminAddress = adminAddress; } function withdraw( WithdrawRequest calldata req ) external { require(s().withdrawEnabled, "Withdraw not enabled"); bytes32 hashedMessage = _hashTypedData(keccak256(abi.encode( keccak256( "Withdraw(address recipient,address dumbContract,bytes32 ethscriptionIds," "bytes32 depositIds,bytes32 withdrawalId,bytes32 blockHash,uint256 blockNumber)" ), req.recipient, s().dumbContractAddress, keccak256(abi.encodePacked(req.ethscriptionIds)), keccak256(abi.encodePacked(req.depositIds)), req.withdrawalId, req.blockHash, req.blockNumber ))); address signer = hashedMessage.recoverCalldata(req.signature); require(signer == s().signerAddress, "Invalid signature"); require(!s().processedWithdrawalIds[req.withdrawalId], "Already processed"); require(s().cancelBlockNumber <= req.blockNumber, "Signature canceled"); require(block.number >= req.blockNumber + s().withdrawDelay, "Withdraw delay"); require(req.blockHash == bytes32(0) || blockhash(req.blockNumber) == req.blockHash, "Invalid block number or hash"); require(req.ethscriptionIds.length > 0, "No ethscriptions"); require(req.ethscriptionIds.length == req.depositIds.length, "Length mismatch"); s().processedWithdrawalIds[req.withdrawalId] = true; for (uint256 i; i < req.ethscriptionIds.length;) { bytes32 ethscriptionId = req.ethscriptionIds[i]; bytes32 depositId = req.depositIds[i]; require(!s().processedWithdrawalsPerDeposit[ethscriptionId][depositId], "Already processed"); s().processedWithdrawalsPerDeposit[ethscriptionId][depositId] = true; emit ethscriptions_protocol_TransferEthscription( req.recipient, ethscriptionId ); unchecked { ++i; } } string memory out = string.concat( 'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"', s().dumbContractAddress.toHexString(), '","function":"markWithdrawalComplete","args":{"to":"', req.recipient.toHexString(), '","withdrawalId":"', uint256(req.withdrawalId).toHexString(32), '"}}}' ); emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, string(out)); } function confirmDeposit( DepositConfirmation calldata deposit ) external { require(s().depositEnabled, "Deposit not enabled"); bytes32 hashedMessage = _hashTypedData(keccak256(abi.encode( keccak256( "DepositConfirmation(address sender,address dumbContract,bytes ethscriptionIds," "bytes32 depositId,bytes32 blockHash,uint256 blockNumber)" ), deposit.sender, s().dumbContractAddress, keccak256(deposit.ethscriptionIds), deposit.depositId, deposit.blockHash, deposit.blockNumber ))); address signer = hashedMessage.recoverCalldata(deposit.signature); require(signer == s().signerAddress, "Invalid signature"); require(!s().processedDepositIds[deposit.depositId], "Already processed"); require(s().cancelBlockNumber <= deposit.blockNumber, "Signature canceled"); require( deposit.blockHash == bytes32(0) || blockhash(deposit.blockNumber) == deposit.blockHash, "Invalid block number or hash" ); require(deposit.ethscriptionIds.length % 32 == 0 && deposit.ethscriptionIds.length > 0, "Invalid ethscriptions"); require( s().pendingDepositHash[deposit.sender] == keccak256(deposit.ethscriptionIds), "Invalid deposits" ); s().processedDepositIds[deposit.depositId] = true; delete s().pendingDepositHash[deposit.sender]; uint256 bridgeInCount = deposit.ethscriptionIds.length / 32; bytes memory out = abi.encodePacked( 'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"', s().dumbContractAddress.toHexString(), '","function":"bridgeIn","args":{"to":"', deposit.sender.toHexString(), '", "amount":"', bridgeInCount.toString(), '"}}}' ); emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, string(out)); } function adminConfirmLegacyDeposit( address recipient, bytes32 depositId ) external onlyAdmin { require(s().pendingDeposits[recipient].length > 0, "No pending deposits"); require(!s().processedDepositIds[depositId], "Already processed"); uint256 bridgeInCount = s().pendingDeposits[recipient].length; delete s().pendingDeposits[recipient]; s().processedDepositIds[depositId] = true; bytes memory out = abi.encodePacked( 'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"', s().dumbContractAddress.toHexString(), '","function":"bridgeIn","args":{"to":"', recipient.toHexString(), '", "amount":"', bridgeInCount.toString(), '"}}}' ); emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, string(out)); } function setControlledBridge(ControlledBridge controlledBridge) external onlyAdmin { s().controlledBridge = controlledBridge; } function setWithdrawEnabled(bool enabled) external onlyAdmin { s().withdrawEnabled = enabled; } function setDepositEnabled(bool enabled) external onlyAdmin { s().depositEnabled = enabled; } function setBridgeInLimit(uint16 bridgeInLimit) external onlyAdmin { s().bridgeInLimit = bridgeInLimit; } function enableAllFeatures() external onlyAdmin { s().depositEnabled = true; s().withdrawEnabled = true; } function disableAllFeatures() external onlyAdmin { s().depositEnabled = false; s().withdrawEnabled = false; } function cancelSignatures() external onlyAdmin { s().cancelBlockNumber = block.number; } function setDumbContract(address dumbContract) external onlyAdmin { s().dumbContractAddress = dumbContract; } function setSigner(address signer) external onlyAdmin { s().signerAddress = signer; } function setAdmin(address admin) external onlyAdmin { s().adminAddress = admin; } function setWithdrawDelay(uint256 withdrawDelay) external onlyAdmin { s().withdrawDelay = withdrawDelay; } function legacyPendingDeposits(address sender) external view returns (bytes32[] memory) { return s().pendingDeposits[sender]; } fallback() external { require(msg.data.length % 32 == 0 && msg.data.length > 0, "Invalid concatenated hashes length"); require(s().bridgeInLimit > 0, "Bridge in limit not set"); require(msg.data.length / 32 <= s().bridgeInLimit, "Too many ethscriptions"); require(s().depositEnabled, "Deposit not enabled"); require(s().pendingDepositHash[msg.sender] == bytes32(0), "Existing pending deposit"); require(s().pendingDeposits[msg.sender].length == 0, "Existing pending deposit"); s().pendingDepositHash[msg.sender] = keccak256(msg.data); } function getPendingDepositHash(address sender) external view returns (bytes32) { return s().pendingDepositHash[sender]; } function getProcessedWithdrawalsPerDeposit(bytes32 ethscriptionId, bytes32 depositId) external view returns (bool) { return s().processedWithdrawalsPerDeposit[ethscriptionId][depositId]; } function getSigner() external view returns (address) { return s().signerAddress; } function getAdmin() external view returns (address) { return s().adminAddress; } function getWithdrawDelay() external view returns (uint256) { return s().withdrawDelay; } function getDumbContract() external view returns (address) { return s().dumbContractAddress; } function getCancelBlockNumber() external view returns (uint256) { return s().cancelBlockNumber; } function getDepositEnabled() external view returns (bool) { return s().depositEnabled; } function getBridgeInLimit() external view returns (uint16) { return s().bridgeInLimit; } function getWithdrawalEnabled() external view returns (bool) { return s().depositEnabled; } function processedWithdraws(bytes32 withdrawalId) external view returns (bool) { return s().processedWithdrawalIds[withdrawalId]; } function processedDepositIds(bytes32 depositId) external view returns (bool) { return s().processedDepositIds[depositId]; } function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { name = "Facet Ethscription ERC20 Bridge"; version = "4"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice The address and bytecode of the canonical ERC1967Factory deployment. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ERC1967FactoryLib.sol) /// @author jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy) /// /// @dev The canonical ERC1967Factory is deployed permissionlessly via /// 0age's ImmutableCreate2Factory located at 0x0000000000FFe8B47B3e2130213B802212439497. /// /// `ADDRESS = immutableCreate2Factory.safeCreate2(SALT, INITCODE)` /// /// If the canonical ERC1967Factory has not been deployed on your EVM chain of choice, /// please feel free to deploy via 0age's ImmutableCreate2Factory. /// /// If 0age's ImmutableCreate2Factory has not been deployed on your EVM chain of choice, /// please refer to 0age's ImmutableCreate2Factory deployment instructions at: /// https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md /// /// Contract verification: /// - Source code: /// https://github.com/Vectorized/solady/blob/5212e50fef1f2ff1b1b5e03a5d276a0d23c02713/src/utils/ERC1967Factory.sol /// (The EXACT source code is required. Use the file at the commit instead of the latest copy.) /// - Optimization Enabled: Yes with 1000000 runs /// - Compiler Version: v0.8.19+commit.7dd6d404 /// - Other Settings: default evmVersion, MIT license library ERC1967FactoryConstants { /// @dev The canonical ERC1967Factory address for EVM chains. address internal constant ADDRESS = 0x0000000000006396FF2a80c067f99B3d2Ab4Df24; /// @dev The canonical ERC1967Factory bytecode for EVM chains. /// Useful for forge tests: /// `vm.etch(ADDRESS, BYTECODE)`. bytes internal constant BYTECODE = hex"6080604052600436106100b15760003560e01c8063545e7c611161006957806399a88ec41161004e57806399a88ec41461019d578063a97b90d5146101b0578063db4c545e146101c357600080fd5b8063545e7c61146101775780639623609d1461018a57600080fd5b80633729f9221161009a5780633729f922146101315780634314f120146101445780635414dff01461015757600080fd5b80631acfd02a146100b65780632abbef15146100d8575b600080fd5b3480156100c257600080fd5b506100d66100d1366004610604565b6101e6565b005b3480156100e457600080fd5b506101076100f3366004610637565b30600c908152600091909152602090205490565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010761013f366004610652565b610237565b6101076101523660046106d7565b61024e565b34801561016357600080fd5b50610107610172366004610738565b610267565b610107610185366004610604565b61029a565b6100d66101983660046106d7565b6102af565b6100d66101ab366004610604565b61035f565b6101076101be366004610751565b610370565b3480156101cf57600080fd5b506101d86103a9565b604051908152602001610128565b30600c52816000526020600c2033815414610209576382b429006000526004601cfd5b81905580827f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f600080a35050565b60006102468484843685610370565b949350505050565b600061025e8585838087876103c2565b95945050505050565b6000806102726103a9565b905060ff600053806035523060601b6001528260155260556000209150600060355250919050565b60006102a88383368461024e565b9392505050565b30600c5283600052336020600c2054146102d1576382b429006000526004601cfd5b6040518381527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602082015281836040830137600080836040018334895af1610331573d610327576355299b496000526004601cfd5b3d6000803e3d6000fd5b5082847f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c7600080a350505050565b61036c82823660006102af565b5050565b60008360601c33148460601c151761039057632f6348366000526004601cfd5b61039f868686600187876103c2565b9695505050505050565b6000806103b461049c565b608960139091012092915050565b6000806103cd61049c565b90508480156103e757866089601384016000f592506103f3565b6089601383016000f092505b50816104075763301164256000526004601cfd5b8781527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602082015282846040830137600080846040018334865af161045a573d6103275763301164256000526004601cfd5b30600c5281600052866020600c20558688837fc95935a66d15e0da5e412aca0ad27ae891d20b2fb91cf3994b6a3bf2b8178082600080a4509695505050505050565b6040513060701c801561054257666052573d6000fd607b8301527f3d356020355560408036111560525736038060403d373d3d355af43d6000803e60748301527f3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b60548301527f14605757363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc60348301523060148301526c607f3d8160093d39f33d3d337382525090565b66604c573d6000fd60758301527f3d3560203555604080361115604c5736038060403d373d3d355af43d6000803e606e8301527f3735a920a3ca505d382bbc545af43d6000803e604c573d6000fd5b3d6000f35b604e8301527f14605157363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc602e83015230600e8301526c60793d8160093d39f33d3d336d82525090565b803573ffffffffffffffffffffffffffffffffffffffff811681146105ff57600080fd5b919050565b6000806040838503121561061757600080fd5b610620836105db565b915061062e602084016105db565b90509250929050565b60006020828403121561064957600080fd5b6102a8826105db565b60008060006060848603121561066757600080fd5b610670846105db565b925061067e602085016105db565b9150604084013590509250925092565b60008083601f8401126106a057600080fd5b50813567ffffffffffffffff8111156106b857600080fd5b6020830191508360208285010111156106d057600080fd5b9250929050565b600080600080606085870312156106ed57600080fd5b6106f6856105db565b9350610704602086016105db565b9250604085013567ffffffffffffffff81111561072057600080fd5b61072c8782880161068e565b95989497509550505050565b60006020828403121561074a57600080fd5b5035919050565b60008060008060006080868803121561076957600080fd5b610772866105db565b9450610780602087016105db565b935060408601359250606086013567ffffffffffffffff8111156107a357600080fd5b6107af8882890161068e565b96999598509396509294939250505056fea26469706673582212200ac7c3ccbc2d311c48bf5465b021542e0e306fe3c462c060ba6a3d2f81ff6c5f64736f6c63430008130033"; /// @dev The initcode used to deploy the canonical ERC1967Factory. bytes internal constant INITCODE = abi.encodePacked( hex"608060405234801561001057600080fd5b506107f6806100206000396000f3fe", BYTECODE ); /// @dev For deterministic deployment via 0age's ImmutableCreate2Factory. bytes32 internal constant SALT = 0x0000000000000000000000000000000000000000e75e4f228818c80007508f33; }
// 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) /// /// Note: /// For performance and bytecode compactness, most of the string operations are restricted to /// byte strings (7-bit ASCII), except where otherwise specified. /// Usage of byte string operations on charsets with runes spanning two or more bytes /// can lead to undefined behavior. library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The length of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// @dev The length of the string is more than 32 bytes. error TooBigForSmallString(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly 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. str := add(mload(0x40), 0x80) // Update the free memory pointer to allocate. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `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) 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) } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory str) { if (value >= 0) { return toString(uint256(value)); } unchecked { str = toString(~uint256(value) + 1); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let length := mload(str) // Load the string length. mstore(str, 0x2d) // Store the '-' character. str := sub(str, 1) // Move back the string pointer by a byte. mstore(str, add(length, 1)) // Update the string 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) { str = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @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` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // 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. str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) // Allocate the memory. mstore(0x40, add(str, 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 start := sub(str, add(length, length)) let w := not(1) // Tsk. 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. for {} 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(str, start)) { break } } if temp { mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`. revert(0x1c, 0x04) } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) 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) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x". /// The output excludes leading "0" from the `toHexString` output. /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. function toMinimalHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := add(mload(str), 2) // Compute the length. mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero. str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output excludes leading "0" from the `toHexStringNoPrefix` output. /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := mload(str) // Get the length. str := add(str, o) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is 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` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // 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. str := add(mload(0x40), 0x80) // Allocate the memory. mstore(0x40, add(str, 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 w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 {} { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @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) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, 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. for { let i := 0 } 1 {} { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for {} iszero(eq(raw, end)) {} { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /// @dev Returns if this string is a 7-bit ASCII string. /// (i.e. all characters codes are in [0..127]) function is7BitASCII(string memory s) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let mask := shl(7, div(not(0), 255)) result := 1 let n := mload(s) if n { let o := add(s, 0x20) let end := add(o, n) let last := mload(end) mstore(end, 0) for {} 1 {} { if and(mask, mload(o)) { result := 0 break } o := add(o, 0x20) if iszero(lt(o, end)) { break } } mstore(end, last) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, byte string operations are restricted // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets. // Usage of byte string operations on charsets with runes spanning two or more bytes // can lead to undefined behavior. /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. function replace(string memory subject, string memory search, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly 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, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) 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) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) 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. for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) let last := add(add(result, 0x20), k) // Zeroize the slot after the string. mstore(last, 0) mstore(0x40, add(last, 0x20)) // Allocate the memory. mstore(result, k) // Store the length. } } /// @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) { /// @solidity memory-safe-assembly assembly { for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { if iszero(gt(from, subjectLength)) { result := from break } result := subjectLength break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(add(search, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } if iszero(lt(searchLength, 0x20)) { 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) if iszero(lt(subject, end)) { break } } break } for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { 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) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let searchLength := mload(search) if gt(searchLength, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte 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 true if `search` is found in `subject`, false otherwise. function contains(string memory subject, string memory search) internal pure returns (bool) { return indexOf(subject, search) != NOT_FOUND; } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item 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) { /// @solidity memory-safe-assembly 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. // forgefmt: disable-next-item 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) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) if iszero(times) { break } } mstore(output, 0) // Zeroize the slot after the string. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Store the length. // Allocate the memory. mstore(0x40, add(result, add(resultLength, 0x20))) } } } /// @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) { /// @solidity memory-safe-assembly 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) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let o := and(add(resultLength, 0x1f), w) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := add(o, w) // `sub(o, 0x20)`. 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, 0x3f), w))) } } } /// @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) { /// @solidity memory-safe-assembly 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, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) 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) 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 { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) 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); /// @solidity memory-safe-assembly assembly { let w := not(0x1f) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 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. for { let o := and(add(elementLength, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. 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, 0x3f), w))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) 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) { /// @solidity memory-safe-assembly assembly { let w := not(0x1f) result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLength, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLength := mload(b) let output := add(result, aLength) // Copy `b` one word at a time, backwards. for { let o := and(add(bLength, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. 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, 0x1f), w)) } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. /// WARNING! This function is only compatible with 7-bit ASCII strings. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let length := mload(subject) if length { result := add(mload(0x40), 0x20) subject := add(subject, 1) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) let w := not(0) for { let o := length } 1 {} { o := add(o, w) let b := and(0xff, mload(add(subject, o))) mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) if iszero(o) { break } } result := mload(0x40) mstore(result, length) // Store the length. let last := add(add(result, 0x20), length) mstore(last, 0) // Zeroize the slot after the string. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } } /// @dev Returns a string from a small bytes32 string. /// `s` must be null-terminated, or behavior will be undefined. function fromSmallString(bytes32 s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let n := 0 for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'. mstore(result, n) let o := add(result, 0x20) mstore(o, s) mstore(add(o, n), 0) mstore(0x40, add(result, 0x40)) } } /// @dev Returns the small string, with all bytes after the first null byte zeroized. function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'. mstore(0x00, s) mstore(result, 0x00) result := mload(0x00) } } /// @dev Returns the string as a normalized null-terminated small string. function toSmallString(string memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(s) if iszero(lt(result, 33)) { mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`. revert(0x1c, 0x04) } result := shl(shl(3, sub(32, result)), mload(add(s, result))) } } /// @dev Returns a lowercased copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(result, c) result := add(result, 1) continue } let t := shr(248, mload(c)) mstore(result, mload(and(t, 0x1f))) result := add(result, shr(5, t)) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(result, c) result := add(result, 1) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), c) result := add(result, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(result, mload(0x19)) // "\\u00XX". result := add(result, 6) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), mload(add(c, 8))) result := add(result, 2) } if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { result = escapeJSON(s, false); } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string. function eqs(string memory a, bytes32 b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { // These should be evaluated on compile time, as far as possible. let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. let x := not(or(m, or(b, add(m, and(b, m))))) let r := shl(7, iszero(iszero(shr(128, x)))) r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) } } /// @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) { /// @solidity memory-safe-assembly 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 behavior is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly 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) { /// @solidity memory-safe-assembly 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 behavior is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly 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 { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) let retSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retSize), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, retSize) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Contract for EIP-712 typed structured data hashing and signing. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol) /// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol) /// /// @dev Note, this implementation: /// - Uses `address(this)` for the `verifyingContract` field. /// - Does NOT use the optional EIP-712 salt. /// - Does NOT use any EIP-712 extensions. /// This is for simplicity and to save gas. /// If you need to customize, please fork / modify accordingly. abstract contract EIP712 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS AND IMMUTABLES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. bytes32 internal constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; uint256 private immutable _cachedThis; uint256 private immutable _cachedChainId; bytes32 private immutable _cachedNameHash; bytes32 private immutable _cachedVersionHash; bytes32 private immutable _cachedDomainSeparator; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTRUCTOR */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Cache the hashes for cheaper runtime gas costs. /// In the case of upgradeable contracts (i.e. proxies), /// or if the chain id changes due to a hard fork, /// the domain separator will be seamlessly calculated on-the-fly. constructor() { _cachedThis = uint256(uint160(address(this))); _cachedChainId = block.chainid; string memory name; string memory version; if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion(); bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name)); bytes32 versionHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version)); _cachedNameHash = nameHash; _cachedVersionHash = versionHash; bytes32 separator; if (!_domainNameAndVersionMayChange()) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Load the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), versionHash) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) separator := keccak256(m, 0xa0) } } _cachedDomainSeparator = separator; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* FUNCTIONS TO OVERRIDE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Please override this function to return the domain name and version. /// ``` /// function _domainNameAndVersion() /// internal /// pure /// virtual /// returns (string memory name, string memory version) /// { /// name = "Solady"; /// version = "1"; /// } /// ``` /// /// Note: If the returned result may change after the contract has been deployed, /// you must override `_domainNameAndVersionMayChange()` to return true. function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version); /// @dev Returns if `_domainNameAndVersion()` may change /// after the contract has been deployed (i.e. after the constructor). /// Default: false. function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the EIP-712 domain separator. function _domainSeparator() internal view virtual returns (bytes32 separator) { if (_domainNameAndVersionMayChange()) { separator = _buildDomainSeparator(); } else { separator = _cachedDomainSeparator; if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator(); } } /// @dev Returns the hash of the fully encoded EIP-712 message for this domain, /// given `structHash`, as defined in /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct. /// /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message: /// ``` /// bytes32 digest = _hashTypedData(keccak256(abi.encode( /// keccak256("Mail(address to,string contents)"), /// mailTo, /// keccak256(bytes(mailContents)) /// ))); /// address signer = ECDSA.recover(digest, signature); /// ``` function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) { // We will use `digest` to store the domain separator to save a bit of gas. if (_domainNameAndVersionMayChange()) { digest = _buildDomainSeparator(); } else { digest = _cachedDomainSeparator; if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator(); } /// @solidity memory-safe-assembly assembly { // Compute the digest. mstore(0x00, 0x1901000000000000) // Store "\x19\x01". mstore(0x1a, digest) // Store the domain separator. mstore(0x3a, structHash) // Store the struct hash. digest := keccak256(0x18, 0x42) // Restore the part of the free memory slot that was overwritten. mstore(0x3a, 0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-5267 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev See: https://eips.ethereum.org/EIPS/eip-5267 function eip712Domain() public view virtual returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { fields = hex"0f"; // `0b01111`. (name, version) = _domainNameAndVersion(); chainId = block.chainid; verifyingContract = address(this); salt = salt; // `bytes32(0)`. extensions = extensions; // `new uint256[](0)`. } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the EIP-712 domain separator. function _buildDomainSeparator() private view returns (bytes32 separator) { // We will use `separator` to store the name hash to save a bit of gas. bytes32 versionHash; if (_domainNameAndVersionMayChange()) { (string memory name, string memory version) = _domainNameAndVersion(); separator = keccak256(bytes(name)); versionHash = keccak256(bytes(version)); } else { separator = _cachedNameHash; versionHash = _cachedVersionHash; } /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Load the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), separator) // Name hash. mstore(add(m, 0x40), versionHash) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) separator := keccak256(m, 0xa0) } } /// @dev Returns if the cached domain separator has been invalidated. function _cachedDomainSeparatorInvalidated() private view returns (bool result) { uint256 cachedChainId = _cachedChainId; uint256 cachedThis = _cachedThis; /// @solidity memory-safe-assembly assembly { result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis))) } } }
// 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) /// /// @dev Note: /// - The recovery functions use the ecrecover precompile (0x1). /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure. /// This is for more safety by default. /// Use the `tryRecover` variants if you need to get the zero address back /// upon recovery failure instead. /// - As of Solady version 0.0.134, all `bytes signature` variants accept both /// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. /// See: https://eips.ethereum.org/EIPS/eip-2098 /// This is for calldata efficiency on smart accounts prevalent on L2s. /// /// WARNING! Do NOT use signatures as unique identifiers: /// - Use a nonce in the digest to prevent replay attacks on the same contract. /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. /// EIP-712 also enables readable signing of typed data for better user safety. /// This implementation does NOT check if a signature is non-malleable. library ECDSA { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The signature is invalid. error InvalidSignature(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RECOVERY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. for {} 1 {} { mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. break } result := 0 break } result := mload( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) for {} 1 {} { if eq(signature.length, 64) { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. break } result := 0 break } result := mload( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. result := mload( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) result := mload( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* TRY-RECOVER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // WARNING! // These functions will NOT revert upon recovery failure. // Instead, they will return the zero address upon recovery failure. // It is critical that the returned address is NEVER compared against // a zero address (e.g. an uninitialized address variable). /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. for {} 1 {} { mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. break } result := 0 break } pop( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { result := 1 let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) for {} 1 {} { if eq(signature.length, 64) { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. break } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. break } result := 0 break } pop( staticcall( gas(), // Amount of gas left for the transaction. result, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. pop( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) pop( staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x40, // Start of output. 0x20 // Size of output. ) ) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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) { /// @solidity memory-safe-assembly assembly { mstore(0x20, hash) // Store into scratch space for keccak256. mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 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. /// Note: Supports lengths of `s` up to 999999 bytes. function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let sLength := mload(s) let o := 0x20 mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. mstore(0x00, 0x00) // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. for { let temp := sLength } 1 {} { o := sub(o, 1) mstore8(o, add(48, mod(temp, 10))) temp := div(temp, 10) if iszero(temp) { break } } let n := sub(0x3a, o) // Header length: `26 + 32 - o`. // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) mstore(s, sLength) // Restore the length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes. function emptySignature() internal pure returns (bytes calldata signature) { /// @solidity memory-safe-assembly assembly { signature.length := 0 } } }
{ "optimizer": { "enabled": false, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initialOwner","type":"address"},{"indexed":false,"internalType":"string","name":"contentURI","type":"string"}],"name":"ethscriptions_protocol_CreateEthscription","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"ethscriptions_protocol_TransferEthscription","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes32","name":"depositId","type":"bytes32"}],"name":"adminConfirmLegacyDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelSignatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes","name":"ethscriptionIds","type":"bytes"},{"internalType":"bytes32","name":"depositId","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct FacetEthscriptionBridgeV4.DepositConfirmation","name":"deposit","type":"tuple"}],"name":"confirmDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableAllFeatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableAllFeatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBridgeInLimit","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancelBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDepositEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDumbContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"getPendingDepositHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"ethscriptionId","type":"bytes32"},{"internalType":"bytes32","name":"depositId","type":"bytes32"}],"name":"getProcessedWithdrawalsPerDeposit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawalEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"adminAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"legacyPendingDeposits","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"depositId","type":"bytes32"}],"name":"processedDepositIds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"withdrawalId","type":"bytes32"}],"name":"processedWithdraws","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"bridgeInLimit","type":"uint16"}],"name":"setBridgeInLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ControlledBridge","name":"controlledBridge","type":"address"}],"name":"setControlledBridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setDepositEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"dumbContract","type":"address"}],"name":"setDumbContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"withdrawDelay","type":"uint256"}],"name":"setWithdrawDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setWithdrawEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes32[]","name":"ethscriptionIds","type":"bytes32[]"},{"internalType":"bytes32[]","name":"depositIds","type":"bytes32[]"},{"internalType":"bytes32","name":"withdrawalId","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct FacetEthscriptionBridgeV4.WithdrawRequest","name":"req","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101206040523480156200001257600080fd5b503073ffffffffffffffffffffffffffffffffffffffff16608081815250504660a081815250506060806200004c6200014360201b60201c565b6200006a57620000616200014860201b60201c565b80925081935050505b60006200007c6200014360201b60201c565b6200008f57828051906020012062000094565b6000801b5b90506000620000a86200014360201b60201c565b620000bb578280519060200120620000c0565b6000801b5b90508160c081815250508060e081815250506000620000e46200014360201b60201c565b6200012f576040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815283602082015282604082015246606082015230608082015260a081209150505b8061010081815250505050505050620001bf565b600090565b6060806040518060400160405280601f81526020017f466163657420457468736372697074696f6e204552433230204272696467650081525091506040518060400160405280600181526020017f340000000000000000000000000000000000000000000000000000000000000081525090509091565b60805160a05160c05160e0516101005161434b620002046000396000612704015260006129720152600061294f015260006129e201526000612a07015261434b6000f3fe608060405234801561001057600080fd5b50600436106101d35760003560e01c80637beeaded11610104578063c4ad74f3116100a2578063e009795311610071578063e0097953146107db578063e1f60f541461080b578063e219b38d14610815578063fe3300d014610833576101d4565b8063c4ad74f314610757578063c4d66de814610773578063cf55f8a81461078f578063d263c409146107ab576101d4565b80638a788883116100de5780638a788883146106f75780639bd27167146107135780639ec004a214610731578063c38450791461074d576101d4565b80637beeaded1461068557806384b0196e146106a357806386046f58146106c7576101d4565b806360100da6116101715780636e9960c31161014b5780636e9960c314610611578063704b6c021461062f57806372f0cb301461064b5780637ac3c02f14610667576101d4565b806360100da6146105bb5780636ada7847146105d75780636c19e783146105f5576101d4565b806341b8dc5b116101ad57806341b8dc5b1461054957806351466d8d146105655780635b0d8879146105955780635b17d04b1461059f576101d4565b8063069b09e3146104df57806321bec14a146104fd578063294f3bbc1461052d576101d4565b5b6000602060003690506101e79190612bc7565b1480156101f75750600080369050115b610236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161022d90612c7b565b60405180910390fd5b6000610240610851565b600b0160009054906101000a900461ffff1661ffff1611610296576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161028d90612ce7565b60405180910390fd5b61029e610851565b600b0160009054906101000a900461ffff1661ffff16602060003690506102c59190612d36565b1115610306576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fd90612db3565b60405180910390fd5b61030e610851565b60010160009054906101000a900460ff1661035e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035590612e1f565b60405180910390fd5b6000801b61036a610851565b60090160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146103eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103e290612e8b565b60405180910390fd5b60006103f5610851565b60050160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905014610479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161047090612e8b565b60405180910390fd5b60003660405161048a929190612eea565b604051809103902061049a610851565b60090160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055005b6104e761087e565b6040516104f49190612f44565b60405180910390f35b61051760048036038101906105129190612f9f565b6108b1565b6040516105249190612fe7565b60405180910390f35b61054760048036038101906105429190613026565b6108e4565b005b610563600480360381019061055e919061309b565b610edd565b005b61057f600480360381019061057a91906130db565b611235565b60405161058c9190612fe7565b60405180910390f35b61059d61127a565b005b6105b960048036038101906105b49190613147565b611325565b005b6105d560048036038101906105d09190613193565b6113e4565b005b6105df611b27565b6040516105ec9190612fe7565b60405180910390f35b61060f600480360381019061060a91906131dc565b611b47565b005b610619611c2d565b6040516106269190612f44565b60405180910390f35b610649600480360381019061064491906131dc565b611c60565b005b61066560048036038101906106609190613235565b611d46565b005b61066f611df2565b60405161067c9190612f44565b60405180910390f35b61068d611e25565b60405161069a9190613271565b60405180910390f35b6106ab611e38565b6040516106be9796959493929190613413565b60405180910390f35b6106e160048036038101906106dc91906131dc565b611e86565b6040516106ee9190613555565b60405180910390f35b610711600480360381019061070c91906131dc565b611f26565b005b61071b61200c565b6040516107289190612fe7565b60405180910390f35b61074b60048036038101906107469190613147565b61202c565b005b6107556120eb565b005b610771600480360381019061076c91906135b1565b6121ce565b005b61078d600480360381019061078891906131dc565b612290565b005b6107a960048036038101906107a4919061361c565b612462565b005b6107c560048036038101906107c091906131dc565b612548565b6040516107d29190613649565b60405180910390f35b6107f560048036038101906107f09190612f9f565b61259a565b6040516108029190612fe7565b60405180910390f35b6108136125cd565b005b61081d6126b0565b60405161082a9190613673565b60405180910390f35b61083b6126d1565b6040516108489190613271565b60405180910390f35b6000807fbff2a7c862c2783f9ac585842eecebad2c8066f996e6582444b473e4ddf79b5990508091505090565b6000610888610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006108bb610851565b600001600083815260200190815260200160002060009054906101000a900460ff169050919050565b6108ec610851565b60010160009054906101000a900460ff1661093c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161093390612e1f565b60405180910390fd5b6000610a077f412cbf007be5b788a80c6ad2774faa9b1fb7684bf27c6f6e252f1c9d995002fd83600001602081019061097591906131dc565b61097d610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168580602001906109b1919061369d565b6040516109bf929190612eea565b60405180910390208660400135876060013588608001356040516020016109ec9796959493929190613700565b604051602081830303815290604052805190602001206126e4565b90506000610a2e838060a00190610a1e919061369d565b846127639092919063ffffffff16565b9050610a38610851565b60020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610ac9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ac0906137bb565b60405180910390fd5b610ad1610851565b60060160008460400135815260200190815260200160002060009054906101000a900460ff1615610b37576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b2e90613827565b60405180910390fd5b8260800135610b44610851565b600401541115610b89576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b8090613893565b60405180910390fd5b6000801b83606001351480610ba657508260600135836080013540145b610be5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bdc906138ff565b60405180910390fd5b60006020848060200190610bf9919061369d565b9050610c059190612bc7565b148015610c2357506000838060200190610c1f919061369d565b9050115b610c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c599061396b565b60405180910390fd5b828060200190610c72919061369d565b604051610c80929190612eea565b6040518091039020610c90610851565b6009016000856000016020810190610ca891906131dc565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610d23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d1a906139d7565b60405180910390fd5b6001610d2d610851565b60060160008560400135815260200190815260200160002060006101000a81548160ff021916908315150217905550610d64610851565b6009016000846000016020810190610d7c91906131dc565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000905560006020848060200190610dce919061369d565b9050610dda9190612d36565b90506000610e28610de9610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166127f8565b610e59866000016020810190610e3e91906131dc565b73ffffffffffffffffffffffffffffffffffffffff166127f8565b610e628461281e565b604051602001610e7493929190613bd5565b6040516020818303038152906040529050620face773ffffffffffffffffffffffffffffffffffffffff167f665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c82604051610ece9190613c32565b60405180910390a25050505050565b610ee5610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f76576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f6d90613ca0565b60405180910390fd5b6000610f80610851565b60050160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905011611004576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ffb90613d0c565b60405180910390fd5b61100c610851565b600601600082815260200190815260200160002060009054906101000a900460ff161561106e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161106590613827565b60405180910390fd5b6000611078610851565b60050160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905090506110c6610851565b60050160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006111129190612b50565b600161111c610851565b600601600084815260200190815260200160002060006101000a81548160ff0219169083151502179055506000611193611154610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166127f8565b6111b28573ffffffffffffffffffffffffffffffffffffffff166127f8565b6111bb8461281e565b6040516020016111cd93929190613bd5565b6040516020818303038152906040529050620face773ffffffffffffffffffffffffffffffffffffffff167f665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c826040516112279190613c32565b60405180910390a250505050565b600061123f610851565b600a016000848152602001908152602001600020600083815260200190815260200160002060009054906101000a900460ff16905092915050565b611282610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611313576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161130a90613ca0565b60405180910390fd5b4361131c610851565b60040181905550565b61132d610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113b590613ca0565b60405180910390fd5b806113c7610851565b60010160006101000a81548160ff02191690831515021790555050565b6113ec610851565b60010160019054906101000a900460ff1661143c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161143390613d78565b60405180910390fd5b60006115507f14e6af5e76748cb9ada4517dadbe966d51ffe80c1b4291aff27b97579f73e50c83600001602081019061147591906131dc565b61147d610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168580602001906114b19190613d98565b6040516020016114c2929190613e70565b604051602081830303815290604052805190602001208680604001906114e89190613d98565b6040516020016114f9929190613e70565b60405160208183030381529060405280519060200120876060013588608001358960a00135604051602001611535989796959493929190613e89565b604051602081830303815290604052805190602001206126e4565b90506000611577838060c00190611567919061369d565b846127639092919063ffffffff16565b9050611581610851565b60020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611612576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611609906137bb565b60405180910390fd5b61161a610851565b60000160008460600135815260200190815260200160002060009054906101000a900460ff1615611680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161167790613827565b60405180910390fd5b8260a0013561168d610851565b6004015411156116d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116c990613893565b60405180910390fd5b6116da610851565b600701548360a001356116ed9190613f07565b43101561172f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161172690613f87565b60405180910390fd5b6000801b8360800135148061174c575082608001358360a0013540145b61178b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611782906138ff565b60405180910390fd5b600083806020019061179d9190613d98565b9050116117df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117d690613ff3565b60405180910390fd5b8280604001906117ef9190613d98565b90508380602001906118019190613d98565b905014611843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183a9061405f565b60405180910390fd5b600161184d610851565b60000160008560600135815260200190815260200160002060006101000a81548160ff02191690831515021790555060005b83806020019061188f9190613d98565b9050811015611a145760008480602001906118aa9190613d98565b838181106118bb576118ba61407f565b5b90506020020135905060008580604001906118d69190613d98565b848181106118e7576118e661407f565b5b9050602002013590506118f8610851565b600a016000838152602001908152602001600020600082815260200190815260200160002060009054906101000a900460ff161561196b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161196290613827565b60405180910390fd5b6001611975610851565b600a016000848152602001908152602001600020600083815260200190815260200160002060006101000a81548160ff021916908315150217905550818660000160208101906119c591906131dc565b73ffffffffffffffffffffffffffffffffffffffff167ff30861289185032f511ff94a8127e470f3d0e6230be4925cb6fad33f3436dffb60405160405180910390a3826001019250505061187f565b506000611a61611a22610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166127f8565b611a92856000016020810190611a7791906131dc565b73ffffffffffffffffffffffffffffffffffffffff166127f8565b611aad6020876060013560001c61286f90919063ffffffff16565b604051602001611abf9392919061416c565b6040516020818303038152906040529050620face773ffffffffffffffffffffffffffffffffffffffff167f665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c82604051611b199190613c32565b60405180910390a250505050565b6000611b31610851565b60010160009054906101000a900460ff16905090565b611b4f610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611be0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bd790613ca0565b60405180910390fd5b80611be9610851565b60020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000611c37610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b611c68610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cf9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cf090613ca0565b60405180910390fd5b80611d02610851565b60010160026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b611d4e610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ddf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dd690613ca0565b60405180910390fd5b80611de8610851565b6007018190555050565b6000611dfc610851565b60020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000611e2f610851565b60040154905090565b6000606080600080600060607f0f000000000000000000000000000000000000000000000000000000000000009650611e6f612897565b809650819750505046935030925090919293949596565b6060611e90610851565b60050160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015611f1a57602002820191906000526020600020905b815481526020019060010190808311611f06575b50505050509050919050565b611f2e610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611fbf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fb690613ca0565b60405180910390fd5b80611fc8610851565b60030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000612016610851565b60010160009054906101000a900460ff16905090565b612034610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120bc90613ca0565b60405180910390fd5b806120ce610851565b60010160016101000a81548160ff02191690831515021790555050565b6120f3610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612184576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161217b90613ca0565b60405180910390fd5b600161218e610851565b60010160006101000a81548160ff02191690831515021790555060016121b2610851565b60010160016101000a81548160ff021916908315150217905550565b6121d6610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225e90613ca0565b60405180910390fd5b80612270610851565b600b0160006101000a81548161ffff021916908361ffff16021790555050565b6d6396ff2a80c067f99b3d2ab4df2473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461230c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016123039061421d565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1661232c610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146123a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161239d90614289565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612415576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161240c906142f5565b60405180910390fd5b8061241e610851565b60010160026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61246a610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146124fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124f290613ca0565b60405180910390fd5b80612504610851565b60080160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000612552610851565b60090160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006125a4610851565b600601600083815260200190815260200160002060009054906101000a900460ff169050919050565b6125d5610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161265d90613ca0565b60405180910390fd5b6000612670610851565b60010160006101000a81548160ff0219169083151502179055506000612694610851565b60010160016101000a81548160ff021916908315150217905550565b60006126ba610851565b600b0160009054906101000a900461ffff16905090565b60006126db610851565b60070154905090565b60006126ee61290e565b15612702576126fb612913565b905061273e565b7f0000000000000000000000000000000000000000000000000000000000000000905061272d6129dd565b1561273d5761273a612913565b90505b5b67190100000000000060005280601a5281603a52604260182090506000603a52919050565b600060019050604051846000526001156127c657604083036127a3576020840135601b8160ff1c0160205284356040528060011b60011c606052506127c6565b604183036127c157604084013560001a6020526040846040376127c6565b600091505b6020600160806000855afa5191503d6127e757638baa579f6000526004601cfd5b600060605280604052509392505050565b606061280382612a37565b90506002815101613078825260028203915080825250919050565b60606080604051019050602081016040526000815280600019835b60011561285a578184019350600a81066030018453600a8104905080612839575b50828203602084039350808452505050919050565b606061287b8383612aba565b9050600281510161307882526002820391508082525092915050565b6060806040518060400160405280601f81526020017f466163657420457468736372697074696f6e204552433230204272696467650081525091506040518060400160405280600181526020017f340000000000000000000000000000000000000000000000000000000000000081525090509091565b600090565b60008061291e61290e565b1561294d5760008061292e612897565b9150915081805190602001209350808051906020012092505050612994565b7f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090505b6040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815282602082015281604082015246606082015230608082015260a081209250505090565b6000807f0000000000000000000000000000000000000000000000000000000000000000905060007f0000000000000000000000000000000000000000000000000000000000000000905080301482461416159250505090565b60606040519050608081016040526f30313233343536373839616263646566600f526002810190506028815260208101600060288201528260601b925060005b600115612ab357808101820184821a600f81165160018301538060041c51825360018301925060148303612aac575050612ab3565b5050612a77565b5050919050565b6060601f1960428360011b01166040510190506020810160405260008152806f30313233343536373839616263646566600f528283018203600119855b600115612b25578185019450600f8116516001860153600f8160041c165185538060081c9050828518612af7575b8015612b3957632194895a6000526004601cfd5b848403602086039550808652505050505092915050565b5080546000825590600052602060002090810190612b6e9190612b71565b50565b5b80821115612b8a576000816000905550600101612b72565b5090565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000612bd282612b8e565b9150612bdd83612b8e565b925082612bed57612bec612b98565b5b828206905092915050565b600082825260208201905092915050565b7f496e76616c696420636f6e636174656e6174656420686173686573206c656e6760008201527f7468000000000000000000000000000000000000000000000000000000000000602082015250565b6000612c65602283612bf8565b9150612c7082612c09565b604082019050919050565b60006020820190508181036000830152612c9481612c58565b9050919050565b7f42726964676520696e206c696d6974206e6f7420736574000000000000000000600082015250565b6000612cd1601783612bf8565b9150612cdc82612c9b565b602082019050919050565b60006020820190508181036000830152612d0081612cc4565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000612d4182612b8e565b9150612d4c83612b8e565b925082612d5c57612d5b612b98565b5b828204905092915050565b7f546f6f206d616e7920657468736372697074696f6e7300000000000000000000600082015250565b6000612d9d601683612bf8565b9150612da882612d67565b602082019050919050565b60006020820190508181036000830152612dcc81612d90565b9050919050565b7f4465706f736974206e6f7420656e61626c656400000000000000000000000000600082015250565b6000612e09601383612bf8565b9150612e1482612dd3565b602082019050919050565b60006020820190508181036000830152612e3881612dfc565b9050919050565b7f4578697374696e672070656e64696e67206465706f7369740000000000000000600082015250565b6000612e75601883612bf8565b9150612e8082612e3f565b602082019050919050565b60006020820190508181036000830152612ea481612e68565b9050919050565b600081905092915050565b82818337600083830152505050565b6000612ed18385612eab565b9350612ede838584612eb6565b82840190509392505050565b6000612ef7828486612ec5565b91508190509392505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000612f2e82612f03565b9050919050565b612f3e81612f23565b82525050565b6000602082019050612f596000830184612f35565b92915050565b600080fd5b600080fd5b6000819050919050565b612f7c81612f69565b8114612f8757600080fd5b50565b600081359050612f9981612f73565b92915050565b600060208284031215612fb557612fb4612f5f565b5b6000612fc384828501612f8a565b91505092915050565b60008115159050919050565b612fe181612fcc565b82525050565b6000602082019050612ffc6000830184612fd8565b92915050565b600080fd5b600060c0828403121561301d5761301c613002565b5b81905092915050565b60006020828403121561303c5761303b612f5f565b5b600082013567ffffffffffffffff81111561305a57613059612f64565b5b61306684828501613007565b91505092915050565b61307881612f23565b811461308357600080fd5b50565b6000813590506130958161306f565b92915050565b600080604083850312156130b2576130b1612f5f565b5b60006130c085828601613086565b92505060206130d185828601612f8a565b9150509250929050565b600080604083850312156130f2576130f1612f5f565b5b600061310085828601612f8a565b925050602061311185828601612f8a565b9150509250929050565b61312481612fcc565b811461312f57600080fd5b50565b6000813590506131418161311b565b92915050565b60006020828403121561315d5761315c612f5f565b5b600061316b84828501613132565b91505092915050565b600060e0828403121561318a57613189613002565b5b81905092915050565b6000602082840312156131a9576131a8612f5f565b5b600082013567ffffffffffffffff8111156131c7576131c6612f64565b5b6131d384828501613174565b91505092915050565b6000602082840312156131f2576131f1612f5f565b5b600061320084828501613086565b91505092915050565b61321281612b8e565b811461321d57600080fd5b50565b60008135905061322f81613209565b92915050565b60006020828403121561324b5761324a612f5f565b5b600061325984828501613220565b91505092915050565b61326b81612b8e565b82525050565b60006020820190506132866000830184613262565b92915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b6132c18161328c565b82525050565b600081519050919050565b60005b838110156132f05780820151818401526020810190506132d5565b60008484015250505050565b6000601f19601f8301169050919050565b6000613318826132c7565b6133228185612bf8565b93506133328185602086016132d2565b61333b816132fc565b840191505092915050565b61334f81612f69565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61338a81612b8e565b82525050565b600061339c8383613381565b60208301905092915050565b6000602082019050919050565b60006133c082613355565b6133ca8185613360565b93506133d583613371565b8060005b838110156134065781516133ed8882613390565b97506133f8836133a8565b9250506001810190506133d9565b5085935050505092915050565b600060e082019050613428600083018a6132b8565b818103602083015261343a818961330d565b9050818103604083015261344e818861330d565b905061345d6060830187613262565b61346a6080830186612f35565b61347760a0830185613346565b81810360c083015261348981846133b5565b905098975050505050505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6134cc81612f69565b82525050565b60006134de83836134c3565b60208301905092915050565b6000602082019050919050565b600061350282613497565b61350c81856134a2565b9350613517836134b3565b8060005b8381101561354857815161352f88826134d2565b975061353a836134ea565b92505060018101905061351b565b5085935050505092915050565b6000602082019050818103600083015261356f81846134f7565b905092915050565b600061ffff82169050919050565b61358e81613577565b811461359957600080fd5b50565b6000813590506135ab81613585565b92915050565b6000602082840312156135c7576135c6612f5f565b5b60006135d58482850161359c565b91505092915050565b60006135e982612f23565b9050919050565b6135f9816135de565b811461360457600080fd5b50565b600081359050613616816135f0565b92915050565b60006020828403121561363257613631612f5f565b5b600061364084828501613607565b91505092915050565b600060208201905061365e6000830184613346565b92915050565b61366d81613577565b82525050565b60006020820190506136886000830184613664565b92915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126136ba576136b961368e565b5b80840192508235915067ffffffffffffffff8211156136dc576136db613693565b5b6020830192506001820236038313156136f8576136f7613698565b5b509250929050565b600060e082019050613715600083018a613346565b6137226020830189612f35565b61372f6040830188612f35565b61373c6060830187613346565b6137496080830186613346565b61375660a0830185613346565b61376360c0830184613262565b98975050505050505050565b7f496e76616c6964207369676e6174757265000000000000000000000000000000600082015250565b60006137a5601183612bf8565b91506137b08261376f565b602082019050919050565b600060208201905081810360008301526137d481613798565b9050919050565b7f416c72656164792070726f636573736564000000000000000000000000000000600082015250565b6000613811601183612bf8565b915061381c826137db565b602082019050919050565b6000602082019050818103600083015261384081613804565b9050919050565b7f5369676e61747572652063616e63656c65640000000000000000000000000000600082015250565b600061387d601283612bf8565b915061388882613847565b602082019050919050565b600060208201905081810360008301526138ac81613870565b9050919050565b7f496e76616c696420626c6f636b206e756d626572206f72206861736800000000600082015250565b60006138e9601c83612bf8565b91506138f4826138b3565b602082019050919050565b60006020820190508181036000830152613918816138dc565b9050919050565b7f496e76616c696420657468736372697074696f6e730000000000000000000000600082015250565b6000613955601583612bf8565b91506139608261391f565b602082019050919050565b6000602082019050818103600083015261398481613948565b9050919050565b7f496e76616c6964206465706f7369747300000000000000000000000000000000600082015250565b60006139c1601083612bf8565b91506139cc8261398b565b602082019050919050565b600060208201905081810360008301526139f0816139b4565b9050919050565b600081905092915050565b7f646174613a6170706c69636174696f6e2f766e642e66616365742e74782b6a7360008201527f6f6e3b72756c653d65736970362c7b226f70223a2263616c6c222c226461746160208201527f223a7b22746f223a220000000000000000000000000000000000000000000000604082015250565b6000613a846049836139f7565b9150613a8f82613a02565b604982019050919050565b6000613aa5826132c7565b613aaf81856139f7565b9350613abf8185602086016132d2565b80840191505092915050565b7f222c2266756e6374696f6e223a22627269646765496e222c2261726773223a7b60008201527f22746f223a220000000000000000000000000000000000000000000000000000602082015250565b6000613b276026836139f7565b9150613b3282613acb565b602682019050919050565b7f222c2022616d6f756e74223a2200000000000000000000000000000000000000600082015250565b6000613b73600d836139f7565b9150613b7e82613b3d565b600d82019050919050565b7f227d7d7d00000000000000000000000000000000000000000000000000000000600082015250565b6000613bbf6004836139f7565b9150613bca82613b89565b600482019050919050565b6000613be082613a77565b9150613bec8286613a9a565b9150613bf782613b1a565b9150613c038285613a9a565b9150613c0e82613b66565b9150613c1a8284613a9a565b9150613c2582613bb2565b9150819050949350505050565b60006020820190508181036000830152613c4c818461330d565b905092915050565b7f4e6f742061646d696e0000000000000000000000000000000000000000000000600082015250565b6000613c8a600983612bf8565b9150613c9582613c54565b602082019050919050565b60006020820190508181036000830152613cb981613c7d565b9050919050565b7f4e6f2070656e64696e67206465706f7369747300000000000000000000000000600082015250565b6000613cf6601383612bf8565b9150613d0182613cc0565b602082019050919050565b60006020820190508181036000830152613d2581613ce9565b9050919050565b7f5769746864726177206e6f7420656e61626c6564000000000000000000000000600082015250565b6000613d62601483612bf8565b9150613d6d82613d2c565b602082019050919050565b60006020820190508181036000830152613d9181613d55565b9050919050565b60008083356001602003843603038112613db557613db461368e565b5b80840192508235915067ffffffffffffffff821115613dd757613dd6613693565b5b602083019250602082023603831315613df357613df2613698565b5b509250929050565b600081905092915050565b600080fd5b82818337505050565b6000613e208385613dfb565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115613e5357613e52613e06565b5b602083029250613e64838584613e0b565b82840190509392505050565b6000613e7d828486613e14565b91508190509392505050565b600061010082019050613e9f600083018b613346565b613eac602083018a612f35565b613eb96040830189612f35565b613ec66060830188613346565b613ed36080830187613346565b613ee060a0830186613346565b613eed60c0830185613346565b613efa60e0830184613262565b9998505050505050505050565b6000613f1282612b8e565b9150613f1d83612b8e565b9250828201905080821115613f3557613f34612d07565b5b92915050565b7f57697468647261772064656c6179000000000000000000000000000000000000600082015250565b6000613f71600e83612bf8565b9150613f7c82613f3b565b602082019050919050565b60006020820190508181036000830152613fa081613f64565b9050919050565b7f4e6f20657468736372697074696f6e7300000000000000000000000000000000600082015250565b6000613fdd601083612bf8565b9150613fe882613fa7565b602082019050919050565b6000602082019050818103600083015261400c81613fd0565b9050919050565b7f4c656e677468206d69736d617463680000000000000000000000000000000000600082015250565b6000614049600f83612bf8565b915061405482614013565b602082019050919050565b600060208201905081810360008301526140788161403c565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f222c2266756e6374696f6e223a226d61726b5769746864726177616c436f6d7060008201527f6c657465222c2261726773223a7b22746f223a22000000000000000000000000602082015250565b600061410a6034836139f7565b9150614115826140ae565b603482019050919050565b7f222c227769746864726177616c4964223a220000000000000000000000000000815250565b7f227d7d7d00000000000000000000000000000000000000000000000000000000815250565b600061417782613a77565b91506141838286613a9a565b915061418e826140fd565b915061419a8285613a9a565b91506141a582614120565b6012820191506141b58284613a9a565b91506141c082614146565b600482019150819050949350505050565b7f4e6f7420666163746f7279000000000000000000000000000000000000000000600082015250565b6000614207600b83612bf8565b9150614212826141d1565b602082019050919050565b60006020820190508181036000830152614236816141fa565b9050919050565b7f416c726561647920696e697469616c697a656400000000000000000000000000600082015250565b6000614273601383612bf8565b915061427e8261423d565b602082019050919050565b600060208201905081810360008301526142a281614266565b9050919050565b7f4e6f207a65726f20616464726573730000000000000000000000000000000000600082015250565b60006142df600f83612bf8565b91506142ea826142a9565b602082019050919050565b6000602082019050818103600083015261430e816142d2565b905091905056fea264697066735822122049d136c8ac1d9f42705fe1886b42ab293896bd5b899fbc0fa196b992ca86996a64736f6c63430008110033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101d35760003560e01c80637beeaded11610104578063c4ad74f3116100a2578063e009795311610071578063e0097953146107db578063e1f60f541461080b578063e219b38d14610815578063fe3300d014610833576101d4565b8063c4ad74f314610757578063c4d66de814610773578063cf55f8a81461078f578063d263c409146107ab576101d4565b80638a788883116100de5780638a788883146106f75780639bd27167146107135780639ec004a214610731578063c38450791461074d576101d4565b80637beeaded1461068557806384b0196e146106a357806386046f58146106c7576101d4565b806360100da6116101715780636e9960c31161014b5780636e9960c314610611578063704b6c021461062f57806372f0cb301461064b5780637ac3c02f14610667576101d4565b806360100da6146105bb5780636ada7847146105d75780636c19e783146105f5576101d4565b806341b8dc5b116101ad57806341b8dc5b1461054957806351466d8d146105655780635b0d8879146105955780635b17d04b1461059f576101d4565b8063069b09e3146104df57806321bec14a146104fd578063294f3bbc1461052d576101d4565b5b6000602060003690506101e79190612bc7565b1480156101f75750600080369050115b610236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161022d90612c7b565b60405180910390fd5b6000610240610851565b600b0160009054906101000a900461ffff1661ffff1611610296576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161028d90612ce7565b60405180910390fd5b61029e610851565b600b0160009054906101000a900461ffff1661ffff16602060003690506102c59190612d36565b1115610306576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fd90612db3565b60405180910390fd5b61030e610851565b60010160009054906101000a900460ff1661035e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035590612e1f565b60405180910390fd5b6000801b61036a610851565b60090160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146103eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103e290612e8b565b60405180910390fd5b60006103f5610851565b60050160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905014610479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161047090612e8b565b60405180910390fd5b60003660405161048a929190612eea565b604051809103902061049a610851565b60090160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055005b6104e761087e565b6040516104f49190612f44565b60405180910390f35b61051760048036038101906105129190612f9f565b6108b1565b6040516105249190612fe7565b60405180910390f35b61054760048036038101906105429190613026565b6108e4565b005b610563600480360381019061055e919061309b565b610edd565b005b61057f600480360381019061057a91906130db565b611235565b60405161058c9190612fe7565b60405180910390f35b61059d61127a565b005b6105b960048036038101906105b49190613147565b611325565b005b6105d560048036038101906105d09190613193565b6113e4565b005b6105df611b27565b6040516105ec9190612fe7565b60405180910390f35b61060f600480360381019061060a91906131dc565b611b47565b005b610619611c2d565b6040516106269190612f44565b60405180910390f35b610649600480360381019061064491906131dc565b611c60565b005b61066560048036038101906106609190613235565b611d46565b005b61066f611df2565b60405161067c9190612f44565b60405180910390f35b61068d611e25565b60405161069a9190613271565b60405180910390f35b6106ab611e38565b6040516106be9796959493929190613413565b60405180910390f35b6106e160048036038101906106dc91906131dc565b611e86565b6040516106ee9190613555565b60405180910390f35b610711600480360381019061070c91906131dc565b611f26565b005b61071b61200c565b6040516107289190612fe7565b60405180910390f35b61074b60048036038101906107469190613147565b61202c565b005b6107556120eb565b005b610771600480360381019061076c91906135b1565b6121ce565b005b61078d600480360381019061078891906131dc565b612290565b005b6107a960048036038101906107a4919061361c565b612462565b005b6107c560048036038101906107c091906131dc565b612548565b6040516107d29190613649565b60405180910390f35b6107f560048036038101906107f09190612f9f565b61259a565b6040516108029190612fe7565b60405180910390f35b6108136125cd565b005b61081d6126b0565b60405161082a9190613673565b60405180910390f35b61083b6126d1565b6040516108489190613271565b60405180910390f35b6000807fbff2a7c862c2783f9ac585842eecebad2c8066f996e6582444b473e4ddf79b5990508091505090565b6000610888610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006108bb610851565b600001600083815260200190815260200160002060009054906101000a900460ff169050919050565b6108ec610851565b60010160009054906101000a900460ff1661093c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161093390612e1f565b60405180910390fd5b6000610a077f412cbf007be5b788a80c6ad2774faa9b1fb7684bf27c6f6e252f1c9d995002fd83600001602081019061097591906131dc565b61097d610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168580602001906109b1919061369d565b6040516109bf929190612eea565b60405180910390208660400135876060013588608001356040516020016109ec9796959493929190613700565b604051602081830303815290604052805190602001206126e4565b90506000610a2e838060a00190610a1e919061369d565b846127639092919063ffffffff16565b9050610a38610851565b60020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610ac9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ac0906137bb565b60405180910390fd5b610ad1610851565b60060160008460400135815260200190815260200160002060009054906101000a900460ff1615610b37576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b2e90613827565b60405180910390fd5b8260800135610b44610851565b600401541115610b89576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b8090613893565b60405180910390fd5b6000801b83606001351480610ba657508260600135836080013540145b610be5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bdc906138ff565b60405180910390fd5b60006020848060200190610bf9919061369d565b9050610c059190612bc7565b148015610c2357506000838060200190610c1f919061369d565b9050115b610c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c599061396b565b60405180910390fd5b828060200190610c72919061369d565b604051610c80929190612eea565b6040518091039020610c90610851565b6009016000856000016020810190610ca891906131dc565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610d23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d1a906139d7565b60405180910390fd5b6001610d2d610851565b60060160008560400135815260200190815260200160002060006101000a81548160ff021916908315150217905550610d64610851565b6009016000846000016020810190610d7c91906131dc565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000905560006020848060200190610dce919061369d565b9050610dda9190612d36565b90506000610e28610de9610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166127f8565b610e59866000016020810190610e3e91906131dc565b73ffffffffffffffffffffffffffffffffffffffff166127f8565b610e628461281e565b604051602001610e7493929190613bd5565b6040516020818303038152906040529050620face773ffffffffffffffffffffffffffffffffffffffff167f665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c82604051610ece9190613c32565b60405180910390a25050505050565b610ee5610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f76576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f6d90613ca0565b60405180910390fd5b6000610f80610851565b60050160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905011611004576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ffb90613d0c565b60405180910390fd5b61100c610851565b600601600082815260200190815260200160002060009054906101000a900460ff161561106e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161106590613827565b60405180910390fd5b6000611078610851565b60050160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905090506110c6610851565b60050160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006111129190612b50565b600161111c610851565b600601600084815260200190815260200160002060006101000a81548160ff0219169083151502179055506000611193611154610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166127f8565b6111b28573ffffffffffffffffffffffffffffffffffffffff166127f8565b6111bb8461281e565b6040516020016111cd93929190613bd5565b6040516020818303038152906040529050620face773ffffffffffffffffffffffffffffffffffffffff167f665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c826040516112279190613c32565b60405180910390a250505050565b600061123f610851565b600a016000848152602001908152602001600020600083815260200190815260200160002060009054906101000a900460ff16905092915050565b611282610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611313576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161130a90613ca0565b60405180910390fd5b4361131c610851565b60040181905550565b61132d610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113b590613ca0565b60405180910390fd5b806113c7610851565b60010160006101000a81548160ff02191690831515021790555050565b6113ec610851565b60010160019054906101000a900460ff1661143c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161143390613d78565b60405180910390fd5b60006115507f14e6af5e76748cb9ada4517dadbe966d51ffe80c1b4291aff27b97579f73e50c83600001602081019061147591906131dc565b61147d610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168580602001906114b19190613d98565b6040516020016114c2929190613e70565b604051602081830303815290604052805190602001208680604001906114e89190613d98565b6040516020016114f9929190613e70565b60405160208183030381529060405280519060200120876060013588608001358960a00135604051602001611535989796959493929190613e89565b604051602081830303815290604052805190602001206126e4565b90506000611577838060c00190611567919061369d565b846127639092919063ffffffff16565b9050611581610851565b60020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611612576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611609906137bb565b60405180910390fd5b61161a610851565b60000160008460600135815260200190815260200160002060009054906101000a900460ff1615611680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161167790613827565b60405180910390fd5b8260a0013561168d610851565b6004015411156116d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116c990613893565b60405180910390fd5b6116da610851565b600701548360a001356116ed9190613f07565b43101561172f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161172690613f87565b60405180910390fd5b6000801b8360800135148061174c575082608001358360a0013540145b61178b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611782906138ff565b60405180910390fd5b600083806020019061179d9190613d98565b9050116117df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117d690613ff3565b60405180910390fd5b8280604001906117ef9190613d98565b90508380602001906118019190613d98565b905014611843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183a9061405f565b60405180910390fd5b600161184d610851565b60000160008560600135815260200190815260200160002060006101000a81548160ff02191690831515021790555060005b83806020019061188f9190613d98565b9050811015611a145760008480602001906118aa9190613d98565b838181106118bb576118ba61407f565b5b90506020020135905060008580604001906118d69190613d98565b848181106118e7576118e661407f565b5b9050602002013590506118f8610851565b600a016000838152602001908152602001600020600082815260200190815260200160002060009054906101000a900460ff161561196b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161196290613827565b60405180910390fd5b6001611975610851565b600a016000848152602001908152602001600020600083815260200190815260200160002060006101000a81548160ff021916908315150217905550818660000160208101906119c591906131dc565b73ffffffffffffffffffffffffffffffffffffffff167ff30861289185032f511ff94a8127e470f3d0e6230be4925cb6fad33f3436dffb60405160405180910390a3826001019250505061187f565b506000611a61611a22610851565b60030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166127f8565b611a92856000016020810190611a7791906131dc565b73ffffffffffffffffffffffffffffffffffffffff166127f8565b611aad6020876060013560001c61286f90919063ffffffff16565b604051602001611abf9392919061416c565b6040516020818303038152906040529050620face773ffffffffffffffffffffffffffffffffffffffff167f665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c82604051611b199190613c32565b60405180910390a250505050565b6000611b31610851565b60010160009054906101000a900460ff16905090565b611b4f610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611be0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bd790613ca0565b60405180910390fd5b80611be9610851565b60020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000611c37610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b611c68610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cf9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cf090613ca0565b60405180910390fd5b80611d02610851565b60010160026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b611d4e610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ddf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dd690613ca0565b60405180910390fd5b80611de8610851565b6007018190555050565b6000611dfc610851565b60020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000611e2f610851565b60040154905090565b6000606080600080600060607f0f000000000000000000000000000000000000000000000000000000000000009650611e6f612897565b809650819750505046935030925090919293949596565b6060611e90610851565b60050160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015611f1a57602002820191906000526020600020905b815481526020019060010190808311611f06575b50505050509050919050565b611f2e610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611fbf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fb690613ca0565b60405180910390fd5b80611fc8610851565b60030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000612016610851565b60010160009054906101000a900460ff16905090565b612034610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120bc90613ca0565b60405180910390fd5b806120ce610851565b60010160016101000a81548160ff02191690831515021790555050565b6120f3610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612184576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161217b90613ca0565b60405180910390fd5b600161218e610851565b60010160006101000a81548160ff02191690831515021790555060016121b2610851565b60010160016101000a81548160ff021916908315150217905550565b6121d6610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225e90613ca0565b60405180910390fd5b80612270610851565b600b0160006101000a81548161ffff021916908361ffff16021790555050565b6d6396ff2a80c067f99b3d2ab4df2473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461230c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016123039061421d565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1661232c610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146123a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161239d90614289565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612415576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161240c906142f5565b60405180910390fd5b8061241e610851565b60010160026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61246a610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146124fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124f290613ca0565b60405180910390fd5b80612504610851565b60080160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000612552610851565b60090160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006125a4610851565b600601600083815260200190815260200160002060009054906101000a900460ff169050919050565b6125d5610851565b60010160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161265d90613ca0565b60405180910390fd5b6000612670610851565b60010160006101000a81548160ff0219169083151502179055506000612694610851565b60010160016101000a81548160ff021916908315150217905550565b60006126ba610851565b600b0160009054906101000a900461ffff16905090565b60006126db610851565b60070154905090565b60006126ee61290e565b15612702576126fb612913565b905061273e565b7f14f6234f2261fb122a3f7e552bdd161c6761271dc535271a61a5d5107b7554fa905061272d6129dd565b1561273d5761273a612913565b90505b5b67190100000000000060005280601a5281603a52604260182090506000603a52919050565b600060019050604051846000526001156127c657604083036127a3576020840135601b8160ff1c0160205284356040528060011b60011c606052506127c6565b604183036127c157604084013560001a6020526040846040376127c6565b600091505b6020600160806000855afa5191503d6127e757638baa579f6000526004601cfd5b600060605280604052509392505050565b606061280382612a37565b90506002815101613078825260028203915080825250919050565b60606080604051019050602081016040526000815280600019835b60011561285a578184019350600a81066030018453600a8104905080612839575b50828203602084039350808452505050919050565b606061287b8383612aba565b9050600281510161307882526002820391508082525092915050565b6060806040518060400160405280601f81526020017f466163657420457468736372697074696f6e204552433230204272696467650081525091506040518060400160405280600181526020017f340000000000000000000000000000000000000000000000000000000000000081525090509091565b600090565b60008061291e61290e565b1561294d5760008061292e612897565b9150915081805190602001209350808051906020012092505050612994565b7fe68da064b36808be5739f96ef91449ae44a4d956a601ba310f8349cf367525d391507f13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c06090505b6040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815282602082015281604082015246606082015230608082015260a081209250505090565b6000807f0000000000000000000000000000000000000000000000000000000000000001905060007f000000000000000000000000cfe75159b0b4fc357bb693b95dc5a5061cf79bd8905080301482461416159250505090565b60606040519050608081016040526f30313233343536373839616263646566600f526002810190506028815260208101600060288201528260601b925060005b600115612ab357808101820184821a600f81165160018301538060041c51825360018301925060148303612aac575050612ab3565b5050612a77565b5050919050565b6060601f1960428360011b01166040510190506020810160405260008152806f30313233343536373839616263646566600f528283018203600119855b600115612b25578185019450600f8116516001860153600f8160041c165185538060081c9050828518612af7575b8015612b3957632194895a6000526004601cfd5b848403602086039550808652505050505092915050565b5080546000825590600052602060002090810190612b6e9190612b71565b50565b5b80821115612b8a576000816000905550600101612b72565b5090565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000612bd282612b8e565b9150612bdd83612b8e565b925082612bed57612bec612b98565b5b828206905092915050565b600082825260208201905092915050565b7f496e76616c696420636f6e636174656e6174656420686173686573206c656e6760008201527f7468000000000000000000000000000000000000000000000000000000000000602082015250565b6000612c65602283612bf8565b9150612c7082612c09565b604082019050919050565b60006020820190508181036000830152612c9481612c58565b9050919050565b7f42726964676520696e206c696d6974206e6f7420736574000000000000000000600082015250565b6000612cd1601783612bf8565b9150612cdc82612c9b565b602082019050919050565b60006020820190508181036000830152612d0081612cc4565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000612d4182612b8e565b9150612d4c83612b8e565b925082612d5c57612d5b612b98565b5b828204905092915050565b7f546f6f206d616e7920657468736372697074696f6e7300000000000000000000600082015250565b6000612d9d601683612bf8565b9150612da882612d67565b602082019050919050565b60006020820190508181036000830152612dcc81612d90565b9050919050565b7f4465706f736974206e6f7420656e61626c656400000000000000000000000000600082015250565b6000612e09601383612bf8565b9150612e1482612dd3565b602082019050919050565b60006020820190508181036000830152612e3881612dfc565b9050919050565b7f4578697374696e672070656e64696e67206465706f7369740000000000000000600082015250565b6000612e75601883612bf8565b9150612e8082612e3f565b602082019050919050565b60006020820190508181036000830152612ea481612e68565b9050919050565b600081905092915050565b82818337600083830152505050565b6000612ed18385612eab565b9350612ede838584612eb6565b82840190509392505050565b6000612ef7828486612ec5565b91508190509392505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000612f2e82612f03565b9050919050565b612f3e81612f23565b82525050565b6000602082019050612f596000830184612f35565b92915050565b600080fd5b600080fd5b6000819050919050565b612f7c81612f69565b8114612f8757600080fd5b50565b600081359050612f9981612f73565b92915050565b600060208284031215612fb557612fb4612f5f565b5b6000612fc384828501612f8a565b91505092915050565b60008115159050919050565b612fe181612fcc565b82525050565b6000602082019050612ffc6000830184612fd8565b92915050565b600080fd5b600060c0828403121561301d5761301c613002565b5b81905092915050565b60006020828403121561303c5761303b612f5f565b5b600082013567ffffffffffffffff81111561305a57613059612f64565b5b61306684828501613007565b91505092915050565b61307881612f23565b811461308357600080fd5b50565b6000813590506130958161306f565b92915050565b600080604083850312156130b2576130b1612f5f565b5b60006130c085828601613086565b92505060206130d185828601612f8a565b9150509250929050565b600080604083850312156130f2576130f1612f5f565b5b600061310085828601612f8a565b925050602061311185828601612f8a565b9150509250929050565b61312481612fcc565b811461312f57600080fd5b50565b6000813590506131418161311b565b92915050565b60006020828403121561315d5761315c612f5f565b5b600061316b84828501613132565b91505092915050565b600060e0828403121561318a57613189613002565b5b81905092915050565b6000602082840312156131a9576131a8612f5f565b5b600082013567ffffffffffffffff8111156131c7576131c6612f64565b5b6131d384828501613174565b91505092915050565b6000602082840312156131f2576131f1612f5f565b5b600061320084828501613086565b91505092915050565b61321281612b8e565b811461321d57600080fd5b50565b60008135905061322f81613209565b92915050565b60006020828403121561324b5761324a612f5f565b5b600061325984828501613220565b91505092915050565b61326b81612b8e565b82525050565b60006020820190506132866000830184613262565b92915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b6132c18161328c565b82525050565b600081519050919050565b60005b838110156132f05780820151818401526020810190506132d5565b60008484015250505050565b6000601f19601f8301169050919050565b6000613318826132c7565b6133228185612bf8565b93506133328185602086016132d2565b61333b816132fc565b840191505092915050565b61334f81612f69565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61338a81612b8e565b82525050565b600061339c8383613381565b60208301905092915050565b6000602082019050919050565b60006133c082613355565b6133ca8185613360565b93506133d583613371565b8060005b838110156134065781516133ed8882613390565b97506133f8836133a8565b9250506001810190506133d9565b5085935050505092915050565b600060e082019050613428600083018a6132b8565b818103602083015261343a818961330d565b9050818103604083015261344e818861330d565b905061345d6060830187613262565b61346a6080830186612f35565b61347760a0830185613346565b81810360c083015261348981846133b5565b905098975050505050505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6134cc81612f69565b82525050565b60006134de83836134c3565b60208301905092915050565b6000602082019050919050565b600061350282613497565b61350c81856134a2565b9350613517836134b3565b8060005b8381101561354857815161352f88826134d2565b975061353a836134ea565b92505060018101905061351b565b5085935050505092915050565b6000602082019050818103600083015261356f81846134f7565b905092915050565b600061ffff82169050919050565b61358e81613577565b811461359957600080fd5b50565b6000813590506135ab81613585565b92915050565b6000602082840312156135c7576135c6612f5f565b5b60006135d58482850161359c565b91505092915050565b60006135e982612f23565b9050919050565b6135f9816135de565b811461360457600080fd5b50565b600081359050613616816135f0565b92915050565b60006020828403121561363257613631612f5f565b5b600061364084828501613607565b91505092915050565b600060208201905061365e6000830184613346565b92915050565b61366d81613577565b82525050565b60006020820190506136886000830184613664565b92915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126136ba576136b961368e565b5b80840192508235915067ffffffffffffffff8211156136dc576136db613693565b5b6020830192506001820236038313156136f8576136f7613698565b5b509250929050565b600060e082019050613715600083018a613346565b6137226020830189612f35565b61372f6040830188612f35565b61373c6060830187613346565b6137496080830186613346565b61375660a0830185613346565b61376360c0830184613262565b98975050505050505050565b7f496e76616c6964207369676e6174757265000000000000000000000000000000600082015250565b60006137a5601183612bf8565b91506137b08261376f565b602082019050919050565b600060208201905081810360008301526137d481613798565b9050919050565b7f416c72656164792070726f636573736564000000000000000000000000000000600082015250565b6000613811601183612bf8565b915061381c826137db565b602082019050919050565b6000602082019050818103600083015261384081613804565b9050919050565b7f5369676e61747572652063616e63656c65640000000000000000000000000000600082015250565b600061387d601283612bf8565b915061388882613847565b602082019050919050565b600060208201905081810360008301526138ac81613870565b9050919050565b7f496e76616c696420626c6f636b206e756d626572206f72206861736800000000600082015250565b60006138e9601c83612bf8565b91506138f4826138b3565b602082019050919050565b60006020820190508181036000830152613918816138dc565b9050919050565b7f496e76616c696420657468736372697074696f6e730000000000000000000000600082015250565b6000613955601583612bf8565b91506139608261391f565b602082019050919050565b6000602082019050818103600083015261398481613948565b9050919050565b7f496e76616c6964206465706f7369747300000000000000000000000000000000600082015250565b60006139c1601083612bf8565b91506139cc8261398b565b602082019050919050565b600060208201905081810360008301526139f0816139b4565b9050919050565b600081905092915050565b7f646174613a6170706c69636174696f6e2f766e642e66616365742e74782b6a7360008201527f6f6e3b72756c653d65736970362c7b226f70223a2263616c6c222c226461746160208201527f223a7b22746f223a220000000000000000000000000000000000000000000000604082015250565b6000613a846049836139f7565b9150613a8f82613a02565b604982019050919050565b6000613aa5826132c7565b613aaf81856139f7565b9350613abf8185602086016132d2565b80840191505092915050565b7f222c2266756e6374696f6e223a22627269646765496e222c2261726773223a7b60008201527f22746f223a220000000000000000000000000000000000000000000000000000602082015250565b6000613b276026836139f7565b9150613b3282613acb565b602682019050919050565b7f222c2022616d6f756e74223a2200000000000000000000000000000000000000600082015250565b6000613b73600d836139f7565b9150613b7e82613b3d565b600d82019050919050565b7f227d7d7d00000000000000000000000000000000000000000000000000000000600082015250565b6000613bbf6004836139f7565b9150613bca82613b89565b600482019050919050565b6000613be082613a77565b9150613bec8286613a9a565b9150613bf782613b1a565b9150613c038285613a9a565b9150613c0e82613b66565b9150613c1a8284613a9a565b9150613c2582613bb2565b9150819050949350505050565b60006020820190508181036000830152613c4c818461330d565b905092915050565b7f4e6f742061646d696e0000000000000000000000000000000000000000000000600082015250565b6000613c8a600983612bf8565b9150613c9582613c54565b602082019050919050565b60006020820190508181036000830152613cb981613c7d565b9050919050565b7f4e6f2070656e64696e67206465706f7369747300000000000000000000000000600082015250565b6000613cf6601383612bf8565b9150613d0182613cc0565b602082019050919050565b60006020820190508181036000830152613d2581613ce9565b9050919050565b7f5769746864726177206e6f7420656e61626c6564000000000000000000000000600082015250565b6000613d62601483612bf8565b9150613d6d82613d2c565b602082019050919050565b60006020820190508181036000830152613d9181613d55565b9050919050565b60008083356001602003843603038112613db557613db461368e565b5b80840192508235915067ffffffffffffffff821115613dd757613dd6613693565b5b602083019250602082023603831315613df357613df2613698565b5b509250929050565b600081905092915050565b600080fd5b82818337505050565b6000613e208385613dfb565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115613e5357613e52613e06565b5b602083029250613e64838584613e0b565b82840190509392505050565b6000613e7d828486613e14565b91508190509392505050565b600061010082019050613e9f600083018b613346565b613eac602083018a612f35565b613eb96040830189612f35565b613ec66060830188613346565b613ed36080830187613346565b613ee060a0830186613346565b613eed60c0830185613346565b613efa60e0830184613262565b9998505050505050505050565b6000613f1282612b8e565b9150613f1d83612b8e565b9250828201905080821115613f3557613f34612d07565b5b92915050565b7f57697468647261772064656c6179000000000000000000000000000000000000600082015250565b6000613f71600e83612bf8565b9150613f7c82613f3b565b602082019050919050565b60006020820190508181036000830152613fa081613f64565b9050919050565b7f4e6f20657468736372697074696f6e7300000000000000000000000000000000600082015250565b6000613fdd601083612bf8565b9150613fe882613fa7565b602082019050919050565b6000602082019050818103600083015261400c81613fd0565b9050919050565b7f4c656e677468206d69736d617463680000000000000000000000000000000000600082015250565b6000614049600f83612bf8565b915061405482614013565b602082019050919050565b600060208201905081810360008301526140788161403c565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f222c2266756e6374696f6e223a226d61726b5769746864726177616c436f6d7060008201527f6c657465222c2261726773223a7b22746f223a22000000000000000000000000602082015250565b600061410a6034836139f7565b9150614115826140ae565b603482019050919050565b7f222c227769746864726177616c4964223a220000000000000000000000000000815250565b7f227d7d7d00000000000000000000000000000000000000000000000000000000815250565b600061417782613a77565b91506141838286613a9a565b915061418e826140fd565b915061419a8285613a9a565b91506141a582614120565b6012820191506141b58284613a9a565b91506141c082614146565b600482019150819050949350505050565b7f4e6f7420666163746f7279000000000000000000000000000000000000000000600082015250565b6000614207600b83612bf8565b9150614212826141d1565b602082019050919050565b60006020820190508181036000830152614236816141fa565b9050919050565b7f416c726561647920696e697469616c697a656400000000000000000000000000600082015250565b6000614273601383612bf8565b915061427e8261423d565b602082019050919050565b600060208201905081810360008301526142a281614266565b9050919050565b7f4e6f207a65726f20616464726573730000000000000000000000000000000000600082015250565b60006142df600f83612bf8565b91506142ea826142a9565b602082019050919050565b6000602082019050818103600083015261430e816142d2565b905091905056fea264697066735822122049d136c8ac1d9f42705fe1886b42ab293896bd5b899fbc0fa196b992ca86996a64736f6c63430008110033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.