Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- TimeCapsule
- Optimization enabled
- true
- Compiler version
- v0.8.16+commit.07a7930e
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2023-07-17T12:15:44.109806Z
Contract source code
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
/**
* @dev copied and condensed from @openzeppelin/contracts/proxy/utils/Initializable.sol
**/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
/* changed from: (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
to: */(isTopLevelCall && _initialized < 1) || ((address(this).code.length == 0) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
* @dev added by TPM
*/
function isInitialized() public view returns (bool) {
return _initialized == 1;
}
}
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 Interface of the ERC20 standard as defined in the EIP.
*/
interface ITimeCapsuleFactory {
struct OwnerCapsuleRecord {
address capsuleAddress;
bytes32 recoveryAddressHash;
bool validated;
}
function predictedCapsuleAddress(address ownerAddress) external view returns (address predictedAddress);
function createTimeCapsule() external;
function createTimeCapsule(bytes32 _recoveryAddressHash) external;
function capsuleAddressOf(address _owner) external view returns (address capsuleAddress);
function validateRecoveryAddressHash(
address _owner,
bytes32 _recoveryAddressHash,
bytes memory _signature
) external;
function checkRecoveryAddress(address _owner, bytes32 _addressHash) external view returns (bool confirmed);
function isRecoveryHashValidated(address _owner) external view returns (bool);
function getRecoveryRecord(address _owner) external view returns (OwnerCapsuleRecord memory);
function recoverOwnership( address _oldOwner, address _newOwner ) external;
}
/// @dev SYNONYMS: vault, capsule, timecapsule
/// @dev _feeSplitter (feeSplitter.sol) is our own, trusted contract (context: reentrance attacks)
interface IFeeSplitter {
function splitERC20(address tokenAddress) external returns (bool);
}
/**
* @dev A place for TimeCapsule Constants to live
*/
abstract contract TimeCapsuleContext is Initializable {
uint256 constant internal FEE_DIVISOR = 1000; // 0.1%
uint64 constant internal ONE_HOUR = 60 * 60;
uint64 constant internal ONE_DAY = 24 * 60 * 60;
uint64 constant internal SEVEN_DAYS = 7 * ONE_DAY;
uint64 constant internal ONE_YEAR = 365 * ONE_DAY;
uint64 constant internal MAX_LOCK_SECONDS = 20089 * ONE_DAY; // ~55 years (365.25 days each)
uint64 constant internal TIMELOCK_SECONDS = SEVEN_DAYS;
uint64 constant internal BEHAVIORLOCK_SECONDS = ONE_YEAR;
address constant internal NATIVE_COIN = address(0);
address constant internal INVALID_SIGNER_ADDRESS = address(0);
string constant internal VAULT_RECOVERY_AUTHORIZATION="VAULT RECOVERY AUTHORIZATION";
string constant internal CONFIRMING_RECOVERY_ADDRESS="CONFIRMING RECOVERY ADDRESS";
enum VaultStatus {
NOMINAL, // all is well
PANIC, // 'panic switch' activated (lockout for recovery)
RECOVERED // (read-only)
}
enum LockState {
INERT,
WITHDRAWAL_PENDING
}
enum LockType {
DRIP,
HARD,
BEHAVIORAL
}
struct Lock {
LockState state; // uint8
bytes32 tag; // <= 31 character descriptor string
uint64 lockTime; // UTC
uint64 unlockTime; // UTC
uint64 releaseTime; // UTC timestamp when time-locked funds release for sending
LockType lockType; // uint8
bytes6 unlockHash; // first 6 bytes of keccak256 hash
// 256 bit boundary
uint256 lockedAmount; // in ERC20 token's base units
uint256 withdrawnToDate; // in ERC20 token's base units
uint256 timelockedAmount; // amount of any pending, time-locked withdrawal
}
}
/**
* @title TimeCapsule
*/
contract TimeCapsule is TimeCapsuleContext{
address public owner;
ITimeCapsuleFactory private _capsuleFactory;
mapping(address => Lock[]) private _locks; // address is 'rc20 token contract address or address(0)
mapping(address => uint256) private _totalLockedAmount;
uint64 private _deadmanTime;
VaultStatus private _vaultStatus;
IFeeSplitter private _feeSplitter;
constructor() {
// Contructor not used by factory. We use it here only to prevent master copy hijacks
// the factory will call this.initialize() for one-off setup tasks.
_disableInitializers();
}
/**
* @dev Because this contract is a proxied clone (oppenzep'Clone.sol)
* functions herein get called **twice**. The first call has `msg.sender`
* set as the address of the caller, as usual. The second _proxy_ call
* has `msg.sender` set to this contract's own address. Hence, we roll
* our own 'Ownable' (minus transferability) and with a different name
* than 'onlyOwner' to avoid confusion.
*/
modifier _onlyCapsuleOwner() {
if (address(this) != msg.sender) {
require(owner == msg.sender, "Not owner");
_;
}
}
/**
* @dev This modifier blocks calls to functions when the vault is in the 'panic' state.
*/
modifier _notPanicState() {
if (address(this) != msg.sender) {
require(_vaultStatus != VaultStatus.PANIC, "Forbidden");
_;
}
}
/**
* Called once by factory directly after cloning to initialize vault.
* @param _newOwner the vaults's owner address
* @param _factoryAddress used to manage recovery from panic state aka "undo a hack"
* @param _feeSplitterContract the [contract] address to send fees
*/
function initialize(
address _newOwner,
address _factoryAddress,
IFeeSplitter _feeSplitterContract
)
initializer
public
{
owner = _newOwner;
_capsuleFactory = ITimeCapsuleFactory(_factoryAddress);
_deadmanTime = (uint64)(block.timestamp);
_vaultStatus = VaultStatus.NOMINAL;
_feeSplitter = _feeSplitterContract;
}
/**
* When native coin is sent directly to this vault (as if it were a standard wallet)
* the funds are auto-locked for seven days (and the fee is paid accordingly.) This is
* intended as a safety feature and not to be advertized as good practice.
*/
receive() external payable { }
event Locked(
bytes32 tag,
address tokenAddress,
uint256 lockIndex,
uint256 amount,
uint256 fee,
uint256 lockTime,
uint256 unlockTime,
LockType lockType
);
event WithdrawalInitiated(
address tokenAddress,
uint256 lockIndex,
uint256 amount
);
event WithdrawalCancelled(
address tokenAddress,
uint256 lockIndex
);
event Withdrawal(
address tokenAddress,
uint256 lockIndex,
uint256 amount
);
event LockReleased(
address tokenAddress,
uint256 lockIndex
);
event RecoveryInitiated();
event Recovered();
error HardLocked(
uint64 unlockTime
);
error InsufficientFunds(
uint256 grossBalance,
uint256 alreadyLocked,
uint256 availableToLock
);
/**
* Transfers nativer coin from the vault to a recipient.
* @notice Do not use simple trasfer(<maxgas: 2300!>) — Gnosis safe (for example) infamously incompatible
* @param _to recipient address
* @param _value base unit amount to transfer
*/
function _transferNative(
address _to,
uint256 _value
)
internal
returns (bool success)
{
(success, ) = _to.call{ value: _value }("");
}
/**
* Transfers 'RC20 tokens from the vault to a recipient.
* @notice internal function
* @param _tokenAddress 'RC20 token contract address
* @param _to recipient address
* @param _value base unit amount to transfer
* @return bool
*/
function _transferToken(
address _tokenAddress,
address _to,
uint256 _value
)
internal
returns (bool)
{
return IERC20(_tokenAddress).transfer(
_to,
_value
);
}
/**
* Returns the current "state" of the vault. See `enum VaultStatus {...}`
* @return status enum VaultStatus.<NOMINAL|PANIC|RECOVERED>
*/
function vaultStatus()
public
view
returns (VaultStatus status)
{
status = _vaultStatus;
}
/**
* Validates recovery address. Validation is required as a separate step to account for possibily
* of mistake or bad actor using the same recovery address as an attempt to 'block' geniune use.
* @param _recoveryAddressHash keccak256 (sha3) hash of recovery address (so as not to 'dox' the address)
* @param _signature personal_sign of the string: "CONFIRMING RECOVERY ADDRESS"
*/
function validateRecoveryAddressHash(
bytes32 _recoveryAddressHash,
bytes memory _signature
)
public
_onlyCapsuleOwner
_notPanicState
{
_capsuleFactory.validateRecoveryAddressHash(
owner,
_recoveryAddressHash,
_signature
);
}
/**
* Used to test if a given recovery address hash is valid for (matches) the hash stored for this vault.
* @param _recoveryAddressHash keccak256 hash of the recovery address / public key
* @return confirmed bool
*/
function checkRecoveryAddress(bytes32 _recoveryAddressHash)
public
view
returns (bool confirmed)
{
confirmed = _capsuleFactory.checkRecoveryAddress(owner, _recoveryAddressHash);
}
/**
* Returns true if tha vault's recovery address hash has been validated — see validateRecoveryAddressHash(...).
* @return validated bool
*/
function isRecoveryHashValidated()
public view
returns (bool validated)
{
return _capsuleFactory.isRecoveryHashValidated(owner);
}
/**
* Returns the number of active locks for a given token contract address (or address(0) for
* native coin.) Used in conjunction with lockData() to retrieve list of locks for a spcific token.
* @param _tokenAddress 'RC2- contract address
* @return count number of active locks for the specified token
*/
function lockCount(address _tokenAddress)
public
view
returns (uint256 count)
{
count = _locks[_tokenAddress].length;
}
/**
* Returns Lock record for an existing lock.
* @param _tokenAddress 'RC20 token contract address
* @param _index lock index
* @return lock Lock record
*/
function lockData(
address _tokenAddress,
uint256 _index
)
public
view
returns (Lock memory lock)
{
Lock[] memory locks = _locks[_tokenAddress];
require(locks.length > 0,
"No locks for given token address"
);
require(locks.length > _index,
"Invalid lock index"
);
lock = locks[_index];
}
/**
* Returns available/lockable balance (owned by this vault and not tied up in locks)
* @param _tokenAddress token contract address or address(0) for native coin
*/
function availableBalance(
address _tokenAddress // use address(0) for native coin
)
public
view
returns (uint256 available)
{
available = 0;
uint256 _grossBalance;
if (_tokenAddress == NATIVE_COIN) {
_grossBalance = address(this).balance;
} else {
require(
_tokenAddress.code.length > 0,
"not a token"
);
_grossBalance = IERC20(_tokenAddress).balanceOf(address(this));
}
if (_grossBalance > _totalLockedAmount[_tokenAddress]) {
available = _grossBalance - _totalLockedAmount[_tokenAddress];
}
}
/**
* @notice internal function
* @param _tag bytes32String: up to 31 characters of user input as a label for the lock
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockAmount unit value of amount to be locked
* @param _lockTime UTC unix timestamp (seconds)
* @param _unlockTime UTC unix timestamp (seconds)
* @param _lockType enum LockType.<DRIP|HARD|BEHAVIORAL>
* @return lockIndex array index of created lock
*/
function _storeNewLock(
bytes32 _tag,
address _tokenAddress,
uint256 _lockAmount,
uint64 _lockTime,
uint64 _unlockTime,
LockType _lockType
)
internal
returns (uint256 lockIndex)
{
Lock[] storage locksRef = _locks[_tokenAddress];
Lock memory lock = Lock({
state: LockState.INERT,
tag: _tag,
lockTime: _lockTime,
unlockTime: _unlockTime,
lockType: _lockType,
unlockHash: 0,
lockedAmount: _lockAmount,
withdrawnToDate: 0,
timelockedAmount: 0,
releaseTime: 0
});
locksRef.push(lock);
lockIndex = locksRef.length - 1;
}
/**
* @notice internal function
* @param _tag bytes32String: up to 31 characters of user input as a label for the lock (may be "")
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockAmount unit value of amount to be locked
* @param _lockTime UTC unix timestamp (seconds)
* @param _unlockTime UTC unix timestamp (seconds)
* @param _lockType LockType.<DRIP|HARD|BEHAVIORAL>
* @return lockIndex array index of created lock
*/
function _createLock(
bytes32 _tag,
address _tokenAddress,
uint256 _lockAmount,
uint256 _fee,
uint64 _lockTime, // UTC unix timestamp (seconds)
uint64 _unlockTime, // UTC unix timestamp (seconds)
LockType _lockType
)
internal
returns (uint256 lockIndex)
{
require(_lockTime >= (uint64)(block.timestamp) - (6 * ONE_HOUR),
"Lock time must not be more than six hours in the past"
);
require(_unlockTime > _lockTime,
"Unlock time must be greater than lock time"
);
require((_unlockTime - _lockTime) < MAX_LOCK_SECONDS, // built-in "safe math" solidity@>0.8.0
"Unlock time must be within 55 years of lock time"
);
unchecked { // division underflow acceptable (decimal truncation)
uint256 _feeCheck = _lockAmount / FEE_DIVISOR;
require(_feeCheck == _fee,
"Fee calc audit failed"
);
}
if (_tokenAddress == NATIVE_COIN) { // native coin
uint256 _availableToLock = availableBalance(_tokenAddress); // any msg.value is already included in balance
if (_availableToLock < (_lockAmount + _fee)) {
revert InsufficientFunds(
address(this).balance - msg.value, // grossBalance (before msg.value, being 'refunded' by this revert)
_totalLockedAmount[_tokenAddress], // alreadyLocked
_availableToLock // availableToLock (includes any incoming msg.value (now being reverted))
);
}
if (_fee > 0) {
(bool feeTransferred, ) = address(_feeSplitter).call{value: _fee}("");
require(
feeTransferred,
"Fee transfer rejected by external contract"
);
}
} else { // 'RC20
IERC20 tokenContract = IERC20(_tokenAddress);
uint256 _availableToLock = availableBalance(_tokenAddress);
if (_availableToLock < (_lockAmount + _fee)) {
uint256 _tokenBalance = IERC20(tokenContract).balanceOf(address(this));
revert InsufficientFunds(
_tokenBalance, // grossBalance
_totalLockedAmount[_tokenAddress], // alreadyLocked
_availableToLock // availableToLock)
);
}
if (_fee > 0) {
bool feeTransferred = IERC20(tokenContract).transfer(
address(_feeSplitter),
_fee
);
require(
feeTransferred,
"Fee transfer rejected by external contract"
);
require(
// @dev splitERC20 splits and sends on the _feeSplitter's entire current balance
_feeSplitter.splitERC20(address(tokenContract)),
"Fee split callout failed"
);
}
}
lockIndex = _storeNewLock(
_tag,
_tokenAddress,
_lockAmount,
_lockTime,
_unlockTime,
_lockType
);
_totalLockedAmount[_tokenAddress] += _lockAmount;
// update 'watchdog' deadman timer
if (_deadmanTime < _unlockTime) _deadmanTime = _unlockTime;
emit Locked(
_tag,
_tokenAddress,
lockIndex,
_lockAmount,
_fee,
_lockTime,
_unlockTime,
_lockType
);
}
/**
* Creates a new standard (LockType.DRIP) time lock.
* @param _tag bytes32String: up to 31 characters of user input as a label for the lock (may be "")
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockAmount unit value of amount to be locked
* @param _lockTime UTC unix timestamp (seconds)
* @param _unlockTime UTC unix timestamp (seconds)
* @return lockIndex array index of created lock
*/
function createLock(
bytes32 _tag,
address _tokenAddress,
uint256 _lockAmount,
uint256 _fee,
uint64 _lockTime,
uint64 _unlockTime
)
public
payable
_onlyCapsuleOwner
_notPanicState
returns (uint256 lockIndex)
{
lockIndex = _createLock(
_tag,
_tokenAddress,
_lockAmount,
_fee,
_lockTime,
_unlockTime,
LockType.DRIP
);
}
/**
* Creates a new hard lock (LockType.HARD) — a lock that does not drip and must complete full term before withdrawal.
* @param _tag bytes32String: up to 31 characters of user input as a label for the lock (may be "")
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockAmount unit value of amount to be locked
* @param _lockTime UTC unix timestamp (seconds)
* @param _unlockTime UTC unix timestamp (seconds)
* @return lockIndex array index of created lock
*/
function createHardLock(
bytes32 _tag,
address _tokenAddress,
uint256 _lockAmount,
uint256 _fee,
uint64 _lockTime,
uint64 _unlockTime
)
public
payable
_onlyCapsuleOwner
_notPanicState
returns (uint256 lockIndex)
{
lockIndex = _createLock(
_tag,
_tokenAddress,
_lockAmount,
_fee,
_lockTime,
_unlockTime,
LockType.HARD
);
}
/**
* Creates a behavioral lock (LockType.BAHVIORAL) which lasts up to a maximum of ONE YEAR from _lockStart
* or immediately released when the correct unlock code is passed to releaseBehaviorLock().
* @notice UI implementations should limit the unlock code to a simple six digit number.
* @param _tag bytes32String: up to 31 characters of user input as a label for the lock (may be "")
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockAmount unit value of amount to be locked
* @param _lockTime UTC unix timestamp (seconds)
* @param _unlockHash first six bytes of a keccak256 (sha3) of a 'secret' unlock text (ideally UI limited to simple 6 digits)
* @return lockIndex array index of created lock
*/
/// @dev we consider it unsafe to trust a miner's clock for lock/unlock times
function createBehaviorLock(
bytes32 _tag,
address _tokenAddress,
uint256 _lockAmount,
uint256 _fee,
uint64 _lockTime,
bytes6 _unlockHash
)
public
payable
_onlyCapsuleOwner
_notPanicState
returns (uint256 lockIndex)
{
lockIndex = _createLock(
_tag,
_tokenAddress,
_lockAmount,
_fee,
_lockTime,
_lockTime + BEHAVIORLOCK_SECONDS,
LockType.BEHAVIORAL
);
_locks[_tokenAddress][lockIndex].unlockHash = _unlockHash;
}
/**
* Intended for a bahioral lock 'friend' to check that the code they have is valid for a given lock.
* @notice Behavioral locks are only minimally secure *intentionally*. The user who created
* the lock may 'cheat' or 'fail to achieve their goal'. Thus for example, potential use
* of this function to brute force the inlock code, while discouraged, is an acceptable
* trade-off. After all, it is the vault owner's money and only their own self worth at stake.
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockIndex lock index
* @param _unlockCode clear text unlock code
* @return correct bool
*/
function checkBehaviorLockCode(
address _tokenAddress,
uint256 _lockIndex,
string memory _unlockCode
)
public
view
returns (bool correct)
{
bytes32 hashedUnlockCode = keccak256(abi.encodePacked(_unlockCode));
correct = bytes6(hashedUnlockCode) == _locks[_tokenAddress][_lockIndex].unlockHash;
}
/**
* Releases funds from a behavior lock if; a) given correct code OR b) a year has passed since the lock was cretaed.
* @notice Funds are return to the vault's lockable balance. Withdrawal from the vault requires a new lock be created
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockIndex lock index (for this token)
* @param _unlockCode clear text 'secret' unlock code (cAse senstitve)
*/
function claimBehaviorLock(
address _tokenAddress,
uint256 _lockIndex,
string memory _unlockCode
)
public
payable
_onlyCapsuleOwner
_notPanicState
{
if (_locks[_tokenAddress][_lockIndex].lockTime > uint64(block.timestamp) - BEHAVIORLOCK_SECONDS) {
require(
checkBehaviorLockCode(_tokenAddress, _lockIndex, _unlockCode) == true,
"Invalid code"
);
}
_totalLockedAmount[_tokenAddress] -= _locks[_tokenAddress][_lockIndex].lockedAmount;
_locks[_tokenAddress][_lockIndex].unlockTime = uint64(block.timestamp);
releaseLock(_tokenAddress, _lockIndex);
}
/**
* Returns value of token or coin realeasable NOW — aka the current DRIP amount.
* @notice The actual value released by initiateWithdrawal() may differ — slightly higher.
* However, making this a public function allows users to get an idea of current drip
* amounts.
* @param _tokenAddress token contract address or address(0) for native coin
* @param _index lock index
* @return withdrawalAllowance currently available withdrawable balance (drip amount)
*/
function calculateAllowance(
address _tokenAddress,
uint256 _index
)
public
view
returns (uint256 withdrawalAllowance)
{
Lock memory lock = lockData(_tokenAddress, _index);
if (lock.withdrawnToDate >= lock.lockedAmount) {
withdrawalAllowance = 0;
} else if (block.timestamp < (lock.lockTime - ONE_HOUR) || block.timestamp >= lock.unlockTime) {
if (lock.withdrawnToDate >= lock.lockedAmount) {
withdrawalAllowance = 0;
} else {
withdrawalAllowance = lock.lockedAmount - lock.withdrawnToDate;
}
} else {
uint256 lockTerm = lock.unlockTime - lock.lockTime;
uint256 termServed = ((block.timestamp - lock.lockTime) * 10000 ) / lockTerm ; // 10000 => 100.00% of term
uint256 totalPortionFromStart = lock.lockedAmount * termServed;
unchecked { // division undeflow acceptable (discard remainer)
totalPortionFromStart /= 10000;
}
if (lock.withdrawnToDate >= totalPortionFromStart) withdrawalAllowance = 0;
else withdrawalAllowance = totalPortionFromStart - lock.withdrawnToDate;
// Web UI uses this function to display currently available "drip" amounts. So, we
// account for a pending withdrawal by not including that amount in the returned allowance.
if (lock.timelockedAmount >= withdrawalAllowance) withdrawalAllowance = 0;
else withdrawalAllowance -= lock.timelockedAmount;
}
}
/**
* Releases any remaining balance in a lock without need of withdrawal.
* Reverts if unlock time not yet reached.
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockIndex lock index (for this token)
* @custom:emits LockReleased(tokenAddress, lockIndex)
*/
function releaseLock(
address _tokenAddress,
uint256 _lockIndex
)
internal
{
Lock[] storage locksRef = _locks[_tokenAddress];
Lock storage lock = locksRef[_lockIndex];
require (
block.timestamp >= lock.unlockTime,
"Before unlock time"
);
// gas efficient removal of array element
uint256 lastIndex = locksRef.length - 1;
if (_lockIndex != lastIndex) locksRef[_lockIndex] = locksRef[lastIndex];
locksRef.pop();
emit LockReleased(
_tokenAddress,
_lockIndex
);
}
/**
* Initiates a seven day withdrawal timelock, after which sendWithdrawal() can be
* called to retrieve the funds.
* @notice Only ONE withdrawl may be active at a time for each token lock. (See `cancelWithdrawal()`, bellow.)
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockIndex lock index (for this token)
* @custom:emits WithdrawalInitiated(tokenAddress, lockIndex, withdrawalAllowance)
*/
function initiateWithdrawal(
address _tokenAddress,
uint256 _lockIndex
)
public
_onlyCapsuleOwner
_notPanicState
returns (uint256 amount)
{
Lock[] storage locksRef = _locks[_tokenAddress];
require(locksRef.length > 0,
"Lock not found"
);
require(locksRef.length > _lockIndex,
"Invalid lock index"
);
require(locksRef[_lockIndex].state != LockState.WITHDRAWAL_PENDING,
"Withdrawal already pending"
);
Lock storage lock = locksRef[_lockIndex];
require(
lock.lockType != LockType.BEHAVIORAL,
"Behavioral lock"
);
if (lock.lockType == LockType.HARD && uint64(block.timestamp) < lock.unlockTime) {
revert HardLocked({
unlockTime: lock.unlockTime
});
}
uint256 withdrawalAllowance = calculateAllowance(
_tokenAddress,
_lockIndex
);
require(withdrawalAllowance > 0,
"None available"
);
lock.state = LockState.WITHDRAWAL_PENDING;
lock.timelockedAmount = withdrawalAllowance;
lock.releaseTime = uint64(block.timestamp) + TIMELOCK_SECONDS;
emit WithdrawalInitiated(
_tokenAddress,
_lockIndex,
withdrawalAllowance
);
amount = withdrawalAllowance;
}
/**
* Cancels a pending withdrawal
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockIndex lock index (for this token)
* @custom:emits WithdrawalCancelled(tokenAddress, lockIndex)
*/
function cancelWithdrawal(
address _tokenAddress,
uint256 _lockIndex
)
public
_onlyCapsuleOwner
{
Lock[] storage locks = _locks[_tokenAddress];
require(locks.length > 0,
"No matching lock found"
);
require(locks.length > _lockIndex,
"Invalid lock index"
);
Lock storage lock = locks[_lockIndex];
require(lock.state == LockState.WITHDRAWAL_PENDING,
"No withdrawal pending"
);
lock.timelockedAmount = 0;
lock.state = LockState.INERT;
emit WithdrawalCancelled(
_tokenAddress,
_lockIndex
);
}
/**
* Sends a pending seven day timelocked withdrawal to the vault's owner (reverts if not yet time)
* @param _tokenAddress token contract address or address(0) for native coin
* @param _lockIndex lock index (for this token)
* @custom:emits Withdrawal(tokenAddress, lockIndex, withdrawalAmount)
*/
function sendWithdrawal(
address _tokenAddress,
uint256 _lockIndex
)
public
_onlyCapsuleOwner
_notPanicState
{
Lock[] storage locksRef = _locks[_tokenAddress];
Lock storage lock = locksRef[_lockIndex];
require(
lock.state == LockState.WITHDRAWAL_PENDING,
"No withdrawal pending"
);
require(
lock.timelockedAmount > 0,
"No withdrawal pending"
);
require(
block.timestamp >= lock.releaseTime,
"Timelocked"
);
/**
* @dev !IMPORTANT!: Clear the balance owed BEFORE calling sender's
* potential contract address to avoid ye olde DAO dance (reentrance hack)
*/
uint256 withdrawalAmount = lock.timelockedAmount;
lock.timelockedAmount = 0;
lock.withdrawnToDate += withdrawalAmount;
bool withdrawalTransferred = (_tokenAddress == NATIVE_COIN)
? _transferNative( owner, withdrawalAmount )
: _transferToken( _tokenAddress, owner, withdrawalAmount )
;
require (
withdrawalTransferred,
"Withdrawal transfer failed"
);
_totalLockedAmount[_tokenAddress] -= withdrawalAmount;
// check for last (emptying) withdrawal
if (lock.withdrawnToDate >= lock.lockedAmount) {
releaseLock(_tokenAddress, _lockIndex);
} else {
lock.releaseTime = 0; // gas discount
lock.state = LockState.INERT;
}
emit Withdrawal(
_tokenAddress,
_lockIndex,
withdrawalAmount
);
}
/**
* 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 message hash
* @param _messageHash keccak256 (sha3) hash of the message signed
* @param _signature 65 byte (130 nibble) signature
*/
function __recoverSignerAddress(
bytes32 _messageHash,
bytes memory _signature
)
private
pure
returns (address signerAddress)
{
(bytes32 r,bytes32 s, uint8 v) = _splitSignature(_signature);
signerAddress = ecrecover(_messageHash, v, r, s);
}
/**
* 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 INVALID_SIGNER_ADDRESS;
bytes32 _messageHash = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n",
Strings.toString(bytes(_message).length),
bytes(_message)
)
);
signerAddress = __recoverSignerAddress(_messageHash, _signature);
}
/**
* @notice internal function
*/
function _panic() internal {
require(
_vaultStatus == VaultStatus.NOMINAL,
"Panic is once only. Good luck!"
);
require(
_capsuleFactory.isRecoveryHashValidated(owner) == true,
"No recovery address"
);
_vaultStatus = VaultStatus.PANIC;
emit RecoveryInitiated();
}
/**
* Places the vault into PANIC state. NON-REVERSIBLE — vault must be recovered to.
* NON-REVERSIBLE — vault must be "recovered" to regain functional access — see recoverVault()
* @notice Two versions — see also panic(bytes memory _signature)
*/
function panic() public _onlyCapsuleOwner {
_panic();
}
/**
* Allows anyone with an off-chain signed message from the original owner to place
* the vault into PANIC state. NON-REVERSIBLE — vault must be "recovered" to regain
* functional access — see recoverVault()
* @notice Two versions — see also panic()
*/
function panic(
bytes memory _signature
)
public
{
address signerAddress = _recoverSignerAddress(VAULT_RECOVERY_AUTHORIZATION, _signature);
require(signerAddress == owner, "Invalid signature");
_panic();
}
/**
* Recovers a vault from PANIC state — see panic() & panic(bytes memory _signature. (Also known as "undo a hack".)
* Recovery can only be executed by the recovery address, which will become the vault's effective new owner.
* The PANIC / RECOVERY process can only be executed ONCE. Don't get hacked again!
* @notice internal function
* @param _originalOwner address of original owner
*/
function _recoverVault(
address _originalOwner
)
internal
{
address _newOwner = msg.sender; // set local owner variable to new owner
// update the factory contract's recovery and owner address mappings
// reverts if vault's recoveryAddressHash does not match keccak256(_newOwner)
_capsuleFactory.recoverOwnership(
_originalOwner,
_newOwner
);
owner = _newOwner;
_vaultStatus = VaultStatus.RECOVERED;
emit Recovered();
}
/**
* Recovers an EXPIRED vault — one that has not had any lock/unlock activity for
* a full year after the latest (most future) lock end time ever created.
* @notice Only the recovery account can do this.
* @param _originalOwner original owner's address
*/
function recoverExpiredVault(
address _originalOwner
)
public
{
uint64 deadmanRecoveryTime = _deadmanTime + ONE_YEAR; // latest ever unlockTime plus one year
uint64 blockTime = (uint64)(block.timestamp);
require(blockTime >= deadmanRecoveryTime, "Vault not expired");
_recoverVault(_originalOwner);
}
/**
* Recovers a vault from PANIC state, passing ownership to the caller.
* The vault must be in PANIC state and caller must be the recovery address
* itself (according to stored recoveryAddressHash.)
**/
function recoverVault(
address _originalOwner
)
public
{
require(_vaultStatus == VaultStatus.PANIC, "Forbidden");
_recoverVault(_originalOwner);
}
}
Contract ABI
[{"type":"constructor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"available","internalType":"uint256"}],"name":"availableBalance","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"withdrawalAllowance","internalType":"uint256"}],"name":"calculateAllowance","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_index","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelWithdrawal","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"correct","internalType":"bool"}],"name":"checkBehaviorLockCode","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockIndex","internalType":"uint256"},{"type":"string","name":"_unlockCode","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"confirmed","internalType":"bool"}],"name":"checkRecoveryAddress","inputs":[{"type":"bytes32","name":"_recoveryAddressHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"claimBehaviorLock","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockIndex","internalType":"uint256"},{"type":"string","name":"_unlockCode","internalType":"string"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"lockIndex","internalType":"uint256"}],"name":"createBehaviorLock","inputs":[{"type":"bytes32","name":"_tag","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockAmount","internalType":"uint256"},{"type":"uint256","name":"_fee","internalType":"uint256"},{"type":"uint64","name":"_lockTime","internalType":"uint64"},{"type":"bytes6","name":"_unlockHash","internalType":"bytes6"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"lockIndex","internalType":"uint256"}],"name":"createHardLock","inputs":[{"type":"bytes32","name":"_tag","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockAmount","internalType":"uint256"},{"type":"uint256","name":"_fee","internalType":"uint256"},{"type":"uint64","name":"_lockTime","internalType":"uint64"},{"type":"uint64","name":"_unlockTime","internalType":"uint64"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"lockIndex","internalType":"uint256"}],"name":"createLock","inputs":[{"type":"bytes32","name":"_tag","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockAmount","internalType":"uint256"},{"type":"uint256","name":"_fee","internalType":"uint256"},{"type":"uint64","name":"_lockTime","internalType":"uint64"},{"type":"uint64","name":"_unlockTime","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_newOwner","internalType":"address"},{"type":"address","name":"_factoryAddress","internalType":"address"},{"type":"address","name":"_feeSplitterContract","internalType":"contract IFeeSplitter"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"}],"name":"initiateWithdrawal","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isInitialized","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"validated","internalType":"bool"}],"name":"isRecoveryHashValidated","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"count","internalType":"uint256"}],"name":"lockCount","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"lock","internalType":"struct TimeCapsuleContext.Lock","components":[{"type":"uint8"},{"type":"bytes32"},{"type":"uint64"},{"type":"uint64"},{"type":"uint64"},{"type":"uint8"},{"type":"bytes6"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"}]}],"name":"lockData","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_index","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"panic","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"panic","inputs":[{"type":"bytes","name":"_signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverExpiredVault","inputs":[{"type":"address","name":"_originalOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverVault","inputs":[{"type":"address","name":"_originalOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"sendWithdrawal","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_lockIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"validateRecoveryAddressHash","inputs":[{"type":"bytes32","name":"_recoveryAddressHash","internalType":"bytes32"},{"type":"bytes","name":"_signature","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"status","internalType":"enum TimeCapsuleContext.VaultStatus"}],"name":"vaultStatus","inputs":[]},{"type":"event","name":"Initialized","inputs":[{"type":"uint8","name":"version","indexed":false}],"anonymous":false},{"type":"event","name":"LockReleased","inputs":[{"type":"address","name":"tokenAddress","indexed":false},{"type":"uint256","name":"lockIndex","indexed":false}],"anonymous":false},{"type":"event","name":"Locked","inputs":[{"type":"bytes32","name":"tag","indexed":false},{"type":"address","name":"tokenAddress","indexed":false},{"type":"uint256","name":"lockIndex","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"fee","indexed":false},{"type":"uint256","name":"lockTime","indexed":false},{"type":"uint256","name":"unlockTime","indexed":false},{"type":"uint8","name":"lockType","indexed":false}],"anonymous":false},{"type":"event","name":"Recovered","inputs":[],"anonymous":false},{"type":"event","name":"RecoveryInitiated","inputs":[],"anonymous":false},{"type":"event","name":"Withdrawal","inputs":[{"type":"address","name":"tokenAddress","indexed":false},{"type":"uint256","name":"lockIndex","indexed":false},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawalCancelled","inputs":[{"type":"address","name":"tokenAddress","indexed":false},{"type":"uint256","name":"lockIndex","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawalInitiated","inputs":[{"type":"address","name":"tokenAddress","indexed":false},{"type":"uint256","name":"lockIndex","indexed":false},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"error","name":"HardLocked","inputs":[{"type":"uint64","name":"unlockTime","internalType":"uint64"}]},{"type":"error","name":"InsufficientFunds","inputs":[{"type":"uint256","name":"grossBalance","internalType":"uint256"},{"type":"uint256","name":"alreadyLocked","internalType":"uint256"},{"type":"uint256","name":"availableToLock","internalType":"uint256"}]},{"type":"receive"}]
Contract Creation Code
0x60806040523480156200001157600080fd5b506200001c62000022565b620000e4565b600054610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161015620000e2576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6132be80620000f46000396000f3fe6080604052600436106101445760003560e01c80636aeaec52116100b6578063c43d84491161006f578063c43d84491461037d578063c8393ba91461039d578063cb47f279146103bd578063df034586146103dd578063eff41bd614610413578063f76ac6931461043357600080fd5b80636aeaec52146102ac5780638da5cb5b146102bf578063a0821be3146102fd578063a4b8e7a41461031d578063be476d8a1461033d578063c0c53b8b1461035d57600080fd5b80634700d305116101085780634700d3051461021057806349f59b5714610225578063617b3ab914610246578063622d1cf9146102595780636233b70b1461026c578063645630211461028c57600080fd5b806324a8166e1461015057806331099906146101805780633801f84f146101a5578063392e53cd146101c75780633eff851c146101e357600080fd5b3661014b57005b600080fd5b34801561015c57600080fd5b50600454600160401b900460ff166040516101779190612ac8565b60405180910390f35b34801561018c57600080fd5b50610195610453565b6040519015158152602001610177565b3480156101b157600080fd5b506101c56101c0366004612b8d565b6104d0565b005b3480156101d357600080fd5b5061019560005460ff1660011490565b3480156101ef57600080fd5b506102036101fe366004612be8565b6105cb565b6040516101779190612c31565b34801561021c57600080fd5b506101c56107f4565b610238610233366004612d13565b610835565b604051908152602001610177565b610238610254366004612d74565b6108cf565b610238610267366004612d13565b6109db565b34801561027857600080fd5b506101c5610287366004612de6565b610a68565b34801561029857600080fd5b506102386102a7366004612be8565b610af0565b6101c56102ba366004612e03565b610c52565b3480156102cb57600080fd5b506000546102e5906201000090046001600160a01b031681565b6040516001600160a01b039091168152602001610177565b34801561030957600080fd5b50610238610318366004612de6565b610e74565b34801561032957600080fd5b506101c5610338366004612de6565b610f8b565b34801561034957600080fd5b506101c5610358366004612be8565b610fd4565b34801561036957600080fd5b506101c5610378366004612e6f565b61113d565b34801561038957600080fd5b50610195610398366004612e03565b6112ad565b3480156103a957600080fd5b506102386103b8366004612be8565b611344565b3480156103c957600080fd5b506101956103d8366004612eba565b6116bc565b3480156103e957600080fd5b506102386103f8366004612de6565b6001600160a01b031660009081526002602052604090205490565b34801561041f57600080fd5b506101c561042e366004612ed3565b61173b565b34801561043f57600080fd5b506101c561044e366004612be8565b6117de565b6001546000805460405163fd9643a560e01b81526001600160a01b0362010000909204821660048201529192169063fd9643a590602401602060405180830381865afa1580156104a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cb9190612f07565b905090565b3033146105c7576000546201000090046001600160a01b031633146105105760405162461bcd60e51b815260040161050790612f29565b60405180910390fd5b3033146105c7576001600454600160401b900460ff16600281111561053757610537612aa2565b036105545760405162461bcd60e51b815260040161050790612f4c565b600154600054604051631cb40a9760e31b81526001600160a01b039283169263e5a054b89261059492620100009091049091169086908690600401612f93565b600060405180830381600087803b1580156105ae57600080fd5b505af11580156105c2573d6000803e3d6000fd5b505050505b5050565b604080516101408101825260008082526020808301829052828401829052606083018290526080830182905260a0830182905260c0830182905260e08301829052610100830182905261012083018290526001600160a01b0386168252600281528382208054855181840281018401909652808652939492939091849084015b8282101561075a57600084815260209020604080516101408101909152600684029091018054829060ff16600181111561068757610687612aa2565b600181111561069857610698612aa2565b8152600182015460208201526002808301546001600160401b038082166040850152600160401b820481166060850152600160801b820416608084015260a090920191600160c01b900460ff16908111156106f5576106f5612aa2565b600281111561070657610706612aa2565b815260028201546001600160d01b0319600160c81b90910460d01b1660208083019190915260038301546040830152600483015460608301526005909201546080909101529082526001909201910161064b565b50505050905060008151116107b15760405162461bcd60e51b815260206004820181905260248201527f4e6f206c6f636b7320666f7220676976656e20746f6b656e20616464726573736044820152606401610507565b828151116107d15760405162461bcd60e51b815260040161050790612fdc565b8083815181106107e3576107e3613008565b602002602001015191505092915050565b303314610833576000546201000090046001600160a01b0316331461082b5760405162461bcd60e51b815260040161050790612f29565b610833611ac6565b565b60003033146108c5576000546201000090046001600160a01b0316331461086e5760405162461bcd60e51b815260040161050790612f29565b3033146108c5576001600454600160401b900460ff16600281111561089557610895612aa2565b036108b25760405162461bcd60e51b815260040161050790612f4c565b6108c28787878787876001611c2e565b90505b9695505050505050565b60003033146108c5576000546201000090046001600160a01b031633146109085760405162461bcd60e51b815260040161050790612f29565b3033146108c5576001600454600160401b900460ff16600281111561092f5761092f612aa2565b0361094c5760405162461bcd60e51b815260040161050790612f4c565b61097487878787876109636201518061016d613034565b61096d908a613063565b6002611c2e565b6001600160a01b038716600090815260026020526040902080549192508391839081106109a3576109a3613008565b906000526020600020906006020160020160196101000a81548165ffffffffffff021916908360d01c02179055509695505050505050565b60003033146108c5576000546201000090046001600160a01b03163314610a145760405162461bcd60e51b815260040161050790612f29565b3033146108c5576001600454600160401b900460ff166002811115610a3b57610a3b612aa2565b03610a585760405162461bcd60e51b815260040161050790612f4c565b6108c28787878787876000611c2e565b6000610a796201518061016d613034565b600454610a8f91906001600160401b0316613063565b9050426001600160401b038083169082161015610ae25760405162461bcd60e51b815260206004820152601160248201527015985d5b1d081b9bdd08195e1c1a5c9959607a1b6044820152606401610507565b610aeb83612223565b505050565b600080610afd84846105cb565b90508060e0015181610100015110610b185760009150610c4b565b610e108160400151610b2a9190613083565b6001600160401b0316421080610b4d575080606001516001600160401b03164210155b15610b87578060e0015181610100015110610b6b5760009150610c4b565b8061010001518160e00151610b8091906130a3565b9150610c4b565b600081604001518260600151610b9d9190613083565b6001600160401b0316905060008183604001516001600160401b031642610bc491906130a3565b610bd0906127106130b6565b610bda91906130d5565b90506000818460e00151610bee91906130b6565b9050612710810490508084610100015110610c0c5760009450610c1f565b610100840151610c1c90826130a3565b94505b8484610120015110610c345760009450610c47565b610120840151610c4490866130a3565b94505b5050505b5092915050565b303314610aeb576000546201000090046001600160a01b03163314610c895760405162461bcd60e51b815260040161050790612f29565b303314610aeb576001600454600160401b900460ff166002811115610cb057610cb0612aa2565b03610ccd5760405162461bcd60e51b815260040161050790612f4c565b610cdc6201518061016d613034565b610ce69042613083565b6001600160401b031660026000856001600160a01b03166001600160a01b031681526020019081526020016000208381548110610d2557610d25613008565b60009182526020909120600260069092020101546001600160401b03161115610d9357610d538383836112ad565b1515600114610d935760405162461bcd60e51b815260206004820152600c60248201526b496e76616c696420636f646560a01b6044820152606401610507565b6001600160a01b0383166000908152600260205260409020805483908110610dbd57610dbd613008565b90600052602060002090600602016003015460036000856001600160a01b03166001600160a01b031681526020019081526020016000206000828254610e0391906130a3565b90915550506001600160a01b0383166000908152600260205260409020805442919084908110610e3557610e35613008565b906000526020600020906006020160020160086101000a8154816001600160401b0302191690836001600160401b03160217905550610aeb83836122ef565b6000806001600160a01b038316610e8c575047610f3f565b6000836001600160a01b03163b11610ed45760405162461bcd60e51b815260206004820152600b60248201526a3737ba1030903a37b5b2b760a91b6044820152606401610507565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610f18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3c91906130f7565b90505b6001600160a01b038316600090815260036020526040902054811115610f85576001600160a01b038316600090815260036020526040902054610f8290826130a3565b91505b50919050565b6001600454600160401b900460ff166002811115610fab57610fab612aa2565b14610fc85760405162461bcd60e51b815260040161050790612f4c565b610fd181612223565b50565b3033146105c7576000546201000090046001600160a01b0316331461100b5760405162461bcd60e51b815260040161050790612f29565b6001600160a01b0382166000908152600260205260409020805461106a5760405162461bcd60e51b8152602060048201526016602482015275139bc81b585d18da1a5b99c81b1bd8dac8199bdd5b9960521b6044820152606401610507565b8054821061108a5760405162461bcd60e51b815260040161050790612fdc565b600081838154811061109e5761109e613008565b6000918252602090912060069091020190506001815460ff1660018111156110c8576110c8612aa2565b146110e55760405162461bcd60e51b815260040161050790613110565b60006005820155805460ff19168155604080516001600160a01b0386168152602081018590527f2eed97477f07c07ec48f8f678f4e84f7c0de55bf33f51c3dc989b1335308031991015b60405180910390a150505050565b600054610100900460ff161580801561115d5750600054600160ff909116105b806111775750303b158015611177575060005460ff166001145b6111da5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610507565b6000805460ff1916600117905580156111fd576000805461ff0019166101001790555b6000805462010000600160b01b031916620100006001600160a01b038781169190910291909117909155600180546001600160a01b03191685831617905560048054918416600160481b026001600160e81b0319909216426001600160401b03161791909117905580156112a7576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161112f565b50505050565b600080826040516020016112c1919061313f565b60405160208183030381529060405280519060200120905060026000866001600160a01b03166001600160a01b03168152602001908152602001600020848154811061130f5761130f613008565b6000918252602090912060069091020160020154600160c81b900460d01b6001600160d01b0319908116911614949350505050565b60003033146116b6576000546201000090046001600160a01b0316331461137d5760405162461bcd60e51b815260040161050790612f29565b3033146116b6576001600454600160401b900460ff1660028111156113a4576113a4612aa2565b036113c15760405162461bcd60e51b815260040161050790612f4c565b6001600160a01b038316600090815260026020526040902080546114185760405162461bcd60e51b815260206004820152600e60248201526d131bd8dac81b9bdd08199bdd5b9960921b6044820152606401610507565b805483106114385760405162461bcd60e51b815260040161050790612fdc565b600181848154811061144c5761144c613008565b600091825260209091206006909102015460ff16600181111561147157611471612aa2565b036114be5760405162461bcd60e51b815260206004820152601a60248201527f5769746864726177616c20616c72656164792070656e64696e670000000000006044820152606401610507565b60008184815481106114d2576114d2613008565b60009182526020909120600690910201905060028082810154600160c01b900460ff169081111561150557611505612aa2565b036115445760405162461bcd60e51b815260206004820152600f60248201526e4265686176696f72616c206c6f636b60881b6044820152606401610507565b6001600282810154600160c01b900460ff169081111561156657611566612aa2565b14801561158b575060028101546001600160401b03600160401b909104811642909116105b156115c0576002810154604051639977d08f60e01b8152600160401b9091046001600160401b03166004820152602401610507565b60006115cc8686610af0565b90506000811161160f5760405162461bcd60e51b815260206004820152600e60248201526d4e6f6e6520617661696c61626c6560901b6044820152606401610507565b815460ff191660011782556005820181905561162f620151806007613034565b6116399042613063565b6002830180546001600160401b0392909216600160801b0267ffffffffffffffff60801b19909216919091179055604080516001600160a01b0388168152602081018790529081018290527f31f69201fab7912e3ec9850e3ab705964bf46d9d4276bdcbb6d05e965e5f54019060600160405180910390a1925050505b92915050565b60015460008054604051636bf444cb60e11b81526001600160a01b036201000090920482166004820152602481018590529192169063d7e8899690604401602060405180830381865afa158015611717573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b69190612f07565b600061177c6040518060400160405280601c81526020017f5641554c54205245434f5645525920415554484f52495a4154494f4e00000000815250836125cf565b6000549091506001600160a01b038083166201000090920416146117d65760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b6044820152606401610507565b6105c7611ac6565b3033146105c7576000546201000090046001600160a01b031633146118155760405162461bcd60e51b815260040161050790612f29565b3033146105c7576001600454600160401b900460ff16600281111561183c5761183c612aa2565b036118595760405162461bcd60e51b815260040161050790612f4c565b6001600160a01b0382166000908152600260205260408120805490919082908490811061188857611888613008565b6000918252602090912060069091020190506001815460ff1660018111156118b2576118b2612aa2565b146118cf5760405162461bcd60e51b815260040161050790613110565b60008160050154116118f35760405162461bcd60e51b815260040161050790613110565b6002810154600160801b90046001600160401b03164210156119445760405162461bcd60e51b815260206004820152600a602482015269151a5b595b1bd8dad95960b21b6044820152606401610507565b6005810180546000918290556004830180549192839261196590849061315b565b90915550600090506001600160a01b0386161561199f5760005461199a9087906201000090046001600160a01b03168461262a565b6119bb565b6000546119bb906201000090046001600160a01b0316836126a1565b905080611a0a5760405162461bcd60e51b815260206004820152601a60248201527f5769746864726177616c207472616e73666572206661696c65640000000000006044820152606401610507565b6001600160a01b03861660009081526003602052604081208054849290611a329084906130a3565b90915550506003830154600484015410611a5557611a5086866122ef565b611a74565b60028301805467ffffffffffffffff60801b19169055825460ff191683555b604080516001600160a01b0388168152602081018790529081018390527fdf273cb619d95419a9cd0ec88123a0538c85064229baa6363788f743fff90deb9060600160405180910390a1505050505050565b6000600454600160401b900460ff166002811115611ae657611ae6612aa2565b14611b335760405162461bcd60e51b815260206004820152601e60248201527f50616e6963206973206f6e6365206f6e6c792e20476f6f64206c75636b2100006044820152606401610507565b60015460005460405163fd9643a560e01b81526001600160a01b03620100009092048216600482015291169063fd9643a590602401602060405180830381865afa158015611b85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba99190612f07565b1515600114611bf05760405162461bcd60e51b81526020600482015260136024820152724e6f207265636f76657279206164647265737360681b6044820152606401610507565b6004805460ff60401b1916600160401b1790556040517fa70647ff2f23a3159af4f0a04b68190abebe4a4dc2c35f19046f0b20529fecf290600090a1565b6000611c3d610e106006613034565b611c479042613083565b6001600160401b0316846001600160401b03161015611cc65760405162461bcd60e51b815260206004820152603560248201527f4c6f636b2074696d65206d757374206e6f74206265206d6f7265207468616e206044820152741cda5e081a1bdd5c9cc81a5b881d1a19481c185cdd605a1b6064820152608401610507565b836001600160401b0316836001600160401b031611611d3a5760405162461bcd60e51b815260206004820152602a60248201527f556e6c6f636b2074696d65206d7573742062652067726561746572207468616e604482015269206c6f636b2074696d6560b01b6064820152608401610507565b611d4962015180614e79613034565b6001600160401b0316611d5c8585613083565b6001600160401b031610611dcb5760405162461bcd60e51b815260206004820152603060248201527f556e6c6f636b2074696d65206d7573742062652077697468696e20353520796560448201526f617273206f66206c6f636b2074696d6560801b6064820152608401610507565b6103e88604858114611e175760405162461bcd60e51b81526020600482015260156024820152741199594818d85b18c8185d591a5d0819985a5b1959605a1b6044820152606401610507565b506001600160a01b038716611f19576000611e3188610e74565b9050611e3d868861315b565b811015611e9157611e4e34476130a3565b6001600160a01b03891660009081526003602052604090819020549051637ab11db960e11b81526004810192909252602482015260448101829052606401610507565b8515611f1357600454604051600091600160481b90046001600160a01b03169088908381818185875af1925050503d8060008114611eeb576040519150601f19603f3d011682016040523d82523d6000602084013e611ef0565b606091505b5050905080611f115760405162461bcd60e51b81526004016105079061316e565b505b50612160565b866000611f2582610e74565b9050611f31878961315b565b811015611feb576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fa391906130f7565b6001600160a01b038b1660009081526003602052604090819020549051637ab11db960e11b815260048101839052602481019190915260448101849052909150606401610507565b861561215d576004805460405163a9059cbb60e01b8152600160481b9091046001600160a01b03908116928201929092526024810189905260009184169063a9059cbb906044016020604051808303816000875af1158015612051573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120759190612f07565b9050806120945760405162461bcd60e51b81526004016105079061316e565b6004805460405163f6f697ab60e01b81526001600160a01b0386811693820193909352600160481b9091049091169063f6f697ab906024016020604051808303816000875af11580156120eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210f9190612f07565b61215b5760405162461bcd60e51b815260206004820152601860248201527f4665652073706c69742063616c6c6f7574206661696c656400000000000000006044820152606401610507565b505b50505b61216e8888888787876126fd565b6001600160a01b03881660009081526003602052604081208054929350889290919061219b90849061315b565b90915550506004546001600160401b03808516911610156121d3576004805467ffffffffffffffff19166001600160401b0385161790555b7fd30902b610d3b194e392d9a203d01d5ea6398a1b92b34c3df7903e68291d32af88888389898989896040516122109897969594939291906131b8565b60405180910390a1979650505050505050565b60015460405163e7274c2b60e01b81526001600160a01b038381166004830152336024830181905292169063e7274c2b90604401600060405180830381600087803b15801561227157600080fd5b505af1158015612285573d6000803e3d6000fd5b50506000805462010000600160b01b031916620100006001600160a01b038616021781556004805460ff60401b1916680200000000000000001790556040517f31eefbe8985ba176de1e0ff181fabf51720b3bf809abe4146eb53e9c87ee8acf9350909150a15050565b6001600160a01b0382166000908152600260205260408120805490919082908490811061231e5761231e613008565b906000526020600020906006020190508060020160089054906101000a90046001600160401b03166001600160401b03164210156123935760405162461bcd60e51b81526020600482015260126024820152714265666f726520756e6c6f636b2074696d6560701b6044820152606401610507565b81546000906123a4906001906130a3565b9050808414612520578281815481106123bf576123bf613008565b90600052602060002090600602018385815481106123df576123df613008565b6000918252602090912082546006909202018054909160ff1690829060ff19166001838181111561241257612412612aa2565b02179055506001828101549082015560028083018054828401805467ffffffffffffffff1981166001600160401b0393841690811783558454600160401b908190048516026001600160801b031990921617178082558354600160801b9081900490931690920267ffffffffffffffff60801b19831681178255925460ff600160c01b918290041694919360ff60c01b191668ffffffffffffffffff60801b1990931692909217919084908111156124cc576124cc612aa2565b0217905550600282810154908201805465ffffffffffff60c81b1916600160c81b9283900465ffffffffffff1690920291909117905560038083015490820155600480830154908201556005918201549101555b8280548061253057612530613217565b600082815260208120600660001990930192830201805460ff19168155600181018290556002810180546001600160f81b031916905560038101829055600481018290556005015590556040517f622ca42598ab680a7def4a7346e1963a82207cf42a5ddb8056ad62cfbda19b88906125c090879087906001600160a01b03929092168252602082015260400190565b60405180910390a15050505050565b600081516041146125e2575060006116b6565b60006125ee84516128cc565b8460405160200161260092919061322d565b6040516020818303038152906040528051906020012090506126228184612a23565b949350505050565b60405163a9059cbb60e01b81526001600160a01b038381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af115801561267d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126229190612f07565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146126ee576040519150601f19603f3d011682016040523d82523d6000602084013e6126f3565b606091505b5090949350505050565b6001600160a01b0385166000908152600260205260408082208151610140810190925290829080828152602081018b90526001600160401b038089166040830152871660608201526000608082015260a00185600281111561276157612761612aa2565b815260006020808301829052604083018b9052606083018290526080909201819052845460018181018755868352929091208351600690920201805493945084939092839160ff19169083818111156127bc576127bc612aa2565b021790555060208201516001820155604082015160028083018054606086015160808701516001600160401b03908116600160801b0267ffffffffffffffff60801b19928216600160401b026001600160801b03199094169190961617919091179081168417825560a086015193919260ff60c01b1990921668ffffffffffffffffff60801b199091161790600160c01b90849081111561285f5761285f612aa2565b021790555060c082015160028201805460d09290921c600160c81b0265ffffffffffff60c81b1990921691909117905560e0820151600382015561010082015160048201556101209091015160059091015581546128bf906001906130a3565b9998505050505050505050565b6060600072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061290c5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310612938576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061295657662386f26fc10000830492506010015b6305f5e100831061296e576305f5e100830492506008015b612710831061298257612710830492506004015b60648310612994576064830492506002015b600a83106129a0576001015b6001016000816001600160401b038111156129bd576129bd612adb565b6040519080825280601f01601f1916602001820160405280156129e7576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846129f157509392505050565b602081810151604080840151606080860151835160008082528188018087528a905291821a81860181905292810186905260808101849052935190959293919260019260a080820193601f1981019281900390910190855afa158015612a8d573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b634e487b7160e01b600052602160045260246000fd5b60038110610fd157610fd1612aa2565b60208101612ad583612ab8565b91905290565b634e487b7160e01b600052604160045260246000fd5b60006001600160401b0380841115612b0b57612b0b612adb565b604051601f8501601f19908116603f01168101908282118183101715612b3357612b33612adb565b81604052809350858152868686011115612b4c57600080fd5b858560208301376000602087830101525050509392505050565b600082601f830112612b7757600080fd5b612b8683833560208501612af1565b9392505050565b60008060408385031215612ba057600080fd5b8235915060208301356001600160401b03811115612bbd57600080fd5b612bc985828601612b66565b9150509250929050565b6001600160a01b0381168114610fd157600080fd5b60008060408385031215612bfb57600080fd5b8235612c0681612bd3565b946020939093013593505050565b60028110612c2457612c24612aa2565b9052565b612c2481612ab8565b600061014082019050612c45828451612c14565b602083015160208301526040830151612c6960408401826001600160401b03169052565b506060830151612c8460608401826001600160401b03169052565b506080830151612c9f60808401826001600160401b03169052565b5060a0830151612cb260a0840182612c28565b5060c0830151612cce60c08401826001600160d01b0319169052565b5060e083015160e083015261010080840151818401525061012080840151818401525092915050565b80356001600160401b0381168114612d0e57600080fd5b919050565b60008060008060008060c08789031215612d2c57600080fd5b863595506020870135612d3e81612bd3565b94506040870135935060608701359250612d5a60808801612cf7565b9150612d6860a08801612cf7565b90509295509295509295565b60008060008060008060c08789031215612d8d57600080fd5b863595506020870135612d9f81612bd3565b94506040870135935060608701359250612dbb60808801612cf7565b915060a08701356001600160d01b031981168114612dd857600080fd5b809150509295509295509295565b600060208284031215612df857600080fd5b8135612b8681612bd3565b600080600060608486031215612e1857600080fd5b8335612e2381612bd3565b92506020840135915060408401356001600160401b03811115612e4557600080fd5b8401601f81018613612e5657600080fd5b612e6586823560208401612af1565b9150509250925092565b600080600060608486031215612e8457600080fd5b8335612e8f81612bd3565b92506020840135612e9f81612bd3565b91506040840135612eaf81612bd3565b809150509250925092565b600060208284031215612ecc57600080fd5b5035919050565b600060208284031215612ee557600080fd5b81356001600160401b03811115612efb57600080fd5b61262284828501612b66565b600060208284031215612f1957600080fd5b81518015158114612b8657600080fd5b6020808252600990820152682737ba1037bbb732b960b91b604082015260600190565b6020808252600990820152682337b93134b23232b760b91b604082015260600190565b60005b83811015612f8a578181015183820152602001612f72565b50506000910152565b60018060a01b03841681528260208201526060604082015260008251806060840152612fc6816080850160208701612f6f565b601f01601f191691909101608001949350505050565b602080825260129082015271092dcecc2d8d2c840d8dec6d640d2dcc8caf60731b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160401b038083168185168183048111821515161561305a5761305a61301e565b02949350505050565b6001600160401b03818116838216019080821115610c4b57610c4b61301e565b6001600160401b03828116828216039080821115610c4b57610c4b61301e565b818103818111156116b6576116b661301e565b60008160001904831182151516156130d0576130d061301e565b500290565b6000826130f257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561310957600080fd5b5051919050565b6020808252601590820152744e6f207769746864726177616c2070656e64696e6760581b604082015260600190565b60008251613151818460208701612f6f565b9190910192915050565b808201808211156116b6576116b661301e565b6020808252602a908201527f466565207472616e736665722072656a65637465642062792065787465726e616040820152691b0818dbdb9d1c9858dd60b21b606082015260800190565b8881526001600160a01b03881660208201526040810187905260608101869052608081018590526001600160401b0384811660a0830152831660c0820152610100810161320483612ab8565b8260e08301529998505050505050505050565b634e487b7160e01b600052603160045260246000fd5b7f19457468657265756d205369676e6564204d6573736167653a0a00000000000081526000835161326581601a850160208801612f6f565b83519083019061327c81601a840160208801612f6f565b01601a0194935050505056fea26469706673582212209d2e749606359036805ac3472864a42b00d9c030766a775ad01e15b6a9d1a40a64736f6c63430008100033
Deployed ByteCode
0x6080604052600436106101445760003560e01c80636aeaec52116100b6578063c43d84491161006f578063c43d84491461037d578063c8393ba91461039d578063cb47f279146103bd578063df034586146103dd578063eff41bd614610413578063f76ac6931461043357600080fd5b80636aeaec52146102ac5780638da5cb5b146102bf578063a0821be3146102fd578063a4b8e7a41461031d578063be476d8a1461033d578063c0c53b8b1461035d57600080fd5b80634700d305116101085780634700d3051461021057806349f59b5714610225578063617b3ab914610246578063622d1cf9146102595780636233b70b1461026c578063645630211461028c57600080fd5b806324a8166e1461015057806331099906146101805780633801f84f146101a5578063392e53cd146101c75780633eff851c146101e357600080fd5b3661014b57005b600080fd5b34801561015c57600080fd5b50600454600160401b900460ff166040516101779190612ac8565b60405180910390f35b34801561018c57600080fd5b50610195610453565b6040519015158152602001610177565b3480156101b157600080fd5b506101c56101c0366004612b8d565b6104d0565b005b3480156101d357600080fd5b5061019560005460ff1660011490565b3480156101ef57600080fd5b506102036101fe366004612be8565b6105cb565b6040516101779190612c31565b34801561021c57600080fd5b506101c56107f4565b610238610233366004612d13565b610835565b604051908152602001610177565b610238610254366004612d74565b6108cf565b610238610267366004612d13565b6109db565b34801561027857600080fd5b506101c5610287366004612de6565b610a68565b34801561029857600080fd5b506102386102a7366004612be8565b610af0565b6101c56102ba366004612e03565b610c52565b3480156102cb57600080fd5b506000546102e5906201000090046001600160a01b031681565b6040516001600160a01b039091168152602001610177565b34801561030957600080fd5b50610238610318366004612de6565b610e74565b34801561032957600080fd5b506101c5610338366004612de6565b610f8b565b34801561034957600080fd5b506101c5610358366004612be8565b610fd4565b34801561036957600080fd5b506101c5610378366004612e6f565b61113d565b34801561038957600080fd5b50610195610398366004612e03565b6112ad565b3480156103a957600080fd5b506102386103b8366004612be8565b611344565b3480156103c957600080fd5b506101956103d8366004612eba565b6116bc565b3480156103e957600080fd5b506102386103f8366004612de6565b6001600160a01b031660009081526002602052604090205490565b34801561041f57600080fd5b506101c561042e366004612ed3565b61173b565b34801561043f57600080fd5b506101c561044e366004612be8565b6117de565b6001546000805460405163fd9643a560e01b81526001600160a01b0362010000909204821660048201529192169063fd9643a590602401602060405180830381865afa1580156104a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cb9190612f07565b905090565b3033146105c7576000546201000090046001600160a01b031633146105105760405162461bcd60e51b815260040161050790612f29565b60405180910390fd5b3033146105c7576001600454600160401b900460ff16600281111561053757610537612aa2565b036105545760405162461bcd60e51b815260040161050790612f4c565b600154600054604051631cb40a9760e31b81526001600160a01b039283169263e5a054b89261059492620100009091049091169086908690600401612f93565b600060405180830381600087803b1580156105ae57600080fd5b505af11580156105c2573d6000803e3d6000fd5b505050505b5050565b604080516101408101825260008082526020808301829052828401829052606083018290526080830182905260a0830182905260c0830182905260e08301829052610100830182905261012083018290526001600160a01b0386168252600281528382208054855181840281018401909652808652939492939091849084015b8282101561075a57600084815260209020604080516101408101909152600684029091018054829060ff16600181111561068757610687612aa2565b600181111561069857610698612aa2565b8152600182015460208201526002808301546001600160401b038082166040850152600160401b820481166060850152600160801b820416608084015260a090920191600160c01b900460ff16908111156106f5576106f5612aa2565b600281111561070657610706612aa2565b815260028201546001600160d01b0319600160c81b90910460d01b1660208083019190915260038301546040830152600483015460608301526005909201546080909101529082526001909201910161064b565b50505050905060008151116107b15760405162461bcd60e51b815260206004820181905260248201527f4e6f206c6f636b7320666f7220676976656e20746f6b656e20616464726573736044820152606401610507565b828151116107d15760405162461bcd60e51b815260040161050790612fdc565b8083815181106107e3576107e3613008565b602002602001015191505092915050565b303314610833576000546201000090046001600160a01b0316331461082b5760405162461bcd60e51b815260040161050790612f29565b610833611ac6565b565b60003033146108c5576000546201000090046001600160a01b0316331461086e5760405162461bcd60e51b815260040161050790612f29565b3033146108c5576001600454600160401b900460ff16600281111561089557610895612aa2565b036108b25760405162461bcd60e51b815260040161050790612f4c565b6108c28787878787876001611c2e565b90505b9695505050505050565b60003033146108c5576000546201000090046001600160a01b031633146109085760405162461bcd60e51b815260040161050790612f29565b3033146108c5576001600454600160401b900460ff16600281111561092f5761092f612aa2565b0361094c5760405162461bcd60e51b815260040161050790612f4c565b61097487878787876109636201518061016d613034565b61096d908a613063565b6002611c2e565b6001600160a01b038716600090815260026020526040902080549192508391839081106109a3576109a3613008565b906000526020600020906006020160020160196101000a81548165ffffffffffff021916908360d01c02179055509695505050505050565b60003033146108c5576000546201000090046001600160a01b03163314610a145760405162461bcd60e51b815260040161050790612f29565b3033146108c5576001600454600160401b900460ff166002811115610a3b57610a3b612aa2565b03610a585760405162461bcd60e51b815260040161050790612f4c565b6108c28787878787876000611c2e565b6000610a796201518061016d613034565b600454610a8f91906001600160401b0316613063565b9050426001600160401b038083169082161015610ae25760405162461bcd60e51b815260206004820152601160248201527015985d5b1d081b9bdd08195e1c1a5c9959607a1b6044820152606401610507565b610aeb83612223565b505050565b600080610afd84846105cb565b90508060e0015181610100015110610b185760009150610c4b565b610e108160400151610b2a9190613083565b6001600160401b0316421080610b4d575080606001516001600160401b03164210155b15610b87578060e0015181610100015110610b6b5760009150610c4b565b8061010001518160e00151610b8091906130a3565b9150610c4b565b600081604001518260600151610b9d9190613083565b6001600160401b0316905060008183604001516001600160401b031642610bc491906130a3565b610bd0906127106130b6565b610bda91906130d5565b90506000818460e00151610bee91906130b6565b9050612710810490508084610100015110610c0c5760009450610c1f565b610100840151610c1c90826130a3565b94505b8484610120015110610c345760009450610c47565b610120840151610c4490866130a3565b94505b5050505b5092915050565b303314610aeb576000546201000090046001600160a01b03163314610c895760405162461bcd60e51b815260040161050790612f29565b303314610aeb576001600454600160401b900460ff166002811115610cb057610cb0612aa2565b03610ccd5760405162461bcd60e51b815260040161050790612f4c565b610cdc6201518061016d613034565b610ce69042613083565b6001600160401b031660026000856001600160a01b03166001600160a01b031681526020019081526020016000208381548110610d2557610d25613008565b60009182526020909120600260069092020101546001600160401b03161115610d9357610d538383836112ad565b1515600114610d935760405162461bcd60e51b815260206004820152600c60248201526b496e76616c696420636f646560a01b6044820152606401610507565b6001600160a01b0383166000908152600260205260409020805483908110610dbd57610dbd613008565b90600052602060002090600602016003015460036000856001600160a01b03166001600160a01b031681526020019081526020016000206000828254610e0391906130a3565b90915550506001600160a01b0383166000908152600260205260409020805442919084908110610e3557610e35613008565b906000526020600020906006020160020160086101000a8154816001600160401b0302191690836001600160401b03160217905550610aeb83836122ef565b6000806001600160a01b038316610e8c575047610f3f565b6000836001600160a01b03163b11610ed45760405162461bcd60e51b815260206004820152600b60248201526a3737ba1030903a37b5b2b760a91b6044820152606401610507565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610f18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3c91906130f7565b90505b6001600160a01b038316600090815260036020526040902054811115610f85576001600160a01b038316600090815260036020526040902054610f8290826130a3565b91505b50919050565b6001600454600160401b900460ff166002811115610fab57610fab612aa2565b14610fc85760405162461bcd60e51b815260040161050790612f4c565b610fd181612223565b50565b3033146105c7576000546201000090046001600160a01b0316331461100b5760405162461bcd60e51b815260040161050790612f29565b6001600160a01b0382166000908152600260205260409020805461106a5760405162461bcd60e51b8152602060048201526016602482015275139bc81b585d18da1a5b99c81b1bd8dac8199bdd5b9960521b6044820152606401610507565b8054821061108a5760405162461bcd60e51b815260040161050790612fdc565b600081838154811061109e5761109e613008565b6000918252602090912060069091020190506001815460ff1660018111156110c8576110c8612aa2565b146110e55760405162461bcd60e51b815260040161050790613110565b60006005820155805460ff19168155604080516001600160a01b0386168152602081018590527f2eed97477f07c07ec48f8f678f4e84f7c0de55bf33f51c3dc989b1335308031991015b60405180910390a150505050565b600054610100900460ff161580801561115d5750600054600160ff909116105b806111775750303b158015611177575060005460ff166001145b6111da5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610507565b6000805460ff1916600117905580156111fd576000805461ff0019166101001790555b6000805462010000600160b01b031916620100006001600160a01b038781169190910291909117909155600180546001600160a01b03191685831617905560048054918416600160481b026001600160e81b0319909216426001600160401b03161791909117905580156112a7576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161112f565b50505050565b600080826040516020016112c1919061313f565b60405160208183030381529060405280519060200120905060026000866001600160a01b03166001600160a01b03168152602001908152602001600020848154811061130f5761130f613008565b6000918252602090912060069091020160020154600160c81b900460d01b6001600160d01b0319908116911614949350505050565b60003033146116b6576000546201000090046001600160a01b0316331461137d5760405162461bcd60e51b815260040161050790612f29565b3033146116b6576001600454600160401b900460ff1660028111156113a4576113a4612aa2565b036113c15760405162461bcd60e51b815260040161050790612f4c565b6001600160a01b038316600090815260026020526040902080546114185760405162461bcd60e51b815260206004820152600e60248201526d131bd8dac81b9bdd08199bdd5b9960921b6044820152606401610507565b805483106114385760405162461bcd60e51b815260040161050790612fdc565b600181848154811061144c5761144c613008565b600091825260209091206006909102015460ff16600181111561147157611471612aa2565b036114be5760405162461bcd60e51b815260206004820152601a60248201527f5769746864726177616c20616c72656164792070656e64696e670000000000006044820152606401610507565b60008184815481106114d2576114d2613008565b60009182526020909120600690910201905060028082810154600160c01b900460ff169081111561150557611505612aa2565b036115445760405162461bcd60e51b815260206004820152600f60248201526e4265686176696f72616c206c6f636b60881b6044820152606401610507565b6001600282810154600160c01b900460ff169081111561156657611566612aa2565b14801561158b575060028101546001600160401b03600160401b909104811642909116105b156115c0576002810154604051639977d08f60e01b8152600160401b9091046001600160401b03166004820152602401610507565b60006115cc8686610af0565b90506000811161160f5760405162461bcd60e51b815260206004820152600e60248201526d4e6f6e6520617661696c61626c6560901b6044820152606401610507565b815460ff191660011782556005820181905561162f620151806007613034565b6116399042613063565b6002830180546001600160401b0392909216600160801b0267ffffffffffffffff60801b19909216919091179055604080516001600160a01b0388168152602081018790529081018290527f31f69201fab7912e3ec9850e3ab705964bf46d9d4276bdcbb6d05e965e5f54019060600160405180910390a1925050505b92915050565b60015460008054604051636bf444cb60e11b81526001600160a01b036201000090920482166004820152602481018590529192169063d7e8899690604401602060405180830381865afa158015611717573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b69190612f07565b600061177c6040518060400160405280601c81526020017f5641554c54205245434f5645525920415554484f52495a4154494f4e00000000815250836125cf565b6000549091506001600160a01b038083166201000090920416146117d65760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b6044820152606401610507565b6105c7611ac6565b3033146105c7576000546201000090046001600160a01b031633146118155760405162461bcd60e51b815260040161050790612f29565b3033146105c7576001600454600160401b900460ff16600281111561183c5761183c612aa2565b036118595760405162461bcd60e51b815260040161050790612f4c565b6001600160a01b0382166000908152600260205260408120805490919082908490811061188857611888613008565b6000918252602090912060069091020190506001815460ff1660018111156118b2576118b2612aa2565b146118cf5760405162461bcd60e51b815260040161050790613110565b60008160050154116118f35760405162461bcd60e51b815260040161050790613110565b6002810154600160801b90046001600160401b03164210156119445760405162461bcd60e51b815260206004820152600a602482015269151a5b595b1bd8dad95960b21b6044820152606401610507565b6005810180546000918290556004830180549192839261196590849061315b565b90915550600090506001600160a01b0386161561199f5760005461199a9087906201000090046001600160a01b03168461262a565b6119bb565b6000546119bb906201000090046001600160a01b0316836126a1565b905080611a0a5760405162461bcd60e51b815260206004820152601a60248201527f5769746864726177616c207472616e73666572206661696c65640000000000006044820152606401610507565b6001600160a01b03861660009081526003602052604081208054849290611a329084906130a3565b90915550506003830154600484015410611a5557611a5086866122ef565b611a74565b60028301805467ffffffffffffffff60801b19169055825460ff191683555b604080516001600160a01b0388168152602081018790529081018390527fdf273cb619d95419a9cd0ec88123a0538c85064229baa6363788f743fff90deb9060600160405180910390a1505050505050565b6000600454600160401b900460ff166002811115611ae657611ae6612aa2565b14611b335760405162461bcd60e51b815260206004820152601e60248201527f50616e6963206973206f6e6365206f6e6c792e20476f6f64206c75636b2100006044820152606401610507565b60015460005460405163fd9643a560e01b81526001600160a01b03620100009092048216600482015291169063fd9643a590602401602060405180830381865afa158015611b85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba99190612f07565b1515600114611bf05760405162461bcd60e51b81526020600482015260136024820152724e6f207265636f76657279206164647265737360681b6044820152606401610507565b6004805460ff60401b1916600160401b1790556040517fa70647ff2f23a3159af4f0a04b68190abebe4a4dc2c35f19046f0b20529fecf290600090a1565b6000611c3d610e106006613034565b611c479042613083565b6001600160401b0316846001600160401b03161015611cc65760405162461bcd60e51b815260206004820152603560248201527f4c6f636b2074696d65206d757374206e6f74206265206d6f7265207468616e206044820152741cda5e081a1bdd5c9cc81a5b881d1a19481c185cdd605a1b6064820152608401610507565b836001600160401b0316836001600160401b031611611d3a5760405162461bcd60e51b815260206004820152602a60248201527f556e6c6f636b2074696d65206d7573742062652067726561746572207468616e604482015269206c6f636b2074696d6560b01b6064820152608401610507565b611d4962015180614e79613034565b6001600160401b0316611d5c8585613083565b6001600160401b031610611dcb5760405162461bcd60e51b815260206004820152603060248201527f556e6c6f636b2074696d65206d7573742062652077697468696e20353520796560448201526f617273206f66206c6f636b2074696d6560801b6064820152608401610507565b6103e88604858114611e175760405162461bcd60e51b81526020600482015260156024820152741199594818d85b18c8185d591a5d0819985a5b1959605a1b6044820152606401610507565b506001600160a01b038716611f19576000611e3188610e74565b9050611e3d868861315b565b811015611e9157611e4e34476130a3565b6001600160a01b03891660009081526003602052604090819020549051637ab11db960e11b81526004810192909252602482015260448101829052606401610507565b8515611f1357600454604051600091600160481b90046001600160a01b03169088908381818185875af1925050503d8060008114611eeb576040519150601f19603f3d011682016040523d82523d6000602084013e611ef0565b606091505b5050905080611f115760405162461bcd60e51b81526004016105079061316e565b505b50612160565b866000611f2582610e74565b9050611f31878961315b565b811015611feb576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fa391906130f7565b6001600160a01b038b1660009081526003602052604090819020549051637ab11db960e11b815260048101839052602481019190915260448101849052909150606401610507565b861561215d576004805460405163a9059cbb60e01b8152600160481b9091046001600160a01b03908116928201929092526024810189905260009184169063a9059cbb906044016020604051808303816000875af1158015612051573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120759190612f07565b9050806120945760405162461bcd60e51b81526004016105079061316e565b6004805460405163f6f697ab60e01b81526001600160a01b0386811693820193909352600160481b9091049091169063f6f697ab906024016020604051808303816000875af11580156120eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210f9190612f07565b61215b5760405162461bcd60e51b815260206004820152601860248201527f4665652073706c69742063616c6c6f7574206661696c656400000000000000006044820152606401610507565b505b50505b61216e8888888787876126fd565b6001600160a01b03881660009081526003602052604081208054929350889290919061219b90849061315b565b90915550506004546001600160401b03808516911610156121d3576004805467ffffffffffffffff19166001600160401b0385161790555b7fd30902b610d3b194e392d9a203d01d5ea6398a1b92b34c3df7903e68291d32af88888389898989896040516122109897969594939291906131b8565b60405180910390a1979650505050505050565b60015460405163e7274c2b60e01b81526001600160a01b038381166004830152336024830181905292169063e7274c2b90604401600060405180830381600087803b15801561227157600080fd5b505af1158015612285573d6000803e3d6000fd5b50506000805462010000600160b01b031916620100006001600160a01b038616021781556004805460ff60401b1916680200000000000000001790556040517f31eefbe8985ba176de1e0ff181fabf51720b3bf809abe4146eb53e9c87ee8acf9350909150a15050565b6001600160a01b0382166000908152600260205260408120805490919082908490811061231e5761231e613008565b906000526020600020906006020190508060020160089054906101000a90046001600160401b03166001600160401b03164210156123935760405162461bcd60e51b81526020600482015260126024820152714265666f726520756e6c6f636b2074696d6560701b6044820152606401610507565b81546000906123a4906001906130a3565b9050808414612520578281815481106123bf576123bf613008565b90600052602060002090600602018385815481106123df576123df613008565b6000918252602090912082546006909202018054909160ff1690829060ff19166001838181111561241257612412612aa2565b02179055506001828101549082015560028083018054828401805467ffffffffffffffff1981166001600160401b0393841690811783558454600160401b908190048516026001600160801b031990921617178082558354600160801b9081900490931690920267ffffffffffffffff60801b19831681178255925460ff600160c01b918290041694919360ff60c01b191668ffffffffffffffffff60801b1990931692909217919084908111156124cc576124cc612aa2565b0217905550600282810154908201805465ffffffffffff60c81b1916600160c81b9283900465ffffffffffff1690920291909117905560038083015490820155600480830154908201556005918201549101555b8280548061253057612530613217565b600082815260208120600660001990930192830201805460ff19168155600181018290556002810180546001600160f81b031916905560038101829055600481018290556005015590556040517f622ca42598ab680a7def4a7346e1963a82207cf42a5ddb8056ad62cfbda19b88906125c090879087906001600160a01b03929092168252602082015260400190565b60405180910390a15050505050565b600081516041146125e2575060006116b6565b60006125ee84516128cc565b8460405160200161260092919061322d565b6040516020818303038152906040528051906020012090506126228184612a23565b949350505050565b60405163a9059cbb60e01b81526001600160a01b038381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af115801561267d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126229190612f07565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146126ee576040519150601f19603f3d011682016040523d82523d6000602084013e6126f3565b606091505b5090949350505050565b6001600160a01b0385166000908152600260205260408082208151610140810190925290829080828152602081018b90526001600160401b038089166040830152871660608201526000608082015260a00185600281111561276157612761612aa2565b815260006020808301829052604083018b9052606083018290526080909201819052845460018181018755868352929091208351600690920201805493945084939092839160ff19169083818111156127bc576127bc612aa2565b021790555060208201516001820155604082015160028083018054606086015160808701516001600160401b03908116600160801b0267ffffffffffffffff60801b19928216600160401b026001600160801b03199094169190961617919091179081168417825560a086015193919260ff60c01b1990921668ffffffffffffffffff60801b199091161790600160c01b90849081111561285f5761285f612aa2565b021790555060c082015160028201805460d09290921c600160c81b0265ffffffffffff60c81b1990921691909117905560e0820151600382015561010082015160048201556101209091015160059091015581546128bf906001906130a3565b9998505050505050505050565b6060600072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061290c5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310612938576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061295657662386f26fc10000830492506010015b6305f5e100831061296e576305f5e100830492506008015b612710831061298257612710830492506004015b60648310612994576064830492506002015b600a83106129a0576001015b6001016000816001600160401b038111156129bd576129bd612adb565b6040519080825280601f01601f1916602001820160405280156129e7576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846129f157509392505050565b602081810151604080840151606080860151835160008082528188018087528a905291821a81860181905292810186905260808101849052935190959293919260019260a080820193601f1981019281900390910190855afa158015612a8d573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b634e487b7160e01b600052602160045260246000fd5b60038110610fd157610fd1612aa2565b60208101612ad583612ab8565b91905290565b634e487b7160e01b600052604160045260246000fd5b60006001600160401b0380841115612b0b57612b0b612adb565b604051601f8501601f19908116603f01168101908282118183101715612b3357612b33612adb565b81604052809350858152868686011115612b4c57600080fd5b858560208301376000602087830101525050509392505050565b600082601f830112612b7757600080fd5b612b8683833560208501612af1565b9392505050565b60008060408385031215612ba057600080fd5b8235915060208301356001600160401b03811115612bbd57600080fd5b612bc985828601612b66565b9150509250929050565b6001600160a01b0381168114610fd157600080fd5b60008060408385031215612bfb57600080fd5b8235612c0681612bd3565b946020939093013593505050565b60028110612c2457612c24612aa2565b9052565b612c2481612ab8565b600061014082019050612c45828451612c14565b602083015160208301526040830151612c6960408401826001600160401b03169052565b506060830151612c8460608401826001600160401b03169052565b506080830151612c9f60808401826001600160401b03169052565b5060a0830151612cb260a0840182612c28565b5060c0830151612cce60c08401826001600160d01b0319169052565b5060e083015160e083015261010080840151818401525061012080840151818401525092915050565b80356001600160401b0381168114612d0e57600080fd5b919050565b60008060008060008060c08789031215612d2c57600080fd5b863595506020870135612d3e81612bd3565b94506040870135935060608701359250612d5a60808801612cf7565b9150612d6860a08801612cf7565b90509295509295509295565b60008060008060008060c08789031215612d8d57600080fd5b863595506020870135612d9f81612bd3565b94506040870135935060608701359250612dbb60808801612cf7565b915060a08701356001600160d01b031981168114612dd857600080fd5b809150509295509295509295565b600060208284031215612df857600080fd5b8135612b8681612bd3565b600080600060608486031215612e1857600080fd5b8335612e2381612bd3565b92506020840135915060408401356001600160401b03811115612e4557600080fd5b8401601f81018613612e5657600080fd5b612e6586823560208401612af1565b9150509250925092565b600080600060608486031215612e8457600080fd5b8335612e8f81612bd3565b92506020840135612e9f81612bd3565b91506040840135612eaf81612bd3565b809150509250925092565b600060208284031215612ecc57600080fd5b5035919050565b600060208284031215612ee557600080fd5b81356001600160401b03811115612efb57600080fd5b61262284828501612b66565b600060208284031215612f1957600080fd5b81518015158114612b8657600080fd5b6020808252600990820152682737ba1037bbb732b960b91b604082015260600190565b6020808252600990820152682337b93134b23232b760b91b604082015260600190565b60005b83811015612f8a578181015183820152602001612f72565b50506000910152565b60018060a01b03841681528260208201526060604082015260008251806060840152612fc6816080850160208701612f6f565b601f01601f191691909101608001949350505050565b602080825260129082015271092dcecc2d8d2c840d8dec6d640d2dcc8caf60731b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160401b038083168185168183048111821515161561305a5761305a61301e565b02949350505050565b6001600160401b03818116838216019080821115610c4b57610c4b61301e565b6001600160401b03828116828216039080821115610c4b57610c4b61301e565b818103818111156116b6576116b661301e565b60008160001904831182151516156130d0576130d061301e565b500290565b6000826130f257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561310957600080fd5b5051919050565b6020808252601590820152744e6f207769746864726177616c2070656e64696e6760581b604082015260600190565b60008251613151818460208701612f6f565b9190910192915050565b808201808211156116b6576116b661301e565b6020808252602a908201527f466565207472616e736665722072656a65637465642062792065787465726e616040820152691b0818dbdb9d1c9858dd60b21b606082015260800190565b8881526001600160a01b03881660208201526040810187905260608101869052608081018590526001600160401b0384811660a0830152831660c0820152610100810161320483612ab8565b8260e08301529998505050505050505050565b634e487b7160e01b600052603160045260246000fd5b7f19457468657265756d205369676e6564204d6573736167653a0a00000000000081526000835161326581601a850160208801612f6f565b83519083019061327c81601a840160208801612f6f565b01601a0194935050505056fea26469706673582212209d2e749606359036805ac3472864a42b00d9c030766a775ad01e15b6a9d1a40a64736f6c63430008100033