Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- TimeCapsuleFactory
- Optimization enabled
- true
- Compiler version
- v0.8.16+commit.07a7930e
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2023-07-17T12:17:59.743579Z
Constructor Arguments
0x0000000000000000000000005fe3543f96c7aa1a85d82206ccedc80dc2722d00000000000000000000000000ac723c0b8cdf9995141f4dabfcf0b30b0cdfccb0
Arg [0] (address) : 0x5fe3543f96c7aa1a85d82206ccedc80dc2722d00
Arg [1] (address) : 0xac723c0b8cdf9995141f4dabfcf0b30b0cdfccb0
Contract source code
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
library Strings {
function toString(uint256 value) internal pure returns (string memory) {
// from @openzeppelin String.sol
unchecked {
////
// uint256 length = Math.log10(value) + 1; =>
// from @openzeppelin Math.sol
uint256 length = 0;
if (value >= 10**64) { value /= 10**64; length += 64; }
if (value >= 10**32) { value /= 10**32; length += 32; }
if (value >= 10**16) { value /= 10**16; length += 16; }
if (value >= 10**8) { value /= 10**8; length += 8; }
if (value >= 10**4) { value /= 10**4; length += 4; }
if (value >= 10**2) { value /= 10**2; length += 2; }
if (value >= 10**1) { length += 1; }
length++;
////
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), "0123456789abcdef"))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
}
/// @dev SYNONYMS: vault, capsule, timecapsule
interface IFeeSplitter {
function splitERC20(address tokenAddress) external returns (bool);
}
interface ITimeCapsule {
function initialize(
address _newOwner,
address _factoryAddress,
IFeeSplitter _feeSplitterContract
) external;
}
/**
* @title TimeCapsuleFactory
* @notice Responsible for TimeCapsule contract deployment plus storage and use of recovery address hashes.
*/
contract TimeCapsuleFactory {
struct OwnerCapsuleRecord {
address capsuleAddress;
bytes32 recoveryAddressHash;
}
mapping(address => OwnerCapsuleRecord) private capsules; // index address => capsule owner address
mapping(bytes32 => address) private ownerHashes; // index bytes32 => keccack256 of vault owner address
// validation: a recoveryAddressHash is "validated" when a matching entry exists in hashCapsuleAddresses
mapping(bytes32 => address) private hashCapsuleAddresses; // index => recovery address hash (for reverse owner lookups)
address private founder; // NOT AN ADMIN KEY -- Only used to allow contract creator to airdrop free capsules (promotional)
address private timeCapsuleImplementationAddress;
IFeeSplitter private NO_EXPECTATIONS; // splits 0.1% fees evenly between three project founders
constructor(
address _timeCapsuleImplementationAddress,
IFeeSplitter _feeSplitterContract
) {
founder = msg.sender;
timeCapsuleImplementationAddress = _timeCapsuleImplementationAddress;
NO_EXPECTATIONS = _feeSplitterContract;
}
/**
* Consciously excluding receive/fallback functions
* receive() external payable { }
* fallback() external payable { }
*/
modifier _onlyOwnersCapsule(address _owner) {
require(
msg.sender == capsuleAddressOf(_owner),
"Invalid caller"
);
_;
}
event TimeCapsuleCreated(
address owner,
address capsuleAddress
);
event OwnerChanged(
address capsuleAddress,
address oldOwner,
address newOwner
);
function predictedCapsuleAddress(address ownerAddress) public view returns (address predictedAddress) {
predictedAddress = Clones.predictDeterministicAddress(timeCapsuleImplementationAddress, bytes32(abi.encode(ownerAddress)));
}
/**
* Instantiates a new vault.
* @param _newOwner address of the new Time Caposule's owner
*/
function _instantiateTimeCapsule(address _newOwner) internal {
require(
capsules[_newOwner].capsuleAddress == address(0),
"Capsule already assigned"
);
// _newOwner must not *be* an existing _and_ *validated* recovery address
require(
hashCapsuleAddresses[keccak256(abi.encodePacked(_newOwner))] == address(0),
"Creator address is a validated recovery address"
);
/// @dev _recoveryAddressHash may be zero (unset)
// instantiate new TimeCapsule (vault) contract
/// @dev we use msg.sender as the salt for a deterministic vault deployment address
address payable _capsuleAddress = payable(Clones.cloneDeterministic(
timeCapsuleImplementationAddress, bytes32(abi.encode(msg.sender))
));
try ITimeCapsule(_capsuleAddress).initialize(
_newOwner, // address _newOwner
address(this), // address _factoryAddress
NO_EXPECTATIONS // FeeSplitter NO_EXPECTATIONS
) {
OwnerCapsuleRecord memory newCapsule = OwnerCapsuleRecord({
capsuleAddress: _capsuleAddress,
recoveryAddressHash: bytes32(0)
});
capsules[_newOwner] = newCapsule;
bytes32 ownerHash = keccak256(abi.encodePacked(_newOwner));
ownerHashes[ownerHash] = _newOwner;
emit TimeCapsuleCreated(_newOwner, _capsuleAddress);
} catch {
revert("Capsule initialization failed");
}
}
/**
* Creates a vault with no recovery address.
*/
function createTimeCapsule() public {
_instantiateTimeCapsule(msg.sender);
}
/**
* Creates a vault with a recovery address.
* @param _recoveryAddressHash keccak256 (sha3) hash of recovery address
*/
function createTimeCapsule(bytes32 _recoveryAddressHash) public {
_instantiateTimeCapsule(msg.sender);
capsules[msg.sender].recoveryAddressHash = _recoveryAddressHash;
}
/**
* Returns the contract address of the owner's vault. (Each owner address can only have one vault.)
* @param _owner address of vault owner
*/
function capsuleAddressOf(address _owner) public view returns (address capsuleAddress) {
capsuleAddress = capsules[_owner].capsuleAddress;
}
/**
* Splits a 65 byte (130 nibble) 'raw' signature into R, S, V components.
* @param _signature 65 byte (130 nibble) 'raw' signature
* @return r signature R component
* @return s signature S component
* @return v signature V component
*/
function _splitSignature(bytes memory _signature) private pure returns (bytes32 r, bytes32 s, uint8 v) {
assembly {
r := mload(add(_signature, 32))
s := mload(add(_signature, 64))
v := byte(0, mload(add(_signature, 96)))
}
}
/**
* Recovers the address of the signer of a arbitrary length message.
* @param _message the signed message
* @param _signature signature
*/
function _recoverSignerAddress(
string memory _message,
bytes memory _signature
)
private
pure
returns (address signerAddress)
{
if (_signature.length != 65) return address(0);
bytes32 _messageHash = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n",
Strings.toString(bytes(_message).length),
bytes(_message)
)
);
(bytes32 r, bytes32 s, uint8 v) = _splitSignature(_signature);
signerAddress = ecrecover(_messageHash, v, r, s);
}
/**
* Sets the vault's recovery address. Reverts if recovery address has been validated.
* @notice internal function
* @param _recoveryAddressHash keccak256 hash of recovery address
*/
function _setRecoveryAddressHash(
address _owner,
bytes32 _recoveryAddressHash
)
internal
{
require(
isRecoveryHashValidated(_owner) == false,
"Duplicate validated recovery address"
);
capsules[_owner].recoveryAddressHash = _recoveryAddressHash;
}
/**
* @notice internal function
* @param _owner vault owner addres
* @param _recoveryAddressHash keccak256 (sha3) hash of the recovery address being signed
* @param _signature signature
*/
function _validateRecoveryAddressHash(
address _owner,
bytes32 _recoveryAddressHash,
bytes memory _signature
)
internal
{
require(
ownerHashes[_recoveryAddressHash] == address(0),
"Cannot own a vault"
);
require(
hashCapsuleAddresses[_recoveryAddressHash] == address(0),
"Already validated"
);
address signerAddress = _recoverSignerAddress(
"CONFIRMING RECOVERY ADDRESS",
_signature
);
require(
keccak256(abi.encodePacked(signerAddress)) == _recoveryAddressHash,
"Invalid signature"
);
bytes32 _existingRecoveryAddressHash = capsules[_owner].recoveryAddressHash;
if (hashCapsuleAddresses[_existingRecoveryAddressHash] == _owner) {
delete hashCapsuleAddresses[_existingRecoveryAddressHash];
}
// recoveryHash is "validated" when a matching record exists in hashCapsuleAddresses[]
hashCapsuleAddresses[_recoveryAddressHash] = _owner;
}
/**
* Sets (or re-sets) and validates the vault's recovery address by having it signed off-chain by
* the recovery address private keys. NOTE: An unvalidated recovery address may only be set at vault
* creation. Future setting of the address *requires* that it be validated immediately, using
* this function.
* @notice the requirement to separately validate a recovery address is both reduces onboarding
* friction — and prevents certain style of DOS attack regarding use of another's address.
* @param _owner vault owner addres
* @param _recoveryAddressHash keccak256 (sha3) hash of the recovery address being signed
* @param _signature signature
*/
function validateRecoveryAddressHash(
address _owner,
bytes32 _recoveryAddressHash,
bytes memory _signature
)
public
_onlyOwnersCapsule(_owner)
{
_setRecoveryAddressHash(_owner, _recoveryAddressHash);
_validateRecoveryAddressHash(
_owner,
_recoveryAddressHash,
_signature
);
}
/**
* Verifies that the recovery address for the vault is indeed the one the being checked.
* @param _owner address of vault owner
* @param _addressHash keccak256 (sha3) hash of the recovery address
*/
function checkRecoveryAddress(
address _owner,
bytes32 _addressHash
)
public
view
returns (bool confirmed)
{
confirmed = _addressHash == capsules[_owner].recoveryAddressHash;
}
/**
* Returns true/false according to recovery address validation status.
* @param _owner address of the vault owner
*/
function isRecoveryHashValidated(address _owner)
public view
_onlyOwnersCapsule(_owner)
returns (bool)
{
bytes32 _recoveryAddressHash = capsules[_owner].recoveryAddressHash;
// hashCapsuleAddresses[_recoveryAddressHash] entry should only exist if hash is validated
return hashCapsuleAddresses[_recoveryAddressHash] == _owner;
}
/**
* Recovers ownership of a vault to the recovery address.
* @notice Can only be executed by private key associated with recorded recovery address hash
* @param _oldOwner address of the current (panic state) owner
* @param _newOwner address of the new owner
*/
function recoverOwnership(
address _oldOwner,
address _newOwner
)
public
_onlyOwnersCapsule(_oldOwner)
{
require(
capsules[_oldOwner].recoveryAddressHash == keccak256(abi.encodePacked(_newOwner)),
"Invalid caller" // purposely vague revert message
);
capsules[_newOwner] = OwnerCapsuleRecord({
capsuleAddress: msg.sender,
recoveryAddressHash: bytes32(0)
});
delete capsules[_oldOwner];
delete hashCapsuleAddresses[keccak256(abi.encodePacked(msg.sender))];
delete ownerHashes[keccak256(abi.encodePacked(_oldOwner))];
ownerHashes[keccak256(abi.encodePacked(msg.sender))] = msg.sender;
emit OwnerChanged(
msg.sender,
_oldOwner,
_newOwner
);
}
}
Contract ABI
[{"type":"constructor","inputs":[{"type":"address","name":"_timeCapsuleImplementationAddress","internalType":"address"},{"type":"address","name":"_feeSplitterContract","internalType":"contract IFeeSplitter"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"capsuleAddress","internalType":"address"}],"name":"capsuleAddressOf","inputs":[{"type":"address","name":"_owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"confirmed","internalType":"bool"}],"name":"checkRecoveryAddress","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"bytes32","name":"_addressHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createTimeCapsule","inputs":[{"type":"bytes32","name":"_recoveryAddressHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createTimeCapsule","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isRecoveryHashValidated","inputs":[{"type":"address","name":"_owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"predictedAddress","internalType":"address"}],"name":"predictedCapsuleAddress","inputs":[{"type":"address","name":"ownerAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverOwnership","inputs":[{"type":"address","name":"_oldOwner","internalType":"address"},{"type":"address","name":"_newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"validateRecoveryAddressHash","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"bytes32","name":"_recoveryAddressHash","internalType":"bytes32"},{"type":"bytes","name":"_signature","internalType":"bytes"}]},{"type":"event","name":"OwnerChanged","inputs":[{"type":"address","name":"capsuleAddress","indexed":false},{"type":"address","name":"oldOwner","indexed":false},{"type":"address","name":"newOwner","indexed":false}],"anonymous":false},{"type":"event","name":"TimeCapsuleCreated","inputs":[{"type":"address","name":"owner","indexed":false},{"type":"address","name":"capsuleAddress","indexed":false}],"anonymous":false}]
Contract Creation Code
0x608060405234801561001057600080fd5b506040516110fb3803806110fb83398101604081905261002f91610083565b600380546001600160a01b03199081163317909155600480546001600160a01b03948516908316179055600580549290931691161790556100bd565b6001600160a01b038116811461008057600080fd5b50565b6000806040838503121561009657600080fd5b82516100a18161006b565b60208401519092506100b28161006b565b809150509250929050565b61102f806100cc6000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063d7e889961161005b578063d7e8899614610106578063e5a054b814610145578063e7274c2b14610158578063fd9643a51461016b57600080fd5b8063048abced1461008d57806329af8a1f146100a2578063586cb099146100eb578063b3431a5e146100fe575b600080fd5b6100a061009b366004610d83565b61017e565b005b6100ce6100b0366004610db8565b6001600160a01b039081166000908152602081905260409020541690565b6040516001600160a01b0390911681526020015b60405180910390f35b6100ce6100f9366004610db8565b61019c565b6100a06101e1565b610135610114366004610dd3565b6001600160a01b039091166000908152602081905260409020600101541490565b60405190151581526020016100e2565b6100a0610153366004610e13565b6101ec565b6100a0610166366004610ede565b61024b565b610135610179366004610db8565b61047a565b61018733610504565b33600090815260208190526040902060010155565b600454604080516001600160a01b0384811660208301526000936101db93911691016040516020818303038152906040526101d690610f11565b6107ed565b92915050565b6101ea33610504565b565b6001600160a01b0383811660009081526020819052604090205484911633146102305760405162461bcd60e51b815260040161022790610f35565b60405180910390fd5b61023a8484610850565b6102458484846108d1565b50505050565b6001600160a01b0382811660009081526020819052604090205483911633146102865760405162461bcd60e51b815260040161022790610f35565b816040516020016102979190610f5d565b60408051601f1981840301815291815281516020928301206001600160a01b038616600090815292839052912060010154146102e55760405162461bcd60e51b815260040161022790610f35565b60408051808201825233808252600060208084018281526001600160a01b038881168452838352868420955186549082166001600160a01b031991821617875591516001968701558916835285832080549091168155909301819055925160029392610352929101610f5d565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154906001600160a01b03021916905560016000846040516020016103a19190610f5d565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154906001600160a01b0302191690553360016000336040516020016103f19190610f5d565b60408051808303601f19018152918152815160209283012083528282019390935290820160002080546001600160a01b0319166001600160a01b03948516179055815133815286841691810191909152918416908201527f381c0d11398486654573703c51ee8210ce9461764d133f9f0e53b6a5397053319060600160405180910390a1505050565b60008161049f816001600160a01b039081166000908152602081905260409020541690565b6001600160a01b0316336001600160a01b0316146104cf5760405162461bcd60e51b815260040161022790610f35565b6001600160a01b0380841660008181526020818152604080832060010154835260029091529020549091161491505b50919050565b6001600160a01b03818116600090815260208190526040902054161561056c5760405162461bcd60e51b815260206004820152601860248201527f43617073756c6520616c72656164792061737369676e656400000000000000006044820152606401610227565b60006001600160a01b0316600260008360405160200161058c9190610f5d565b60408051601f19818403018152918152815160209283012083529082019290925201600020546001600160a01b0316146106205760405162461bcd60e51b815260206004820152602f60248201527f43726561746f72206164647265737320697320612076616c696461746564207260448201526e65636f76657279206164647265737360881b6064820152608401610227565b6004546040805133602082015260009261065e926001600160a01b03909116910160405160208183030381529060405261065990610f11565b610ab0565b60055460405163c0c53b8b60e01b81526001600160a01b038581166004830152306024830152918216604482015291925082169063c0c53b8b90606401600060405180830381600087803b1580156106b557600080fd5b505af19250505080156106c6575060015b6107125760405162461bcd60e51b815260206004820152601d60248201527f43617073756c6520696e697469616c697a6174696f6e206661696c65640000006044820152606401610227565b6040805180820182526001600160a01b038381168252600060208084018281528784168352828252858320855181546001600160a01b031916951694909417845551600190930192909255925191929161076e91869101610f5d565b60408051808303601f19018152828252805160209182012060008181526001835283902080546001600160a01b0319166001600160a01b038a8116918217909255855287169184019190915292507f30bf3f418b8397f2cc23bfe61c58a92e1b61277f5d22404443504fae23cc3160910160405180910390a150505050565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101206000905b9392505050565b6108598261047a565b156108b25760405162461bcd60e51b8152602060048201526024808201527f4475706c69636174652076616c696461746564207265636f76657279206164646044820152637265737360e01b6064820152608401610227565b6001600160a01b03909116600090815260208190526040902060010155565b6000828152600160205260409020546001600160a01b03161561092b5760405162461bcd60e51b815260206004820152601260248201527110d85b9b9bdd081bdddb8818481d985d5b1d60721b6044820152606401610227565b6000828152600260205260409020546001600160a01b0316156109845760405162461bcd60e51b8152602060048201526011602482015270105b1c9958591e481d985b1a59185d1959607a1b6044820152606401610227565b60006109c56040518060400160405280601b81526020017f434f4e4649524d494e47205245434f564552592041444452455353000000000081525083610b4d565b905082816040516020016109d99190610f5d565b6040516020818303038152906040528051906020012014610a305760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b6044820152606401610227565b6001600160a01b038085166000818152602081815260408083206001015480845260029092529091205490921603610a7f57600081815260026020526040902080546001600160a01b03191690555b505050600090815260026020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b0381166101db5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401610227565b60008151604114610b60575060006101db565b6000610b6c8451610c2b565b84604051602001610b7e929190610f9e565b6040516020818303038152906040528051906020012090506000806000610bba8660208101516040820151606090920151909260009190911a90565b60408051600081526020810180835289905260ff8316918101919091526060810184905260808101839052929550909350915060019060a0016020604051602081039080840390855afa158015610c15573d6000803e3d6000fd5b5050604051601f19015198975050505050505050565b6060600072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610c6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310610c97576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310610cb557662386f26fc10000830492506010015b6305f5e1008310610ccd576305f5e100830492506008015b6127108310610ce157612710830492506004015b60648310610cf3576064830492506002015b600a8310610cff576001015b60010160008167ffffffffffffffff811115610d1d57610d1d610dfd565b6040519080825280601f01601f191660200182016040528015610d47576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610d5157509392505050565b600060208284031215610d9557600080fd5b5035919050565b80356001600160a01b0381168114610db357600080fd5b919050565b600060208284031215610dca57600080fd5b61084982610d9c565b60008060408385031215610de657600080fd5b610def83610d9c565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b600080600060608486031215610e2857600080fd5b610e3184610d9c565b925060208401359150604084013567ffffffffffffffff80821115610e5557600080fd5b818601915086601f830112610e6957600080fd5b813581811115610e7b57610e7b610dfd565b604051601f8201601f19908116603f01168101908382118183101715610ea357610ea3610dfd565b81604052828152896020848701011115610ebc57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060408385031215610ef157600080fd5b610efa83610d9c565b9150610f0860208401610d9c565b90509250929050565b805160208083015191908110156104fe5760001960209190910360031b1b16919050565b6020808252600e908201526d24b73b30b634b21031b0b63632b960911b604082015260600190565b60609190911b6bffffffffffffffffffffffff1916815260140190565b60005b83811015610f95578181015183820152602001610f7d565b50506000910152565b7f19457468657265756d205369676e6564204d6573736167653a0a000000000000815260008351610fd681601a850160208801610f7a565b835190830190610fed81601a840160208801610f7a565b01601a0194935050505056fea264697066735822122050fc5cba022461e9f93f2ba1f1990f745eddded6f4c1bf801581f80c06e2aa2164736f6c634300081000330000000000000000000000005fe3543f96c7aa1a85d82206ccedc80dc2722d00000000000000000000000000ac723c0b8cdf9995141f4dabfcf0b30b0cdfccb0
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063d7e889961161005b578063d7e8899614610106578063e5a054b814610145578063e7274c2b14610158578063fd9643a51461016b57600080fd5b8063048abced1461008d57806329af8a1f146100a2578063586cb099146100eb578063b3431a5e146100fe575b600080fd5b6100a061009b366004610d83565b61017e565b005b6100ce6100b0366004610db8565b6001600160a01b039081166000908152602081905260409020541690565b6040516001600160a01b0390911681526020015b60405180910390f35b6100ce6100f9366004610db8565b61019c565b6100a06101e1565b610135610114366004610dd3565b6001600160a01b039091166000908152602081905260409020600101541490565b60405190151581526020016100e2565b6100a0610153366004610e13565b6101ec565b6100a0610166366004610ede565b61024b565b610135610179366004610db8565b61047a565b61018733610504565b33600090815260208190526040902060010155565b600454604080516001600160a01b0384811660208301526000936101db93911691016040516020818303038152906040526101d690610f11565b6107ed565b92915050565b6101ea33610504565b565b6001600160a01b0383811660009081526020819052604090205484911633146102305760405162461bcd60e51b815260040161022790610f35565b60405180910390fd5b61023a8484610850565b6102458484846108d1565b50505050565b6001600160a01b0382811660009081526020819052604090205483911633146102865760405162461bcd60e51b815260040161022790610f35565b816040516020016102979190610f5d565b60408051601f1981840301815291815281516020928301206001600160a01b038616600090815292839052912060010154146102e55760405162461bcd60e51b815260040161022790610f35565b60408051808201825233808252600060208084018281526001600160a01b038881168452838352868420955186549082166001600160a01b031991821617875591516001968701558916835285832080549091168155909301819055925160029392610352929101610f5d565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154906001600160a01b03021916905560016000846040516020016103a19190610f5d565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154906001600160a01b0302191690553360016000336040516020016103f19190610f5d565b60408051808303601f19018152918152815160209283012083528282019390935290820160002080546001600160a01b0319166001600160a01b03948516179055815133815286841691810191909152918416908201527f381c0d11398486654573703c51ee8210ce9461764d133f9f0e53b6a5397053319060600160405180910390a1505050565b60008161049f816001600160a01b039081166000908152602081905260409020541690565b6001600160a01b0316336001600160a01b0316146104cf5760405162461bcd60e51b815260040161022790610f35565b6001600160a01b0380841660008181526020818152604080832060010154835260029091529020549091161491505b50919050565b6001600160a01b03818116600090815260208190526040902054161561056c5760405162461bcd60e51b815260206004820152601860248201527f43617073756c6520616c72656164792061737369676e656400000000000000006044820152606401610227565b60006001600160a01b0316600260008360405160200161058c9190610f5d565b60408051601f19818403018152918152815160209283012083529082019290925201600020546001600160a01b0316146106205760405162461bcd60e51b815260206004820152602f60248201527f43726561746f72206164647265737320697320612076616c696461746564207260448201526e65636f76657279206164647265737360881b6064820152608401610227565b6004546040805133602082015260009261065e926001600160a01b03909116910160405160208183030381529060405261065990610f11565b610ab0565b60055460405163c0c53b8b60e01b81526001600160a01b038581166004830152306024830152918216604482015291925082169063c0c53b8b90606401600060405180830381600087803b1580156106b557600080fd5b505af19250505080156106c6575060015b6107125760405162461bcd60e51b815260206004820152601d60248201527f43617073756c6520696e697469616c697a6174696f6e206661696c65640000006044820152606401610227565b6040805180820182526001600160a01b038381168252600060208084018281528784168352828252858320855181546001600160a01b031916951694909417845551600190930192909255925191929161076e91869101610f5d565b60408051808303601f19018152828252805160209182012060008181526001835283902080546001600160a01b0319166001600160a01b038a8116918217909255855287169184019190915292507f30bf3f418b8397f2cc23bfe61c58a92e1b61277f5d22404443504fae23cc3160910160405180910390a150505050565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101206000905b9392505050565b6108598261047a565b156108b25760405162461bcd60e51b8152602060048201526024808201527f4475706c69636174652076616c696461746564207265636f76657279206164646044820152637265737360e01b6064820152608401610227565b6001600160a01b03909116600090815260208190526040902060010155565b6000828152600160205260409020546001600160a01b03161561092b5760405162461bcd60e51b815260206004820152601260248201527110d85b9b9bdd081bdddb8818481d985d5b1d60721b6044820152606401610227565b6000828152600260205260409020546001600160a01b0316156109845760405162461bcd60e51b8152602060048201526011602482015270105b1c9958591e481d985b1a59185d1959607a1b6044820152606401610227565b60006109c56040518060400160405280601b81526020017f434f4e4649524d494e47205245434f564552592041444452455353000000000081525083610b4d565b905082816040516020016109d99190610f5d565b6040516020818303038152906040528051906020012014610a305760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b6044820152606401610227565b6001600160a01b038085166000818152602081815260408083206001015480845260029092529091205490921603610a7f57600081815260026020526040902080546001600160a01b03191690555b505050600090815260026020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b0381166101db5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401610227565b60008151604114610b60575060006101db565b6000610b6c8451610c2b565b84604051602001610b7e929190610f9e565b6040516020818303038152906040528051906020012090506000806000610bba8660208101516040820151606090920151909260009190911a90565b60408051600081526020810180835289905260ff8316918101919091526060810184905260808101839052929550909350915060019060a0016020604051602081039080840390855afa158015610c15573d6000803e3d6000fd5b5050604051601f19015198975050505050505050565b6060600072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610c6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310610c97576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310610cb557662386f26fc10000830492506010015b6305f5e1008310610ccd576305f5e100830492506008015b6127108310610ce157612710830492506004015b60648310610cf3576064830492506002015b600a8310610cff576001015b60010160008167ffffffffffffffff811115610d1d57610d1d610dfd565b6040519080825280601f01601f191660200182016040528015610d47576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610d5157509392505050565b600060208284031215610d9557600080fd5b5035919050565b80356001600160a01b0381168114610db357600080fd5b919050565b600060208284031215610dca57600080fd5b61084982610d9c565b60008060408385031215610de657600080fd5b610def83610d9c565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b600080600060608486031215610e2857600080fd5b610e3184610d9c565b925060208401359150604084013567ffffffffffffffff80821115610e5557600080fd5b818601915086601f830112610e6957600080fd5b813581811115610e7b57610e7b610dfd565b604051601f8201601f19908116603f01168101908382118183101715610ea357610ea3610dfd565b81604052828152896020848701011115610ebc57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060408385031215610ef157600080fd5b610efa83610d9c565b9150610f0860208401610d9c565b90509250929050565b805160208083015191908110156104fe5760001960209190910360031b1b16919050565b6020808252600e908201526d24b73b30b634b21031b0b63632b960911b604082015260600190565b60609190911b6bffffffffffffffffffffffff1916815260140190565b60005b83811015610f95578181015183820152602001610f7d565b50506000910152565b7f19457468657265756d205369676e6564204d6573736167653a0a000000000000815260008351610fd681601a850160208801610f7a565b835190830190610fed81601a840160208801610f7a565b01601a0194935050505056fea264697066735822122050fc5cba022461e9f93f2ba1f1990f745eddded6f4c1bf801581f80c06e2aa2164736f6c63430008100033