Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify.
View contract in Sourcify repository
- Contract name:
- Token
- Optimization enabled
- true
- Compiler version
- v0.8.16+commit.07a7930e
- Optimization runs
- 500000
- EVM Version
- london
- Verified at
- 2026-04-24T21:44:32.839044Z
Constructor Arguments
000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da7174
Arg [0] (address) : 0xd310a3041dfcf14def5ccbc508668974b5da7174
src/token/Token.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { UUPS } from "../lib/proxy/UUPS.sol";
import { ReentrancyGuard } from "../lib/utils/ReentrancyGuard.sol";
import { ERC721Votes } from "../lib/token/ERC721Votes.sol";
import { ERC721 } from "../lib/token/ERC721.sol";
import { Ownable } from "../lib/utils/Ownable.sol";
import { TokenStorageV1 } from "./storage/TokenStorageV1.sol";
import { IBaseMetadata } from "./metadata/interfaces/IBaseMetadata.sol";
import { IManager } from "../manager/IManager.sol";
import { IAuction } from "../auction/IAuction.sol";
import { IToken } from "./IToken.sol";
import { VersionedContract } from "../VersionedContract.sol";
/// @title Token
/// @author Rohan Kulkarni
/// @custom:repo github.com/ourzora/nouns-protocol
/// @notice A DAO's ERC-721 governance token
contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC721Votes, TokenStorageV1 {
/// ///
/// IMMUTABLES ///
/// ///
/// @notice The contract upgrade manager
IManager private immutable manager;
/// ///
/// CONSTRUCTOR ///
/// ///
/// @param _manager The contract upgrade manager address
constructor(address _manager) payable initializer {
manager = IManager(_manager);
}
/// ///
/// INITIALIZER ///
/// ///
/// @notice Initializes a DAO's ERC-721 token contract
/// @param _founders The DAO founders
/// @param _initStrings The encoded token and metadata initialization strings
/// @param _metadataRenderer The token's metadata renderer
/// @param _auction The token's auction house
/// @param _initialOwner The initial owner of the token
function initialize(
IManager.FounderParams[] calldata _founders,
bytes calldata _initStrings,
address _metadataRenderer,
address _auction,
address _initialOwner
) external initializer {
// Ensure the caller is the contract manager
if (msg.sender != address(manager)) {
revert ONLY_MANAGER();
}
// Initialize the reentrancy guard
__ReentrancyGuard_init();
// Setup ownable
__Ownable_init(_initialOwner);
// Store the founders and compute their allocations
_addFounders(_founders);
// Decode the token name and symbol
(string memory _name, string memory _symbol, , , , ) = abi.decode(_initStrings, (string, string, string, string, string, string));
// Initialize the ERC-721 token
__ERC721_init(_name, _symbol);
// Store the metadata renderer and auction house
settings.metadataRenderer = IBaseMetadata(_metadataRenderer);
settings.auction = _auction;
}
/// @notice Called by the auction upon the first unpause / token mint to transfer ownership from founder to treasury
/// @dev Only callable by the auction contract
function onFirstAuctionStarted() external override {
if (msg.sender != settings.auction) {
revert ONLY_AUCTION();
}
// Force transfer ownership to the treasury
_transferOwnership(IAuction(settings.auction).treasury());
}
/// @notice Called upon initialization to add founders and compute their vesting allocations
/// @dev We do this by reserving an mapping of [0-100] token indices, such that if a new token mint ID % 100 is reserved, it's sent to the appropriate founder.
/// @param _founders The list of DAO founders
function _addFounders(IManager.FounderParams[] calldata _founders) internal {
// Used to store the total percent ownership among the founders
uint256 totalOwnership;
uint8 numFoundersAdded = 0;
unchecked {
// For each founder:
for (uint256 i; i < _founders.length; ++i) {
// Cache the percent ownership
uint256 founderPct = _founders[i].ownershipPct;
// Continue if no ownership is specified
if (founderPct == 0) {
continue;
}
// Update the total ownership and ensure it's valid
totalOwnership += founderPct;
// Check that founders own less than 100% of tokens
if (totalOwnership > 99) {
revert INVALID_FOUNDER_OWNERSHIP();
}
// Compute the founder's id
uint256 founderId = numFoundersAdded++;
// Get the pointer to store the founder
Founder storage newFounder = founder[founderId];
// Store the founder's vesting details
newFounder.wallet = _founders[i].wallet;
newFounder.vestExpiry = uint32(_founders[i].vestExpiry);
// Total ownership cannot be above 100 so this fits safely in uint8
newFounder.ownershipPct = uint8(founderPct);
// Compute the vesting schedule
uint256 schedule = 100 / founderPct;
// Used to store the base token id the founder will recieve
uint256 baseTokenId;
// For each token to vest:
for (uint256 j; j < founderPct; ++j) {
// Get the available token id
baseTokenId = _getNextTokenId(baseTokenId);
// Store the founder as the recipient
tokenRecipient[baseTokenId] = newFounder;
emit MintScheduled(baseTokenId, founderId, newFounder);
// Update the base token id
baseTokenId = (baseTokenId + schedule) % 100;
}
}
// Store the founders' details
settings.totalOwnership = uint8(totalOwnership);
settings.numFounders = numFoundersAdded;
}
}
/// @dev Finds the next available base token id for a founder
/// @param _tokenId The ERC-721 token id
function _getNextTokenId(uint256 _tokenId) internal view returns (uint256) {
unchecked {
while (tokenRecipient[_tokenId].wallet != address(0)) {
_tokenId = (++_tokenId) % 100;
}
return _tokenId;
}
}
/// ///
/// MINT ///
/// ///
/// @notice Mints tokens to the auction house for bidding and handles founder vesting
function mint() external nonReentrant returns (uint256 tokenId) {
// Cache the auction address
address minter = settings.auction;
// Ensure the caller is the auction
if (msg.sender != minter) {
revert ONLY_AUCTION();
}
// Cannot realistically overflow
unchecked {
do {
// Get the next token to mint
tokenId = settings.mintCount++;
// Lookup whether the token is for a founder, and mint accordingly if so
} while (_isForFounder(tokenId));
}
// Mint the next available token to the auction house for bidding
_mint(minter, tokenId);
}
/// @dev Overrides _mint to include attribute generation
/// @param _to The token recipient
/// @param _tokenId The ERC-721 token id
function _mint(address _to, uint256 _tokenId) internal override {
// Mint the token
super._mint(_to, _tokenId);
// Increment the total supply
unchecked {
++settings.totalSupply;
}
// Generate the token attributes
if (!settings.metadataRenderer.onMinted(_tokenId)) revert NO_METADATA_GENERATED();
}
/// @dev Checks if a given token is for a founder and mints accordingly
/// @param _tokenId The ERC-721 token id
function _isForFounder(uint256 _tokenId) private returns (bool) {
// Get the base token id
uint256 baseTokenId = _tokenId % 100;
// If there is no scheduled recipient:
if (tokenRecipient[baseTokenId].wallet == address(0)) {
return false;
// Else if the founder is still vesting:
} else if (block.timestamp < tokenRecipient[baseTokenId].vestExpiry) {
// Mint the token to the founder
_mint(tokenRecipient[baseTokenId].wallet, _tokenId);
return true;
// Else the founder has finished vesting:
} else {
// Remove them from future lookups
delete tokenRecipient[baseTokenId];
return false;
}
}
/// ///
/// BURN ///
/// ///
/// @notice Burns a token that did not see any bids
/// @param _tokenId The ERC-721 token id
function burn(uint256 _tokenId) external {
// Ensure the caller is the auction house
if (msg.sender != settings.auction) {
revert ONLY_AUCTION();
}
// Burn the token
_burn(_tokenId);
}
function _burn(uint256 _tokenId) internal override {
super._burn(_tokenId);
unchecked {
--settings.totalSupply;
}
}
/// ///
/// METADATA ///
/// ///
/// @notice The URI for a token
/// @param _tokenId The ERC-721 token id
function tokenURI(uint256 _tokenId) public view override(IToken, ERC721) returns (string memory) {
return settings.metadataRenderer.tokenURI(_tokenId);
}
/// @notice The URI for the contract
function contractURI() public view override(IToken, ERC721) returns (string memory) {
return settings.metadataRenderer.contractURI();
}
/// ///
/// FOUNDERS ///
/// ///
/// @notice The number of founders
function totalFounders() external view returns (uint256) {
return settings.numFounders;
}
/// @notice The founders total percent ownership
function totalFounderOwnership() external view returns (uint256) {
return settings.totalOwnership;
}
/// @notice The vesting details of a founder
/// @param _founderId The founder id
function getFounder(uint256 _founderId) external view returns (Founder memory) {
return founder[_founderId];
}
/// @notice The vesting details of all founders
function getFounders() external view returns (Founder[] memory) {
// Cache the number of founders
uint256 numFounders = settings.numFounders;
// Get a temporary array to hold all founders
Founder[] memory founders = new Founder[](numFounders);
// Cannot realistically overflow
unchecked {
// Add each founder to the array
for (uint256 i; i < numFounders; ++i) {
founders[i] = founder[i];
}
}
return founders;
}
/// @notice The founder scheduled to receive the given token id
/// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
/// @param _tokenId The ERC-721 token id
function getScheduledRecipient(uint256 _tokenId) external view returns (Founder memory) {
return tokenRecipient[_tokenId % 100];
}
/// @notice Update the list of allocation owners
/// @param newFounders the full list of founders
function updateFounders(IManager.FounderParams[] calldata newFounders) external onlyOwner {
// Cache the number of founders
uint256 numFounders = settings.numFounders;
// Get a temporary array to hold all founders
Founder[] memory cachedFounders = new Founder[](numFounders);
// Cannot realistically overflow
unchecked {
// Add each founder to the array
for (uint256 i; i < numFounders; ++i) {
cachedFounders[i] = founder[i];
}
}
// Keep a mapping of all the reserved token IDs we're set to clear.
bool[] memory clearedTokenIds = new bool[](100);
unchecked {
// for each existing founder:
for (uint256 i; i < cachedFounders.length; ++i) {
// copy the founder into memory
Founder memory cachedFounder = cachedFounders[i];
// Delete the founder from the stored mapping
delete founder[i];
// Some DAOs were initialized with 0 percentage ownership.
// This skips them to avoid a division by zero error.
if (cachedFounder.ownershipPct == 0) {
continue;
}
// using the ownership percentage, get reserved token percentages
uint256 schedule = 100 / cachedFounder.ownershipPct;
// Used to reverse engineer the indices the founder has reserved tokens in.
uint256 baseTokenId;
for (uint256 j; j < cachedFounder.ownershipPct; ++j) {
// Get the next index that hasn't already been cleared
while (clearedTokenIds[baseTokenId] != false) {
baseTokenId = (++baseTokenId) % 100;
}
delete tokenRecipient[baseTokenId];
clearedTokenIds[baseTokenId] = true;
emit MintUnscheduled(baseTokenId, i, cachedFounder);
// Update the base token id
baseTokenId = (baseTokenId + schedule) % 100;
}
}
}
settings.numFounders = 0;
settings.totalOwnership = 0;
emit FounderAllocationsCleared(newFounders);
_addFounders(newFounders);
}
/// ///
/// SETTINGS ///
/// ///
/// @notice The total supply of tokens
function totalSupply() external view returns (uint256) {
return settings.totalSupply;
}
/// @notice The address of the auction house
function auction() external view returns (address) {
return settings.auction;
}
/// @notice The address of the metadata renderer
function metadataRenderer() external view returns (address) {
return address(settings.metadataRenderer);
}
function owner() public view override(IToken, Ownable) returns (address) {
return super.owner();
}
/// ///
/// TOKEN UPGRADE ///
/// ///
/// @notice Ensures the caller is authorized to upgrade the contract and that the new implementation is valid
/// @dev This function is called in `upgradeTo` & `upgradeToAndCall`
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal view override {
// Ensure the caller is the shared owner of the token and metadata renderer
if (msg.sender != owner()) revert ONLY_OWNER();
// Ensure the implementation is valid
if (!manager.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl);
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../interfaces/IUUPS.sol";
import { ERC1967Upgrade } from "./ERC1967Upgrade.sol";
/// @title UUPS
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/UUPSUpgradeable.sol)
/// - Uses custom errors declared in IUUPS
/// - Inherits a modern, minimal ERC1967Upgrade
abstract contract UUPS is IUUPS, ERC1967Upgrade {
/// ///
/// IMMUTABLES ///
/// ///
/// @dev The address of the implementation
address private immutable __self = address(this);
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures that execution is via proxy delegatecall with the correct implementation
modifier onlyProxy() {
if (address(this) == __self) revert ONLY_DELEGATECALL();
if (_getImplementation() != __self) revert ONLY_PROXY();
_;
}
/// @dev Ensures that execution is via direct call
modifier notDelegated() {
if (address(this) != __self) revert ONLY_CALL();
_;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Hook to authorize an implementation upgrade
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal virtual;
/// @notice Upgrades to an implementation
/// @param _newImpl The new implementation address
function upgradeTo(address _newImpl) external onlyProxy {
_authorizeUpgrade(_newImpl);
_upgradeToAndCallUUPS(_newImpl, "", false);
}
/// @notice Upgrades to an implementation with an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function upgradeToAndCall(address _newImpl, bytes memory _data) external payable onlyProxy {
_authorizeUpgrade(_newImpl);
_upgradeToAndCallUUPS(_newImpl, _data, true);
}
/// @notice The storage slot of the implementation address
function proxiableUUID() external view notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IERC721
/// @author Rohan Kulkarni
/// @notice The external ERC721 events, errors, and functions
interface IERC721 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a token is transferred from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/// @notice Emitted when an owner approves an account to manage a token
/// @param owner The owner address
/// @param approved The account address
/// @param tokenId The ERC-721 token id
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/// @notice Emitted when an owner sets an approval for a spender to manage all tokens
/// @param owner The owner address
/// @param operator The spender address
/// @param approved If the approval is being set or removed
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if a caller is not authorized to approve or transfer a token
error INVALID_APPROVAL();
/// @dev Reverts if a transfer is called with the incorrect token owner
error INVALID_OWNER();
/// @dev Reverts if a transfer is attempted to address(0)
error INVALID_RECIPIENT();
/// @dev Reverts if an existing token is called to be minted
error ALREADY_MINTED();
/// @dev Reverts if a non-existent token is called to be burned
error NOT_MINTED();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The number of tokens owned
/// @param owner The owner address
function balanceOf(address owner) external view returns (uint256);
/// @notice The owner of a token
/// @param tokenId The ERC-721 token id
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice The account approved to manage a token
/// @param tokenId The ERC-721 token id
function getApproved(uint256 tokenId) external view returns (address);
/// @notice If an operator is authorized to manage all of an owner's tokens
/// @param owner The owner address
/// @param operator The operator address
function isApprovedForAll(address owner, address operator) external view returns (bool);
/// @notice Authorizes an account to manage a token
/// @param to The account address
/// @param tokenId The ERC-721 token id
function approve(address to, uint256 tokenId) external;
/// @notice Authorizes an account to manage all tokens
/// @param operator The account address
/// @param approved If permission is being given or removed
function setApprovalForAll(address operator, bool approved) external;
/// @notice Safe transfers a token from sender to recipient with additional data
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
/// @param data The additional data sent in the call to the recipient
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/// @notice Safe transfers a token from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/// @notice Transfers a token from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/utils/ERC721Holder.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC1155/utils/ERC1155Holder.sol)
abstract contract ERC1155TokenReceiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title EIP712
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/Address.sol)
/// - Uses custom errors `INVALID_TARGET()` & `DELEGATE_CALL_FAILED()`
/// - Adds util converting address to bytes32
library Address {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the target of a delegatecall is not a contract
error INVALID_TARGET();
/// @dev Reverts if a delegatecall has failed
error DELEGATE_CALL_FAILED();
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Utility to convert an address to bytes32
function toBytes32(address _account) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_account)) << 96);
}
/// @dev If an address is a contract
function isContract(address _account) internal view returns (bool rv) {
assembly {
rv := gt(extcodesize(_account), 0)
}
}
/// @dev Performs a delegatecall on an address
function functionDelegateCall(address _target, bytes memory _data) internal returns (bytes memory) {
if (!isContract(_target)) revert INVALID_TARGET();
(bool success, bytes memory returndata) = _target.delegatecall(_data);
return verifyCallResult(success, returndata);
}
/// @dev Verifies a delegatecall was successful
function verifyCallResult(bool _success, bytes memory _returndata) internal pure returns (bytes memory) {
if (_success) {
return _returndata;
} else {
if (_returndata.length > 0) {
assembly {
let returndata_size := mload(_returndata)
revert(add(32, _returndata), returndata_size)
}
} else {
revert DELEGATE_CALL_FAILED();
}
}
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721Votes } from "../interfaces/IERC721Votes.sol";
import { ERC721 } from "../token/ERC721.sol";
import { EIP712 } from "../utils/EIP712.sol";
/// @title ERC721Votes
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/extensions/draft-ERC721Votes.sol) & Nouns DAO ERC721Checkpointable.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license.
/// - Uses custom errors defined in IERC721Votes
/// - Checkpoints are based on timestamps instead of block numbers
/// - Tokens are self-delegated by default
/// - The total number of votes is the token supply itself
abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 {
/// ///
/// CONSTANTS ///
/// ///
/// @dev The EIP-712 typehash to delegate with a signature
bytes32 internal constant DELEGATION_TYPEHASH = keccak256("Delegation(address from,address to,uint256 nonce,uint256 deadline)");
/// ///
/// STORAGE ///
/// ///
/// @notice The delegate for an account
/// @notice Account => Delegate
mapping(address => address) internal delegation;
/// @notice The number of checkpoints for an account
/// @dev Account => Num Checkpoints
mapping(address => uint256) internal numCheckpoints;
/// @notice The checkpoint for an account
/// @dev Account => Checkpoint Id => Checkpoint
mapping(address => mapping(uint256 => Checkpoint)) internal checkpoints;
/// ///
/// VOTING WEIGHT ///
/// ///
/// @notice The current number of votes for an account
/// @param _account The account address
function getVotes(address _account) public view returns (uint256) {
// Get the account's number of checkpoints
uint256 nCheckpoints = numCheckpoints[_account];
// Cannot underflow as `nCheckpoints` is ensured to be greater than 0 if reached
unchecked {
// Return the number of votes at the latest checkpoint if applicable
return nCheckpoints != 0 ? checkpoints[_account][nCheckpoints - 1].votes : 0;
}
}
/// @notice The number of votes for an account at a past timestamp
/// @param _account The account address
/// @param _timestamp The past timestamp
function getPastVotes(address _account, uint256 _timestamp) public view returns (uint256) {
// Ensure the given timestamp is in the past
if (_timestamp >= block.timestamp) revert INVALID_TIMESTAMP();
// Get the account's number of checkpoints
uint256 nCheckpoints = numCheckpoints[_account];
// If there are none return 0
if (nCheckpoints == 0) return 0;
// Get the account's checkpoints
mapping(uint256 => Checkpoint) storage accountCheckpoints = checkpoints[_account];
unchecked {
// Get the latest checkpoint id
// Cannot underflow as `nCheckpoints` is ensured to be greater than 0
uint256 lastCheckpoint = nCheckpoints - 1;
// If the latest checkpoint has a valid timestamp, return its number of votes
if (accountCheckpoints[lastCheckpoint].timestamp <= _timestamp) return accountCheckpoints[lastCheckpoint].votes;
// If the first checkpoint doesn't have a valid timestamp, return 0
if (accountCheckpoints[0].timestamp > _timestamp) return 0;
// Otherwise, find a checkpoint with a valid timestamp
// Use the latest id as the initial upper bound
uint256 high = lastCheckpoint;
uint256 low;
uint256 middle;
// Used to temporarily hold a checkpoint
Checkpoint memory cp;
// While a valid checkpoint is to be found:
while (high > low) {
// Find the id of the middle checkpoint
middle = high - (high - low) / 2;
// Get the middle checkpoint
cp = accountCheckpoints[middle];
// If the timestamp is a match:
if (cp.timestamp == _timestamp) {
// Return the voting weight
return cp.votes;
// Else if the timestamp is before the one looking for:
} else if (cp.timestamp < _timestamp) {
// Update the lower bound
low = middle;
// Else update the upper bound
} else {
high = middle - 1;
}
}
return accountCheckpoints[low].votes;
}
}
/// ///
/// DELEGATION ///
/// ///
/// @notice The delegate for an account
/// @param _account The account address
function delegates(address _account) public view returns (address) {
address current = delegation[_account];
return current == address(0) ? _account : current;
}
/// @notice Delegates votes to an account
/// @param _to The address delegating votes to
function delegate(address _to) external {
_delegate(msg.sender, _to);
}
/// @notice Delegates votes from a signer to an account
/// @param _from The address delegating votes from
/// @param _to The address delegating votes to
/// @param _deadline The signature deadline
/// @param _v The 129th byte and chain id of the signature
/// @param _r The first 64 bytes of the signature
/// @param _s Bytes 64-128 of the signature
function delegateBySig(
address _from,
address _to,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external {
// Ensure the signature has not expired
if (block.timestamp > _deadline) revert EXPIRED_SIGNATURE();
// Used to store the digest
bytes32 digest;
// Cannot realistically overflow
unchecked {
// Compute the hash of the domain seperator with the typed delegation data
digest = keccak256(
abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATION_TYPEHASH, _from, _to, nonces[_from]++, _deadline)))
);
}
// Recover the message signer
address recoveredAddress = ecrecover(digest, _v, _r, _s);
// Ensure the recovered signer is the voter
if (recoveredAddress == address(0) || recoveredAddress != _from) revert INVALID_SIGNATURE();
// Update the delegate
_delegate(_from, _to);
}
/// @dev Updates delegate addresses
/// @param _from The address delegating votes from
/// @param _to The address delegating votes to
function _delegate(address _from, address _to) internal {
// If address(0) is being delegated to, update the op as a self-delegate
if (_to == address(0)) _to = _from;
// Get the previous delegate
address prevDelegate = delegates(_from);
// Store the new delegate
delegation[_from] = _to;
emit DelegateChanged(_from, prevDelegate, _to);
// Transfer voting weight from the previous delegate to the new delegate
_moveDelegateVotes(prevDelegate, _to, balanceOf(_from));
}
/// @dev Transfers voting weight
/// @param _from The address delegating votes from
/// @param _to The address delegating votes to
/// @param _amount The number of votes delegating
function _moveDelegateVotes(
address _from,
address _to,
uint256 _amount
) internal {
unchecked {
// If voting weight is being transferred:
if (_from != _to && _amount > 0) {
// If this isn't a token mint:
if (_from != address(0)) {
// Get the sender's number of checkpoints
uint256 newCheckpointId = numCheckpoints[_from];
// Used to store their previous checkpoint id
uint256 prevCheckpointId;
// Used to store their previous checkpoint's voting weight
uint256 prevTotalVotes;
// Used to store their previous checkpoint's timestamp
uint256 prevTimestamp;
// If this isn't the sender's first checkpoint:
if (newCheckpointId != 0) {
// Get their previous checkpoint's id
prevCheckpointId = newCheckpointId - 1;
// Get their previous checkpoint's voting weight
prevTotalVotes = checkpoints[_from][prevCheckpointId].votes;
// Get their previous checkpoint's timestamp
prevTimestamp = checkpoints[_from][prevCheckpointId].timestamp;
}
// Update their voting weight
_writeCheckpoint(_from, newCheckpointId, prevCheckpointId, prevTimestamp, prevTotalVotes, prevTotalVotes - _amount);
}
// If this isn't a token burn:
if (_to != address(0)) {
// Get the recipients's number of checkpoints
uint256 nCheckpoints = numCheckpoints[_to];
// Used to store their previous checkpoint id
uint256 prevCheckpointId;
// Used to store their previous checkpoint's voting weight
uint256 prevTotalVotes;
// Used to store their previous checkpoint's timestamp
uint256 prevTimestamp;
// If this isn't the recipient's first checkpoint:
if (nCheckpoints != 0) {
// Get their previous checkpoint's id
prevCheckpointId = nCheckpoints - 1;
// Get their previous checkpoint's voting weight
prevTotalVotes = checkpoints[_to][prevCheckpointId].votes;
// Get their previous checkpoint's timestamp
prevTimestamp = checkpoints[_to][prevCheckpointId].timestamp;
}
// Update their voting weight
_writeCheckpoint(_to, nCheckpoints, prevCheckpointId, prevTimestamp, prevTotalVotes, prevTotalVotes + _amount);
}
}
}
}
/// @dev Records a checkpoint
/// @param _account The account address
/// @param _newId The new checkpoint id
/// @param _prevId The previous checkpoint id
/// @param _prevTimestamp The previous checkpoint timestamp
/// @param _prevTotalVotes The previous checkpoint voting weight
/// @param _newTotalVotes The new checkpoint voting weight
function _writeCheckpoint(
address _account,
uint256 _newId,
uint256 _prevId,
uint256 _prevTimestamp,
uint256 _prevTotalVotes,
uint256 _newTotalVotes
) private {
unchecked {
// If the new checkpoint is not the user's first AND has the timestamp of the previous checkpoint:
if (_newId > 0 && _prevTimestamp == block.timestamp) {
// Just update the previous checkpoint's votes
checkpoints[_account][_prevId].votes = uint192(_newTotalVotes);
// Else write a new checkpoint:
} else {
// Get the pointer to store the checkpoint
Checkpoint storage checkpoint = checkpoints[_account][_newId];
// Store the new voting weight and the current time
checkpoint.votes = uint192(_newTotalVotes);
checkpoint.timestamp = uint64(block.timestamp);
// Increment the account's number of checkpoints
++numCheckpoints[_account];
}
emit DelegateVotesChanged(_account, _prevTotalVotes, _newTotalVotes);
}
}
/// @dev Enables each NFT to equal 1 vote
/// @param _from The token sender
/// @param _to The token recipient
/// @param _tokenId The ERC-721 token id
function _afterTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal override {
// Transfer 1 vote from the sender to the recipient
_moveDelegateVotes(delegates(_from), delegates(_to), 1);
super._afterTokenTransfer(_from, _to, _tokenId);
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IERC721Votes } from "../lib/interfaces/IERC721Votes.sol";
import { IManager } from "../manager/IManager.sol";
import { TokenTypesV1 } from "./types/TokenTypesV1.sol";
/// @title IToken
/// @author Rohan Kulkarni
/// @notice The external Token events, errors and functions
interface IToken is IUUPS, IERC721Votes, TokenTypesV1 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a token is scheduled to be allocated
/// @param baseTokenId The
/// @param founderId The founder's id
/// @param founder The founder's vesting details
event MintScheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
/// @notice Emitted when a token allocation is unscheduled (removed)
/// @param baseTokenId The token ID % 100
/// @param founderId The founder's id
/// @param founder The founder's vesting details
event MintUnscheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
/// @notice Emitted when a tokens founders are deleted from storage
/// @param newFounders the list of founders
event FounderAllocationsCleared(IManager.FounderParams[] newFounders);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the founder ownership exceeds 100 percent
error INVALID_FOUNDER_OWNERSHIP();
/// @dev Reverts if the caller was not the auction contract
error ONLY_AUCTION();
/// @dev Reverts if no metadata was generated upon mint
error NO_METADATA_GENERATED();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's ERC-721 token
/// @param founders The founding members to receive vesting allocations
/// @param initStrings The encoded token and metadata initialization strings
/// @param metadataRenderer The token's metadata renderer
/// @param auction The token's auction house
function initialize(
IManager.FounderParams[] calldata founders,
bytes calldata initStrings,
address metadataRenderer,
address auction,
address initialOwner
) external;
/// @notice Mints tokens to the auction house for bidding and handles founder vesting
function mint() external returns (uint256 tokenId);
/// @notice Burns a token that did not see any bids
/// @param tokenId The ERC-721 token id
function burn(uint256 tokenId) external;
/// @notice The URI for a token
/// @param tokenId The ERC-721 token id
function tokenURI(uint256 tokenId) external view returns (string memory);
/// @notice The URI for the contract
function contractURI() external view returns (string memory);
/// @notice The number of founders
function totalFounders() external view returns (uint256);
/// @notice The founders total percent ownership
function totalFounderOwnership() external view returns (uint256);
/// @notice The vesting details of a founder
/// @param founderId The founder id
function getFounder(uint256 founderId) external view returns (Founder memory);
/// @notice The vesting details of all founders
function getFounders() external view returns (Founder[] memory);
/// @notice Update the list of allocation owners
/// @param newFounders the full list of FounderParam structs
function updateFounders(IManager.FounderParams[] calldata newFounders) external;
/// @notice The founder scheduled to receive the given token id
/// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
/// @param tokenId The ERC-721 token id
function getScheduledRecipient(uint256 tokenId) external view returns (Founder memory);
/// @notice The total supply of tokens
function totalSupply() external view returns (uint256);
/// @notice The token's auction house
function auction() external view returns (address);
/// @notice The token's metadata renderer
function metadataRenderer() external view returns (address);
/// @notice The owner of the token and metadata renderer
function owner() external view returns (address);
/// @notice Callback called by auction on first auction started to transfer ownership to treasury from founder
function onFirstAuctionStarted() external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IEIP712 } from "../interfaces/IEIP712.sol";
import { Initializable } from "../utils/Initializable.sol";
/// @title EIP712
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/cryptography/draft-EIP712Upgradeable.sol)
/// - Uses custom errors declared in IEIP712
/// - Caches `INITIAL_CHAIN_ID` and `INITIAL_DOMAIN_SEPARATOR` upon initialization
/// - Adds mapping for account nonces
abstract contract EIP712 is IEIP712, Initializable {
/// ///
/// CONSTANTS ///
/// ///
/// @dev The EIP-712 domain typehash
bytes32 internal constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// ///
/// STORAGE ///
/// ///
/// @notice The hash of the EIP-712 domain name
bytes32 internal HASHED_NAME;
/// @notice The hash of the EIP-712 domain version
bytes32 internal HASHED_VERSION;
/// @notice The domain separator computed upon initialization
bytes32 internal INITIAL_DOMAIN_SEPARATOR;
/// @notice The chain id upon initialization
uint256 internal INITIAL_CHAIN_ID;
/// @notice The account nonces
/// @dev Account => Nonce
mapping(address => uint256) internal nonces;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes EIP-712 support
/// @param _name The EIP-712 domain name
/// @param _version The EIP-712 domain version
function __EIP712_init(string memory _name, string memory _version) internal onlyInitializing {
HASHED_NAME = keccak256(bytes(_name));
HASHED_VERSION = keccak256(bytes(_version));
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
/// @notice The current nonce for an account
/// @param _account The account address
function nonce(address _account) external view returns (uint256) {
return nonces[_account];
}
/// @notice The EIP-712 domain separator
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
}
/// @dev Computes the EIP-712 domain separator
function _computeDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_TYPEHASH, HASHED_NAME, HASHED_VERSION, block.chainid, address(this)));
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
abstract contract VersionedContract {
function contractVersion() external pure returns (string memory) {
return "1.1.0";
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IOwnable } from "../lib/interfaces/IOwnable.sol";
import { IPausable } from "../lib/interfaces/IPausable.sol";
/// @title IAuction
/// @author Rohan Kulkarni
/// @notice The external Auction events, errors, and functions
interface IAuction is IUUPS, IOwnable, IPausable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a bid is placed
/// @param tokenId The ERC-721 token id
/// @param bidder The address of the bidder
/// @param amount The amount of ETH
/// @param extended If the bid extended the auction
/// @param endTime The end time of the auction
event AuctionBid(uint256 tokenId, address bidder, uint256 amount, bool extended, uint256 endTime);
/// @notice Emitted when an auction is settled
/// @param tokenId The ERC-721 token id of the settled auction
/// @param winner The address of the winning bidder
/// @param amount The amount of ETH raised from the winning bid
event AuctionSettled(uint256 tokenId, address winner, uint256 amount);
/// @notice Emitted when an auction is created
/// @param tokenId The ERC-721 token id of the created auction
/// @param startTime The start time of the created auction
/// @param endTime The end time of the created auction
event AuctionCreated(uint256 tokenId, uint256 startTime, uint256 endTime);
/// @notice Emitted when the auction duration is updated
/// @param duration The new auction duration
event DurationUpdated(uint256 duration);
/// @notice Emitted when the reserve price is updated
/// @param reservePrice The new reserve price
event ReservePriceUpdated(uint256 reservePrice);
/// @notice Emitted when the min bid increment percentage is updated
/// @param minBidIncrementPercentage The new min bid increment percentage
event MinBidIncrementPercentageUpdated(uint256 minBidIncrementPercentage);
/// @notice Emitted when the time buffer is updated
/// @param timeBuffer The new time buffer
event TimeBufferUpdated(uint256 timeBuffer);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if a bid is placed for the wrong token
error INVALID_TOKEN_ID();
/// @dev Reverts if a bid is placed for an auction thats over
error AUCTION_OVER();
/// @dev Reverts if a bid is placed for an auction that hasn't started
error AUCTION_NOT_STARTED();
/// @dev Reverts if attempting to settle an active auction
error AUCTION_ACTIVE();
/// @dev Reverts if attempting to settle an auction that was already settled
error AUCTION_SETTLED();
/// @dev Reverts if a bid does not meet the reserve price
error RESERVE_PRICE_NOT_MET();
/// @dev Reverts if a bid does not meet the minimum bid
error MINIMUM_BID_NOT_MET();
/// @dev Error for when the bid increment is set to 0.
error MIN_BID_INCREMENT_1_PERCENT();
/// @dev Reverts if the contract does not have enough ETH
error INSOLVENT();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// @dev Thrown if the WETH contract throws a failure on transfer
error FAILING_WETH_TRANSFER();
/// @dev Thrown if the auction creation failed
error AUCTION_CREATE_FAILED_TO_LAUNCH();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's auction house
/// @param token The ERC-721 token address
/// @param founder The founder responsible for starting the first auction
/// @param treasury The treasury address where ETH will be sent
/// @param duration The duration of each auction
/// @param reservePrice The reserve price of each auction
function initialize(
address token,
address founder,
address treasury,
uint256 duration,
uint256 reservePrice
) external;
/// @notice Creates a bid for the current token
/// @param tokenId The ERC-721 token id
function createBid(uint256 tokenId) external payable;
/// @notice Settles the current auction and creates the next one
function settleCurrentAndCreateNewAuction() external;
/// @notice Settles the latest auction when the contract is paused
function settleAuction() external;
/// @notice Pauses the auction house
function pause() external;
/// @notice Unpauses the auction house
function unpause() external;
/// @notice The time duration of each auction
function duration() external view returns (uint256);
/// @notice The reserve price of each auction
function reservePrice() external view returns (uint256);
/// @notice The minimum amount of time to place a bid during an active auction
function timeBuffer() external view returns (uint256);
/// @notice The minimum percentage an incoming bid must raise the highest bid
function minBidIncrement() external view returns (uint256);
/// @notice Updates the time duration of each auction
/// @param duration The new time duration
function setDuration(uint256 duration) external;
/// @notice Updates the reserve price of each auction
/// @param reservePrice The new reserve price
function setReservePrice(uint256 reservePrice) external;
/// @notice Updates the time buffer of each auction
/// @param timeBuffer The new time buffer
function setTimeBuffer(uint256 timeBuffer) external;
/// @notice Updates the minimum bid increment of each subsequent bid
/// @param percentage The new percentage
function setMinimumBidIncrement(uint256 percentage) external;
/// @notice Get the address of the treasury
function treasury() external returns (address);
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { Initializable } from "../utils/Initializable.sol";
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (security/ReentrancyGuardUpgradeable.sol)
/// - Uses custom error `REENTRANCY()`
abstract contract ReentrancyGuard is Initializable {
/// ///
/// STORAGE ///
/// ///
/// @dev Indicates a function has not been entered
uint256 internal constant _NOT_ENTERED = 1;
/// @dev Indicates a function has been entered
uint256 internal constant _ENTERED = 2;
/// @notice The reentrancy status of a function
uint256 internal _status;
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if attempted reentrancy
error REENTRANCY();
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes the reentrancy guard
function __ReentrancyGuard_init() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/// @dev Ensures a function cannot be reentered
modifier nonReentrant() {
if (_status == _ENTERED) revert REENTRANCY();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721 } from "./IERC721.sol";
import { IEIP712 } from "./IEIP712.sol";
/// @title IERC721Votes
/// @author Rohan Kulkarni
/// @notice The external ERC721Votes events, errors, and functions
interface IERC721Votes is IERC721, IEIP712 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when an account changes their delegate
event DelegateChanged(address indexed delegator, address indexed from, address indexed to);
/// @notice Emitted when a delegate's number of votes is updated
event DelegateVotesChanged(address indexed delegate, uint256 prevTotalVotes, uint256 newTotalVotes);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the timestamp provided isn't in the past
error INVALID_TIMESTAMP();
/// ///
/// STRUCTS ///
/// ///
/// @notice The checkpoint data type
/// @param timestamp The recorded timestamp
/// @param votes The voting weight
struct Checkpoint {
uint64 timestamp;
uint192 votes;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The current number of votes for an account
/// @param account The account address
function getVotes(address account) external view returns (uint256);
/// @notice The number of votes for an account at a past timestamp
/// @param account The account address
/// @param timestamp The past timestamp
function getPastVotes(address account, uint256 timestamp) external view returns (uint256);
/// @notice The delegate for an account
/// @param account The account address
function delegates(address account) external view returns (address);
/// @notice Delegates votes to an account
/// @param to The address delegating votes to
function delegate(address to) external;
/// @notice Delegates votes from a signer to an account
/// @param from The address delegating votes from
/// @param to The address delegating votes to
/// @param deadline The signature deadline
/// @param v The 129th byte and chain id of the signature
/// @param r The first 64 bytes of the signature
/// @param s Bytes 64-128 of the signature
function delegateBySig(
address from,
address to,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
/IBaseMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../../../lib/interfaces/IUUPS.sol";
/// @title IBaseMetadata
/// @author Rohan Kulkarni
/// @notice The external Base Metadata errors and functions
interface IBaseMetadata is IUUPS {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's token metadata renderer
/// @param initStrings The encoded token and metadata initialization strings
/// @param token The associated ERC-721 token address
function initialize(
bytes calldata initStrings,
address token
) external;
/// @notice Generates attributes for a token upon mint
/// @param tokenId The ERC-721 token id
function onMinted(uint256 tokenId) external returns (bool);
/// @notice The token URI
/// @param tokenId The ERC-721 token id
function tokenURI(uint256 tokenId) external view returns (string memory);
/// @notice The contract URI
function contractURI() external view returns (string memory);
/// @notice The associated ERC-721 token
function token() external view returns (address);
/// @notice Get metadata owner address
function owner() external view returns (address);
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IOwnable
/// @author Rohan Kulkarni
/// @notice The external Ownable events, errors, and functions
interface IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when ownership has been updated
/// @param prevOwner The previous owner address
/// @param newOwner The new owner address
event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
/// @notice Emitted when an ownership transfer is pending
/// @param owner The current owner address
/// @param pendingOwner The pending new owner address
event OwnerPending(address indexed owner, address indexed pendingOwner);
/// @notice Emitted when a pending ownership transfer has been canceled
/// @param owner The current owner address
/// @param canceledOwner The canceled owner address
event OwnerCanceled(address indexed owner, address indexed canceledOwner);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if an unauthorized user calls an owner function
error ONLY_OWNER();
/// @dev Reverts if an unauthorized user calls a pending owner function
error ONLY_PENDING_OWNER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The address of the owner
function owner() external view returns (address);
/// @notice The address of the pending owner
function pendingOwner() external view returns (address);
/// @notice Forces an ownership transfer
/// @param newOwner The new owner address
function transferOwnership(address newOwner) external;
/// @notice Initiates a two-step ownership transfer
/// @param newOwner The new owner address
function safeTransferOwnership(address newOwner) external;
/// @notice Accepts an ownership transfer
function acceptOwnership() external;
/// @notice Cancels a pending ownership transfer
function cancelOwnershipTransfer() external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IOwnable } from "../lib/interfaces/IOwnable.sol";
/// @title IManager
/// @author Rohan Kulkarni
/// @notice The external Manager events, errors, structs and functions
interface IManager is IUUPS, IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a DAO is deployed
/// @param token The ERC-721 token address
/// @param metadata The metadata renderer address
/// @param auction The auction address
/// @param treasury The treasury address
/// @param governor The governor address
event DAODeployed(address token, address metadata, address auction, address treasury, address governor);
/// @notice Emitted when an upgrade is registered by the Builder DAO
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
event UpgradeRegistered(address baseImpl, address upgradeImpl);
/// @notice Emitted when an upgrade is unregistered by the Builder DAO
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
event UpgradeRemoved(address baseImpl, address upgradeImpl);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if at least one founder is not provided upon deploy
error FOUNDER_REQUIRED();
/// ///
/// STRUCTS ///
/// ///
/// @notice The founder parameters
/// @param wallet The wallet address
/// @param ownershipPct The percent ownership of the token
/// @param vestExpiry The timestamp that vesting expires
struct FounderParams {
address wallet;
uint256 ownershipPct;
uint256 vestExpiry;
}
/// @notice DAO Version Information information struct
struct DAOVersionInfo {
string token;
string metadata;
string auction;
string treasury;
string governor;
}
/// @notice The ERC-721 token parameters
/// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri
struct TokenParams {
bytes initStrings;
}
/// @notice The auction parameters
/// @param reservePrice The reserve price of each auction
/// @param duration The duration of each auction
struct AuctionParams {
uint256 reservePrice;
uint256 duration;
}
/// @notice The governance parameters
/// @param timelockDelay The time delay to execute a queued transaction
/// @param votingDelay The time delay to vote on a created proposal
/// @param votingPeriod The time period to vote on a proposal
/// @param proposalThresholdBps The basis points of the token supply required to create a proposal
/// @param quorumThresholdBps The basis points of the token supply required to reach quorum
/// @param vetoer The address authorized to veto proposals (address(0) if none desired)
struct GovParams {
uint256 timelockDelay;
uint256 votingDelay;
uint256 votingPeriod;
uint256 proposalThresholdBps;
uint256 quorumThresholdBps;
address vetoer;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The token implementation address
function tokenImpl() external view returns (address);
/// @notice The metadata renderer implementation address
function metadataImpl() external view returns (address);
/// @notice The auction house implementation address
function auctionImpl() external view returns (address);
/// @notice The treasury implementation address
function treasuryImpl() external view returns (address);
/// @notice The governor implementation address
function governorImpl() external view returns (address);
/// @notice Deploys a DAO with custom token, auction, and governance settings
/// @param founderParams The DAO founder(s)
/// @param tokenParams The ERC-721 token settings
/// @param auctionParams The auction settings
/// @param govParams The governance settings
function deploy(
FounderParams[] calldata founderParams,
TokenParams calldata tokenParams,
AuctionParams calldata auctionParams,
GovParams calldata govParams
)
external
returns (
address token,
address metadataRenderer,
address auction,
address treasury,
address governor
);
/// @notice A DAO's remaining contract addresses from its token address
/// @param token The ERC-721 token address
function getAddresses(address token)
external
returns (
address metadataRenderer,
address auction,
address treasury,
address governor
);
/// @notice If an implementation is registered by the Builder DAO as an optional upgrade
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function isRegisteredUpgrade(address baseImpl, address upgradeImpl) external view returns (bool);
/// @notice Called by the Builder DAO to offer opt-in implementation upgrades for all other DAOs
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function registerUpgrade(address baseImpl, address upgradeImpl) external;
/// @notice Called by the Builder DAO to remove an upgrade
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function removeUpgrade(address baseImpl, address upgradeImpl) external;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IOwnable } from "../interfaces/IOwnable.sol";
import { Initializable } from "../utils/Initializable.sol";
/// @title Ownable
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (access/OwnableUpgradeable.sol)
/// - Uses custom errors declared in IOwnable
/// - Adds optional two-step ownership transfer (`safeTransferOwnership` + `acceptOwnership`)
abstract contract Ownable is IOwnable, Initializable {
/// ///
/// STORAGE ///
/// ///
/// @dev The address of the owner
address internal _owner;
/// @dev The address of the pending owner
address internal _pendingOwner;
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures the caller is the owner
modifier onlyOwner() {
if (msg.sender != _owner) revert ONLY_OWNER();
_;
}
/// @dev Ensures the caller is the pending owner
modifier onlyPendingOwner() {
if (msg.sender != _pendingOwner) revert ONLY_PENDING_OWNER();
_;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes contract ownership
/// @param _initialOwner The initial owner address
function __Ownable_init(address _initialOwner) internal onlyInitializing {
_owner = _initialOwner;
emit OwnerUpdated(address(0), _initialOwner);
}
/// @notice The address of the owner
function owner() public virtual view returns (address) {
return _owner;
}
/// @notice The address of the pending owner
function pendingOwner() public view returns (address) {
return _pendingOwner;
}
/// @notice Forces an ownership transfer from the last owner
/// @param _newOwner The new owner address
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/// @notice Forces an ownership transfer from any sender
/// @param _newOwner New owner to transfer contract to
/// @dev Ensure is called only from trusted internal code, no access control checks.
function _transferOwnership(address _newOwner) internal {
emit OwnerUpdated(_owner, _newOwner);
_owner = _newOwner;
if (_pendingOwner != address(0)) delete _pendingOwner;
}
/// @notice Initiates a two-step ownership transfer
/// @param _newOwner The new owner address
function safeTransferOwnership(address _newOwner) public onlyOwner {
_pendingOwner = _newOwner;
emit OwnerPending(_owner, _newOwner);
}
/// @notice Accepts an ownership transfer
function acceptOwnership() public onlyPendingOwner {
emit OwnerUpdated(_owner, msg.sender);
_owner = _pendingOwner;
delete _pendingOwner;
}
/// @notice Cancels a pending ownership transfer
function cancelOwnershipTransfer() public onlyOwner {
emit OwnerCanceled(_owner, _pendingOwner);
delete _pendingOwner;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IInitializable } from "../interfaces/IInitializable.sol";
import { Address } from "../utils/Address.sol";
/// @title Initializable
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/Initializable.sol)
/// - Uses custom errors declared in IInitializable
abstract contract Initializable is IInitializable {
/// ///
/// STORAGE ///
/// ///
/// @dev Indicates the contract has been initialized
uint8 internal _initialized;
/// @dev Indicates the contract is being initialized
bool internal _initializing;
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures an initialization function is only called within an `initializer` or `reinitializer` function
modifier onlyInitializing() {
if (!_initializing) revert NOT_INITIALIZING();
_;
}
/// @dev Enables initializing upgradeable contracts
modifier initializer() {
bool isTopLevelCall = !_initializing;
if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED();
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/// @dev Enables initializer versioning
/// @param _version The version to set
modifier reinitializer(uint8 _version) {
if (_initializing || _initialized >= _version) revert ALREADY_INITIALIZED();
_initialized = _version;
_initializing = true;
_;
_initializing = false;
emit Initialized(_version);
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Prevents future initialization
function _disableInitializers() internal virtual {
if (_initializing) revert INITIALIZING();
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import { IERC1967Upgrade } from "./IERC1967Upgrade.sol";
/// @title IUUPS
/// @author Rohan Kulkarni
/// @notice The external UUPS errors and functions
interface IUUPS is IERC1967Upgrade, IERC1822Proxiable {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if not called directly
error ONLY_CALL();
/// @dev Reverts if not called via delegatecall
error ONLY_DELEGATECALL();
/// @dev Reverts if not called via proxy
error ONLY_PROXY();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Upgrades to an implementation
/// @param newImpl The new implementation address
function upgradeTo(address newImpl) external;
/// @notice Upgrades to an implementation with an additional function call
/// @param newImpl The new implementation address
/// @param data The encoded function call
function upgradeToAndCall(address newImpl, bytes memory data) external payable;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721 } from "../interfaces/IERC721.sol";
import { Initializable } from "../utils/Initializable.sol";
import { ERC721TokenReceiver } from "../utils/TokenReceiver.sol";
import { Address } from "../utils/Address.sol";
/// @title ERC721
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/ERC721Upgradeable.sol)
/// - Uses custom errors declared in IERC721
abstract contract ERC721 is IERC721, Initializable {
/// ///
/// STORAGE ///
/// ///
/// @notice The token name
string public name;
/// @notice The token symbol
string public symbol;
/// @notice The token owners
/// @dev ERC-721 token id => Owner
mapping(uint256 => address) internal owners;
/// @notice The owner balances
/// @dev Owner => Balance
mapping(address => uint256) internal balances;
/// @notice The token approvals
/// @dev ERC-721 token id => Manager
mapping(uint256 => address) internal tokenApprovals;
/// @notice The balance approvals
/// @dev Owner => Operator => Approved
mapping(address => mapping(address => bool)) internal operatorApprovals;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes an ERC-721 token
/// @param _name The ERC-721 token name
/// @param _symbol The ERC-721 token symbol
function __ERC721_init(string memory _name, string memory _symbol) internal onlyInitializing {
name = _name;
symbol = _symbol;
}
/// @notice The token URI
/// @param _tokenId The ERC-721 token id
function tokenURI(uint256 _tokenId) public view virtual returns (string memory) {}
/// @notice The contract URI
function contractURI() public view virtual returns (string memory) {}
/// @notice If the contract implements an interface
/// @param _interfaceId The interface id
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return
_interfaceId == 0x01ffc9a7 || // ERC165 Interface ID
_interfaceId == 0x80ac58cd || // ERC721 Interface ID
_interfaceId == 0x5b5e139f; // ERC721Metadata Interface ID
}
/// @notice The account approved to manage a token
/// @param _tokenId The ERC-721 token id
function getApproved(uint256 _tokenId) external view returns (address) {
return tokenApprovals[_tokenId];
}
/// @notice If an operator is authorized to manage all of an owner's tokens
/// @param _owner The owner address
/// @param _operator The operator address
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
return operatorApprovals[_owner][_operator];
}
/// @notice The number of tokens owned
/// @param _owner The owner address
function balanceOf(address _owner) public view returns (uint256) {
if (_owner == address(0)) revert ADDRESS_ZERO();
return balances[_owner];
}
/// @notice The owner of a token
/// @param _tokenId The ERC-721 token id
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = owners[_tokenId];
if (owner == address(0)) revert INVALID_OWNER();
return owner;
}
/// @notice Authorizes an account to manage a token
/// @param _to The account address
/// @param _tokenId The ERC-721 token id
function approve(address _to, uint256 _tokenId) external {
address owner = owners[_tokenId];
if (msg.sender != owner && !operatorApprovals[owner][msg.sender]) revert INVALID_APPROVAL();
tokenApprovals[_tokenId] = _to;
emit Approval(owner, _to, _tokenId);
}
/// @notice Authorizes an account to manage all tokens
/// @param _operator The account address
/// @param _approved If permission is being given or removed
function setApprovalForAll(address _operator, bool _approved) external {
operatorApprovals[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/// @notice Transfers a token from sender to recipient
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function transferFrom(
address _from,
address _to,
uint256 _tokenId
) public {
if (_from != owners[_tokenId]) revert INVALID_OWNER();
if (_to == address(0)) revert ADDRESS_ZERO();
if (msg.sender != _from && !operatorApprovals[_from][msg.sender] && msg.sender != tokenApprovals[_tokenId]) revert INVALID_APPROVAL();
_beforeTokenTransfer(_from, _to, _tokenId);
unchecked {
--balances[_from];
++balances[_to];
}
owners[_tokenId] = _to;
delete tokenApprovals[_tokenId];
emit Transfer(_from, _to, _tokenId);
_afterTokenTransfer(_from, _to, _tokenId);
}
/// @notice Safe transfers a token from sender to recipient
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
) external {
transferFrom(_from, _to, _tokenId);
if (
Address.isContract(_to) &&
ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, "") != ERC721TokenReceiver.onERC721Received.selector
) revert INVALID_RECIPIENT();
}
/// @notice Safe transfers a token from sender to recipient with additional data
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
) external {
transferFrom(_from, _to, _tokenId);
if (
Address.isContract(_to) &&
ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) != ERC721TokenReceiver.onERC721Received.selector
) revert INVALID_RECIPIENT();
}
/// @dev Mints a token to a recipient
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _mint(address _to, uint256 _tokenId) internal virtual {
if (_to == address(0)) revert ADDRESS_ZERO();
if (owners[_tokenId] != address(0)) revert ALREADY_MINTED();
_beforeTokenTransfer(address(0), _to, _tokenId);
unchecked {
++balances[_to];
}
owners[_tokenId] = _to;
emit Transfer(address(0), _to, _tokenId);
_afterTokenTransfer(address(0), _to, _tokenId);
}
/// @dev Burns a token to a recipient
/// @param _tokenId The ERC-721 token id
function _burn(uint256 _tokenId) internal virtual {
address owner = owners[_tokenId];
if (owner == address(0)) revert NOT_MINTED();
_beforeTokenTransfer(owner, address(0), _tokenId);
unchecked {
--balances[owner];
}
delete owners[_tokenId];
delete tokenApprovals[_tokenId];
emit Transfer(owner, address(0), _tokenId);
_afterTokenTransfer(owner, address(0), _tokenId);
}
/// @dev Hook called before a token transfer
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _beforeTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual {}
/// @dev Hook called after a token transfer
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _afterTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual {}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IPausable
/// @author Rohan Kulkarni
/// @notice The external Pausable events, errors, and functions
interface IPausable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the contract is paused
/// @param user The address that paused the contract
event Paused(address user);
/// @notice Emitted when the contract is unpaused
/// @param user The address that unpaused the contract
event Unpaused(address user);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if called when the contract is paused
error PAUSED();
/// @dev Reverts if called when the contract is unpaused
error UNPAUSED();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice If the contract is paused
function paused() external view returns (bool);
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol";
/// @title TokenTypesV1
/// @author Rohan Kulkarni
/// @notice The Token custom data types
interface TokenTypesV1 {
/// @notice The settings type
/// @param auction The DAO auction house
/// @param totalSupply The number of active tokens
/// @param numFounders The number of vesting recipients
/// @param metadatarenderer The token metadata renderer
/// @param mintCount The number of minted tokens
/// @param totalPercentage The total percentage owned by founders
struct Settings {
address auction;
uint88 totalSupply;
uint8 numFounders;
IBaseMetadata metadataRenderer;
uint88 mintCount;
uint8 totalOwnership;
}
/// @notice The founder type
/// @param wallet The address where tokens are sent
/// @param ownershipPct The percentage of token ownership
/// @param vestExpiry The timestamp when vesting ends
struct Founder {
address wallet;
uint8 ownershipPct;
uint32 vestExpiry;
}
}
/StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
}
/draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IERC1967Upgrade
/// @author Rohan Kulkarni
/// @notice The external ERC1967Upgrade events and errors
interface IERC1967Upgrade {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the implementation is upgraded
/// @param impl The address of the implementation
event Upgraded(address impl);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if an implementation is an invalid upgrade
/// @param impl The address of the invalid implementation
error INVALID_UPGRADE(address impl);
/// @dev Reverts if an implementation upgrade is not stored at the storage slot of the original
error UNSUPPORTED_UUID();
/// @dev Reverts if an implementation does not support ERC1822 proxiableUUID()
error ONLY_UUPS();
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IEIP712
/// @author Rohan Kulkarni
/// @notice The external EIP712 errors and functions
interface IEIP712 {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the deadline has passed to submit a signature
error EXPIRED_SIGNATURE();
/// @dev Reverts if the recovered signature is invalid
error INVALID_SIGNATURE();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The sig nonce for an account
/// @param account The account address
function nonce(address account) external view returns (uint256);
/// @notice The EIP-712 domain separator
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
import { IERC1967Upgrade } from "../interfaces/IERC1967Upgrade.sol";
import { Address } from "../utils/Address.sol";
/// @title ERC1967Upgrade
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Upgrade.sol)
/// - Uses custom errors declared in IERC1967Upgrade
/// - Removes ERC1967 admin and beacon support
abstract contract ERC1967Upgrade is IERC1967Upgrade {
/// ///
/// CONSTANTS ///
/// ///
/// @dev bytes32(uint256(keccak256('eip1967.proxy.rollback')) - 1)
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/// @dev bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Upgrades to an implementation with security checks for UUPS proxies and an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function _upgradeToAndCallUUPS(
address _newImpl,
bytes memory _data,
bool _forceCall
) internal {
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(_newImpl);
} else {
try IERC1822Proxiable(_newImpl).proxiableUUID() returns (bytes32 slot) {
if (slot != _IMPLEMENTATION_SLOT) revert UNSUPPORTED_UUID();
} catch {
revert ONLY_UUPS();
}
_upgradeToAndCall(_newImpl, _data, _forceCall);
}
}
/// @dev Upgrades to an implementation with an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function _upgradeToAndCall(
address _newImpl,
bytes memory _data,
bool _forceCall
) internal {
_upgradeTo(_newImpl);
if (_data.length > 0 || _forceCall) {
Address.functionDelegateCall(_newImpl, _data);
}
}
/// @dev Performs an implementation upgrade
/// @param _newImpl The new implementation address
function _upgradeTo(address _newImpl) internal {
_setImplementation(_newImpl);
emit Upgraded(_newImpl);
}
/// @dev Stores the address of an implementation
/// @param _impl The implementation address
function _setImplementation(address _impl) private {
if (!Address.isContract(_impl)) revert INVALID_UPGRADE(_impl);
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = _impl;
}
/// @dev The address of the current implementation
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { TokenTypesV1 } from "../types/TokenTypesV1.sol";
/// @title TokenStorageV1
/// @author Rohan Kulkarni
/// @notice The Token storage contract
contract TokenStorageV1 is TokenTypesV1 {
/// @notice The token settings
Settings internal settings;
/// @notice The vesting details of a founder
/// @dev Founder id => Founder
mapping(uint256 => Founder) internal founder;
/// @notice The recipient of a token
/// @dev ERC-721 token id => Founder
mapping(uint256 => Founder) internal tokenRecipient;
}
/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IInitializable
/// @author Rohan Kulkarni
/// @notice The external Initializable events and errors
interface IInitializable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the contract has been initialized or reinitialized
event Initialized(uint256 version);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if incorrectly initialized with address(0)
error ADDRESS_ZERO();
/// @dev Reverts if disabling initializers during initialization
error INITIALIZING();
/// @dev Reverts if calling an initialization function outside of initialization
error NOT_INITIALIZING();
/// @dev Reverts if reinitializing incorrectly
error ALREADY_INITIALIZED();
}
Compiler Settings
{"remappings":[":@openzeppelin/=node_modules/@openzeppelin/",":ds-test/=node_modules/ds-test/src/",":forge-std/=node_modules/forge-std/src/",":micro-onchain-metadata-utils/=node_modules/micro-onchain-metadata-utils/src/",":sol-uriencode/=node_modules/sol-uriencode/",":sol2string/=node_modules/sol2string/"],"optimizer":{"runs":500000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"src/token/Token.sol":"Token"}}
Contract ABI
[{"type":"constructor","stateMutability":"payable","inputs":[{"type":"address","name":"_manager","internalType":"address"}]},{"type":"error","name":"ADDRESS_ZERO","inputs":[]},{"type":"error","name":"ALREADY_INITIALIZED","inputs":[]},{"type":"error","name":"ALREADY_MINTED","inputs":[]},{"type":"error","name":"DELEGATE_CALL_FAILED","inputs":[]},{"type":"error","name":"EXPIRED_SIGNATURE","inputs":[]},{"type":"error","name":"INITIALIZING","inputs":[]},{"type":"error","name":"INVALID_APPROVAL","inputs":[]},{"type":"error","name":"INVALID_FOUNDER_OWNERSHIP","inputs":[]},{"type":"error","name":"INVALID_OWNER","inputs":[]},{"type":"error","name":"INVALID_RECIPIENT","inputs":[]},{"type":"error","name":"INVALID_SIGNATURE","inputs":[]},{"type":"error","name":"INVALID_TARGET","inputs":[]},{"type":"error","name":"INVALID_TIMESTAMP","inputs":[]},{"type":"error","name":"INVALID_UPGRADE","inputs":[{"type":"address","name":"impl","internalType":"address"}]},{"type":"error","name":"NOT_INITIALIZING","inputs":[]},{"type":"error","name":"NOT_MINTED","inputs":[]},{"type":"error","name":"NO_METADATA_GENERATED","inputs":[]},{"type":"error","name":"ONLY_AUCTION","inputs":[]},{"type":"error","name":"ONLY_CALL","inputs":[]},{"type":"error","name":"ONLY_DELEGATECALL","inputs":[]},{"type":"error","name":"ONLY_MANAGER","inputs":[]},{"type":"error","name":"ONLY_OWNER","inputs":[]},{"type":"error","name":"ONLY_PENDING_OWNER","inputs":[]},{"type":"error","name":"ONLY_PROXY","inputs":[]},{"type":"error","name":"ONLY_UUPS","inputs":[]},{"type":"error","name":"REENTRANCY","inputs":[]},{"type":"error","name":"UNSUPPORTED_UUID","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"approved","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"ApprovalForAll","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"operator","internalType":"address","indexed":true},{"type":"bool","name":"approved","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"DelegateChanged","inputs":[{"type":"address","name":"delegator","internalType":"address","indexed":true},{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"DelegateVotesChanged","inputs":[{"type":"address","name":"delegate","internalType":"address","indexed":true},{"type":"uint256","name":"prevTotalVotes","internalType":"uint256","indexed":false},{"type":"uint256","name":"newTotalVotes","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FounderAllocationsCleared","inputs":[{"type":"tuple[]","name":"newFounders","internalType":"struct IManager.FounderParams[]","indexed":false,"components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint256","name":"ownershipPct","internalType":"uint256"},{"type":"uint256","name":"vestExpiry","internalType":"uint256"}]}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"type":"uint256","name":"version","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"MintScheduled","inputs":[{"type":"uint256","name":"baseTokenId","internalType":"uint256","indexed":false},{"type":"uint256","name":"founderId","internalType":"uint256","indexed":false},{"type":"tuple","name":"founder","internalType":"struct TokenTypesV1.Founder","indexed":false,"components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint8","name":"ownershipPct","internalType":"uint8"},{"type":"uint32","name":"vestExpiry","internalType":"uint32"}]}],"anonymous":false},{"type":"event","name":"MintUnscheduled","inputs":[{"type":"uint256","name":"baseTokenId","internalType":"uint256","indexed":false},{"type":"uint256","name":"founderId","internalType":"uint256","indexed":false},{"type":"tuple","name":"founder","internalType":"struct TokenTypesV1.Founder","indexed":false,"components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint8","name":"ownershipPct","internalType":"uint8"},{"type":"uint32","name":"vestExpiry","internalType":"uint32"}]}],"anonymous":false},{"type":"event","name":"OwnerCanceled","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"canceledOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnerPending","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"pendingOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnerUpdated","inputs":[{"type":"address","name":"prevOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"Upgraded","inputs":[{"type":"address","name":"impl","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"acceptOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"approve","inputs":[{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"auction","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"_owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burn","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelOwnershipTransfer","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"contractURI","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"contractVersion","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegate","inputs":[{"type":"address","name":"_to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegateBySig","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_deadline","internalType":"uint256"},{"type":"uint8","name":"_v","internalType":"uint8"},{"type":"bytes32","name":"_r","internalType":"bytes32"},{"type":"bytes32","name":"_s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"delegates","inputs":[{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getApproved","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct TokenTypesV1.Founder","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint8","name":"ownershipPct","internalType":"uint8"},{"type":"uint32","name":"vestExpiry","internalType":"uint32"}]}],"name":"getFounder","inputs":[{"type":"uint256","name":"_founderId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct TokenTypesV1.Founder[]","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint8","name":"ownershipPct","internalType":"uint8"},{"type":"uint32","name":"vestExpiry","internalType":"uint32"}]}],"name":"getFounders","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getPastVotes","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"uint256","name":"_timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct TokenTypesV1.Founder","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint8","name":"ownershipPct","internalType":"uint8"},{"type":"uint32","name":"vestExpiry","internalType":"uint32"}]}],"name":"getScheduledRecipient","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getVotes","inputs":[{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"tuple[]","name":"_founders","internalType":"struct IManager.FounderParams[]","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint256","name":"ownershipPct","internalType":"uint256"},{"type":"uint256","name":"vestExpiry","internalType":"uint256"}]},{"type":"bytes","name":"_initStrings","internalType":"bytes"},{"type":"address","name":"_metadataRenderer","internalType":"address"},{"type":"address","name":"_auction","internalType":"address"},{"type":"address","name":"_initialOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isApprovedForAll","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"address","name":"_operator","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"metadataRenderer","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}],"name":"mint","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonce","inputs":[{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onFirstAuctionStarted","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"ownerOf","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pendingOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"proxiableUUID","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"safeTransferFrom","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"safeTransferFrom","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"safeTransferOwnership","inputs":[{"type":"address","name":"_newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setApprovalForAll","inputs":[{"type":"address","name":"_operator","internalType":"address"},{"type":"bool","name":"_approved","internalType":"bool"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"_interfaceId","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"tokenURI","inputs":[{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalFounderOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalFounders","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferFrom","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"_newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateFounders","inputs":[{"type":"tuple[]","name":"newFounders","internalType":"struct IManager.FounderParams[]","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint256","name":"ownershipPct","internalType":"uint256"},{"type":"uint256","name":"vestExpiry","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgradeTo","inputs":[{"type":"address","name":"_newImpl","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"upgradeToAndCall","inputs":[{"type":"address","name":"_newImpl","internalType":"address"},{"type":"bytes","name":"_data","internalType":"bytes"}]}]
Contract Creation Code
0x60c060408190523060805262004e2f3881900390819083398101604081905262000029916200011c565b600054610100900460ff161580158062000047575060005460ff1615155b801562000077575062000065306200011660201b62002b3c1760201c565b8062000077575060005460ff16600114155b15620000965760405163439a74c960e01b815260040160405180910390fd5b6000805460ff191660011790558015620000ba576000805461ff0019166101001790555b6001600160a01b03821660a05280156200010e576000805461ff0019169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f329060200160405180910390a15b50506200014e565b3b151590565b6000602082840312156200012f57600080fd5b81516001600160a01b03811681146200014757600080fd5b9392505050565b60805160a051614c986200019760003960008181611a6f0152612e0201526000818161116a015281816111c401528181611777015281816117d101526118c40152614c986000f3fe6080604052600436106102e75760003560e01c806370a0823111610184578063b20d7fa9116100d6578063d9d3e0901161008a578063e8a3d48511610064578063e8a3d48514610988578063e985e9c51461099d578063f2fde38b146109f357600080fd5b8063d9d3e09014610902578063dc276e571461093d578063e30c39781461095d57600080fd5b8063b88d4fde116100bb578063b88d4fde146108a2578063c87b56dd146108c2578063d1bfda66146108e257600080fd5b8063b20d7fa914610860578063b73cdd191461088057600080fd5b80638da5cb5b116101385780639d8aefc2116101125780639d8aefc2146107e5578063a0a8e460146107fa578063a22cb4651461084057600080fd5b80638da5cb5b1461077f57806395d89b41146107b05780639ab24eb0146107c557600080fd5b806374fd46551161016957806374fd46551461070457806379ba50971461073f5780637d9f6db51461075457600080fd5b806370a08231146106a157806370ae92d2146106c157600080fd5b80633a46b1a81161023d57806352d1902d116101f15780636097bf62116101cb5780636097bf62146106365780636352211e14610656578063703199701461067657600080fd5b806352d1902d146105e1578063587cde1e146105f65780635c19a95c1461061657600080fd5b806342842e0e1161022257806342842e0e1461058e57806342966c68146105ae5780634f1ef286146105ce57600080fd5b80633a46b1a8146104b45780633bcb43f3146104d457600080fd5b806318160ddd1161029f5780633644e515116102795780633644e5151461045f5780633659cfe614610474578063395db2cd1461049457600080fd5b806318160ddd146103f057806323452b9c1461042a57806323b872dd1461043f57600080fd5b8063081812fc116102d0578063081812fc14610343578063095ea7b3146103ab5780631249c58b146103cd57600080fd5b806301ffc9a7146102ec57806306fdde0314610321575b600080fd5b3480156102f857600080fd5b5061030c610307366004614076565b610a13565b60405190151581526020015b60405180910390f35b34801561032d57600080fd5b50610336610af8565b60405161031891906140b7565b34801561034f57600080fd5b5061038661035e366004614108565b6000908152600c602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610318565b3480156103b757600080fd5b506103cb6103c6366004614143565b610b86565b005b3480156103d957600080fd5b506103e2610ca4565b604051908152602001610318565b3480156103fc57600080fd5b506011547401000000000000000000000000000000000000000090046affffffffffffffffffffff166103e2565b34801561043657600080fd5b506103cb610db6565b34801561044b57600080fd5b506103cb61045a36600461416f565b610e88565b34801561046b57600080fd5b506103e26110d0565b34801561048057600080fd5b506103cb61048f3660046141b0565b611153565b3480156104a057600080fd5b506103cb6104af3660046141b0565b6112ab565b3480156104c057600080fd5b506103e26104cf366004614143565b61137f565b3480156104e057600080fd5b506105816104ef366004614108565b6040805160608082018352600080835260208084018290529284018190529384526013825292829020825193840183525473ffffffffffffffffffffffffffffffffffffffff8116845274010000000000000000000000000000000000000000810460ff16918401919091527501000000000000000000000000000000000000000000900463ffffffff169082015290565b60405161031891906141cd565b34801561059a57600080fd5b506103cb6105a936600461416f565b6115e3565b3480156105ba57600080fd5b506103cb6105c9366004614108565b611706565b6103cb6105dc36600461430f565b611760565b3480156105ed57600080fd5b506103e26118aa565b34801561060257600080fd5b506103866106113660046141b0565b611940565b34801561062257600080fd5b506103cb6106313660046141b0565b61197f565b34801561064257600080fd5b506103cb610651366004614401565b611989565b34801561066257600080fd5b50610386610671366004614108565b611bc6565b34801561068257600080fd5b5060125473ffffffffffffffffffffffffffffffffffffffff16610386565b3480156106ad57600080fd5b506103e26106bc3660046141b0565b611c22565b3480156106cd57600080fd5b506103e26106dc3660046141b0565b73ffffffffffffffffffffffffffffffffffffffff1660009081526007602052604090205490565b34801561071057600080fd5b506011547f0100000000000000000000000000000000000000000000000000000000000000900460ff166103e2565b34801561074b57600080fd5b506103cb611c9a565b34801561076057600080fd5b5060115473ffffffffffffffffffffffffffffffffffffffff16610386565b34801561078b57600080fd5b5060005462010000900473ffffffffffffffffffffffffffffffffffffffff16610386565b3480156107bc57600080fd5b50610336611da4565b3480156107d157600080fd5b506103e26107e03660046141b0565b611db1565b3480156107f157600080fd5b506103cb611e80565b34801561080657600080fd5b5060408051808201909152600581527f312e312e300000000000000000000000000000000000000000000000000000006020820152610336565b34801561084c57600080fd5b506103cb61085b3660046144b8565b611f6f565b34801561086c57600080fd5b506103cb61087b3660046144f1565b612006565b34801561088c57600080fd5b5061089561225b565b604051610318919061455c565b3480156108ae57600080fd5b506103cb6108bd3660046145dd565b6123c5565b3480156108ce57600080fd5b506103366108dd366004614108565b6124dd565b3480156108ee57600080fd5b506103cb6108fd366004614650565b612593565b34801561090e57600080fd5b506012547f0100000000000000000000000000000000000000000000000000000000000000900460ff166103e2565b34801561094957600080fd5b50610581610958366004614108565b612978565b34801561096957600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16610386565b34801561099457600080fd5b50610336612a26565b3480156109a957600080fd5b5061030c6109b8366004614692565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600d6020908152604080832093909416825291909152205460ff1690565b3480156109ff57600080fd5b506103cb610a0e3660046141b0565b612adc565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610aa657507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610af257507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60088054610b05906146c0565b80601f0160208091040260200160405190810160405280929190818152602001828054610b31906146c0565b8015610b7e5780601f10610b5357610100808354040283529160200191610b7e565b820191906000526020600020905b815481529060010190602001808311610b6157829003601f168201915b505050505081565b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff16338114801590610bec575073ffffffffffffffffffffffffffffffffffffffff81166000908152600d6020908152604080832033845290915290205460ff16155b15610c23576040517f3201fe7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600c602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60006002805403610ce1576040517fad2ce74900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805560115473ffffffffffffffffffffffffffffffffffffffff16338114610d37576040517f3b30aafa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fff0000000000000000000000ffffffffffffffffffffffffffffffffffffffff8116600174010000000000000000000000000000000000000000928390046affffffffffffffffffffff90811691820116909202179091559150610d9f82612b42565b610d3757610dad8183612c32565b50600160025590565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314610e0d576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546000805460405173ffffffffffffffffffffffffffffffffffffffff9384169362010000909204909116917f682679deecef4dcd49674845cc1e3a075fea9073680aa445a8207d5a4bdea3da91a3600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff848116911614610ee8576040517f9d2d273100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216610f35576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff841614801590610f8c575073ffffffffffffffffffffffffffffffffffffffff83166000908152600d6020908152604080832033845290915290205460ff16155b8015610fbc57506000818152600c602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b15610ff3576040517f3201fe7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8084166000818152600b6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905593861680835284832080546001019055858352600a825284832080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168317909155600c90925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a46110cb838383612d61565b505050565b6000600654461461114c57611147600354600454604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b5060055490565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001630036111c2576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166112377f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611284576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61128d81612d7d565b6112a881604051806020016040528060008152506000612f62565b50565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314611302576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092556000805460405192936201000090910416917f4f2638f5949b9614ef8d5e268cb51348ad7f434a34812bf64b6e95014fbd357e9190a350565b60004282106113ba576040517f118818d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152600f6020526040812054908190036113f2576000915050610af2565b73ffffffffffffffffffffffffffffffffffffffff841660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85018085529281905292205467ffffffffffffffff168510611496576000908152602091909152604090205468010000000000000000900477ffffffffffffffffffffffffffffffffffffffffffffffff169150610af29050565b60008080526020839052604090205467ffffffffffffffff168510156114c25760009350505050610af2565b60408051808201909152600080825260208201819052829181905b828411156115a05760028385030484036000818152602088815260409182902082518084019093525467ffffffffffffffff81168084526801000000000000000090910477ffffffffffffffffffffffffffffffffffffffffffffffff1691830191909152919350915089900361157a576020015177ffffffffffffffffffffffffffffffffffffffffffffffff169650610af295505050505050565b805167ffffffffffffffff16891115611595578192506114dd565b6001820393506114dd565b505060009081526020939093525050604090205468010000000000000000900477ffffffffffffffffffffffffffffffffffffffffffffffff1691505092915050565b6115ee838383610e88565b813b151580156116cf57506040517f150b7a020000000000000000000000000000000000000000000000000000000080825233600483015273ffffffffffffffffffffffffffffffffffffffff858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af1158015611686573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116aa919061473c565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156110cb576040517f521005a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60115473ffffffffffffffffffffffffffffffffffffffff163314611757576040517f3b30aafa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112a8816130b5565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001630036117cf576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166118447f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611891576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61189a82612d7d565b6118a682826001612f62565b5050565b60003073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461191b576040517f575bc92e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600e602052604081205490911680156119765780611978565b825b9392505050565b6112a8338261313a565b600054610100900460ff16158015806119a6575060005460ff1615155b80156119c25750303b1515806119c2575060005460ff16600114155b156119f9576040517f439a74c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611a5757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611ac6576040517fa2ddd97100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ace6131f8565b611ad782613240565b611ae188886132f9565b600080611af087890189614779565b5050505091509150611b0282826136b8565b50506012805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790925560118054928616929091169190911790558015611bbc57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f329060200160405180910390a15b5050505050505050565b6000818152600a602052604081205473ffffffffffffffffffffffffffffffffffffffff1680610af2576040517f9d2d273100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff8216611c71576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff166000908152600b602052604090205490565b60015473ffffffffffffffffffffffffffffffffffffffff163314611ceb576040517f065cd53100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805460405133926201000090920473ffffffffffffffffffffffffffffffffffffffff16917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a360018054600080547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff831662010000021790557fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b60098054610b05906146c0565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600f6020526040812054808203611de5576000611e5f565b73ffffffffffffffffffffffffffffffffffffffff831660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501845290915290205468010000000000000000900477ffffffffffffffffffffffffffffffffffffffffffffffff165b77ffffffffffffffffffffffffffffffffffffffffffffffff169392505050565b60115473ffffffffffffffffffffffffffffffffffffffff163314611ed1576040517f3b30aafa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601154604080517f61d027b30000000000000000000000000000000000000000000000000000000081529051611f6d9273ffffffffffffffffffffffffffffffffffffffff16916361d027b391600480830192602092919082900301816000875af1158015611f44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f68919061486e565b613712565b565b336000818152600d6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b83421115612040576040517f6ed6bef000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061204a6110d0565b73ffffffffffffffffffffffffffffffffffffffff88811660008181526007602090815260409182902080546001810190915582517f9ba0adc65ac9b85f9640562bd298ef1e78f86fbfbc6433772a69f08b092c5b238184015280840194909452938b166060840152608083019390935260a08083018a90528151808403909101815260c0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000060e083015260e282019290925261010281019190915261012201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa1580156121a0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116158061221a57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b15612251576040517fa3402a3800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bbc888861313a565b6011546060907f0100000000000000000000000000000000000000000000000000000000000000900460ff1660008167ffffffffffffffff8111156122a2576122a261420d565b60405190808252806020026020018201604052801561230b57816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122c05790505b50905060005b828110156123be576000818152601360209081526040918290208251606081018452905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff1692820192909252750100000000000000000000000000000000000000000090910463ffffffff169181019190915282518390839081106123ab576123ab61488b565b6020908102919091010152600101612311565b5092915050565b6123d0858585610e88565b833b1515801561249f57506040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff86169063150b7a02906124379033908a908990899089906004016148ba565b6020604051808303816000875af1158015612456573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247a919061473c565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156124d6576040517f521005a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6012546040517fc87b56dd0000000000000000000000000000000000000000000000000000000081526004810183905260609173ffffffffffffffffffffffffffffffffffffffff169063c87b56dd90602401600060405180830381865afa15801561254d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610af29190810190614939565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1633146125ea576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011547f0100000000000000000000000000000000000000000000000000000000000000900460ff1660008167ffffffffffffffff81111561262e5761262e61420d565b60405190808252806020026020018201604052801561269757816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161264c5790505b50905060005b8281101561274a576000818152601360209081526040918290208251606081018452905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff1692820192909252750100000000000000000000000000000000000000000090910463ffffffff169181019190915282518390839081106127375761273761488b565b602090810291909101015260010161269d565b50604080516064808252610ca0820190925260009160208201610c808036833701905050905060005b82518110156129005760008382815181106127905761279061488b565b6020908102919091018101516000848152601383526040812080547fffffffffffffff000000000000000000000000000000000000000000000000001690559181015190925060ff1690036127e557506128f8565b6000816020015160ff166064816127fe576127fe61470d565b0460ff1690506000805b836020015160ff168110156128f3575b85828151811061282a5761282a61488b565b60209081029190910101511561284b57600190910190606482069150612818565b600082815260146020526040902080547fffffffffffffff0000000000000000000000000000000000000000000000000016905585516001908790849081106128965761289661488b565b6020026020010190151590811515815250507f6309c4a2f8a0b726702416b909c21dce80f888bf255698775cd12336acd16b958286866040516128db939291906149a7565b60405180910390a16064828401069150600101612808565b505050505b600101612773565b50601180547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081169091556012805490911690556040517f6fb4c002bf55855ac8dd687342fe82b08a0a20ea80a7588566bfc6703bae213f9061296690879087906149fd565b60405180910390a16124d685856132f9565b6040805160608101825260008082526020820181905291810191909152601460006129a4606485614a6d565b815260208082019290925260409081016000208151606081018352905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff1693820193909352750100000000000000000000000000000000000000000090920463ffffffff169082015292915050565b601254604080517fe8a3d485000000000000000000000000000000000000000000000000000000008152905160609273ffffffffffffffffffffffffffffffffffffffff169163e8a3d4859160048083019260009291908290030181865afa158015612a96573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526111479190810190614939565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314612b33576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112a881613712565b3b151590565b600080612b50606484614a6d565b60008181526014602052604090205490915073ffffffffffffffffffffffffffffffffffffffff16612b855750600092915050565b6000818152601460205260409020547501000000000000000000000000000000000000000000900463ffffffff16421015612bf257600081815260146020526040902054612be99073ffffffffffffffffffffffffffffffffffffffff1684612c32565b50600192915050565b600090815260146020526040812080547fffffffffffffff0000000000000000000000000000000000000000000000000016905592915050565b50919050565b612c3c82826137de565b601180547fff0000000000000000000000ffffffffffffffffffffffffffffffffffffffff811674010000000000000000000000000000000000000000918290046affffffffffffffffffffff908116600101169091021790556012546040517f25b4e7be0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff909116906325b4e7be906024016020604051808303816000875af1158015612d07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d2b9190614aa8565b6118a6576040517f110e7d7800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110cb612d6d84611940565b612d7684611940565b600161391f565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e00576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639bb8dcfd612e7a7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff91821660048201529084166024820152604401602060405180830381865afa158015612eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f0f9190614aa8565b6112a8576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615612f95576110cb83613b36565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561301a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261301791810190614ac5565b60015b613050576040517fc0bb20b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146130a9576040517f0849b49600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506110cb838383613bec565b6130be81613c17565b50601180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6affffffffffffffffffffff7401000000000000000000000000000000000000000080840482169290920116027fff0000000000000000000000ffffffffffffffffffffffffffffffffffffffff909116179055565b73ffffffffffffffffffffffffffffffffffffffff81166131585750805b600061316383611940565b73ffffffffffffffffffffffffffffffffffffffff8481166000818152600e602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a46110cb81836131f386611c22565b61391f565b600054610100900460ff16613239576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600255565b600054610100900460ff16613281576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffff0000000000000000000000000000000000000000ffff166201000073ffffffffffffffffffffffffffffffffffffffff84169081029190911782556040519091907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76908290a350565b600080805b8381101561364f57600085858381811061331a5761331a61488b565b905060600201602001359050806000036133345750613647565b928301926063841115613373576040517f91cc635e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60ff8316600081815260136020526040902060019094019387878581811061339d5761339d61488b565b6133b392602060609092020190810191506141b0565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff919091161781558787858181106134055761340561488b565b83547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff167501000000000000000000000000000000000000000000606092909202939093016040013563ffffffff16027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16919091177401000000000000000000000000000000000000000060ff861602178255506000836064816134ac576134ac61470d565b0490506000805b85811015613640576134c482613d39565b60008181526014602052604090819020865481547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff9092169182178355885460ff740100000000000000000000000000000000000000009182900416027fffffffffffffffffffffff00000000000000000000000000000000000000000090911690911717808255875463ffffffff75010000000000000000000000000000000000000000009182900416027fffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffff909116179055519092507fe9af43aba12c8c691c53cf87a0d60c9ff279731244b2186b92c6a90b535a2d2c906136289084908890889092835260208301919091525473ffffffffffffffffffffffffffffffffffffffff8116604083015260a081811c60ff16606084015260a89190911c63ffffffff1660808301520190565b60405180910390a160648284010691506001016134b3565b5050505050505b6001016132fe565b506012805460ff9384167f01000000000000000000000000000000000000000000000000000000000000009081027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92831617909255601180549390941690910291161790555050565b600054610100900460ff166136f9576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60086137058382614b2c565b5060096110cb8282614b2c565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808516936201000090930416917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a36000805473ffffffffffffffffffffffffffffffffffffffff80841662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff9092169190911790915560015416156112a857600180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905550565b73ffffffffffffffffffffffffffffffffffffffff821661382b576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff1615613887576040517fdfa4c0d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600b6020908152604080832080546001019055848352600a90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46118a660008383612d61565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561395b5750600081115b156110cb5773ffffffffffffffffffffffffffffffffffffffff831615613a495773ffffffffffffffffffffffffffffffffffffffff83166000908152600f60205260408120549080808315613a345750505073ffffffffffffffffffffffffffffffffffffffff841660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff850180855292529091205468010000000000000000810477ffffffffffffffffffffffffffffffffffffffffffffffff169067ffffffffffffffff165b613a4487858584868a8803613d7b565b505050505b73ffffffffffffffffffffffffffffffffffffffff8216156110cb5773ffffffffffffffffffffffffffffffffffffffff82166000908152600f60205260408120549080808315613b1d5750505073ffffffffffffffffffffffffffffffffffffffff831660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff850180855292529091205468010000000000000000810477ffffffffffffffffffffffffffffffffffffffffffffffff169067ffffffffffffffff165b613b2d86858584868a8801613d7b565b50505050505050565b803b613b86576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401612f59565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b613bf583613ee8565b600082511180613c025750805b156110cb57613c118383613f3d565b50505050565b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff1680613c73576040517f29074bf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000818152600b6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055858352600a825280832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116909155600c9092528083208054909216909155518492907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a46118a681600084612d61565b60005b60008281526014602052604090205473ffffffffffffffffffffffffffffffffffffffff1615613d7757600190910190606482069150613d3c565b5090565b600085118015613d8a57504283145b15613df95773ffffffffffffffffffffffffffffffffffffffff861660009081526010602090815260408083208784529091529020805467ffffffffffffffff166801000000000000000077ffffffffffffffffffffffffffffffffffffffffffffffff841602179055613e8f565b73ffffffffffffffffffffffffffffffffffffffff8616600081815260106020908152604080832089845282528083204267ffffffffffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff871668010000000000000000027fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016179055928252600f905220805460010190555b604080518381526020810183905273ffffffffffffffffffffffffffffffffffffffff8816917fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724910160405180910390a2505050505050565b613ef181613b36565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9060200160405180910390a150565b6060823b613f77576040517f37f2022900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051613f9f9190614c46565b600060405180830381855af49150503d8060008114613fda576040519150601f19603f3d011682016040523d82523d6000602084013e613fdf565b606091505b5091509150613fee8282613ff7565b95945050505050565b60608215614006575080610af2565b8151156140165781518083602001fd5b6040517f62536b1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffff00000000000000000000000000000000000000000000000000000000811681146112a857600080fd5b60006020828403121561408857600080fd5b813561197881614048565b60005b838110156140ae578181015183820152602001614096565b50506000910152565b60208152600082518060208401526140d6816040850160208701614093565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561411a57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146112a857600080fd5b6000806040838503121561415657600080fd5b823561416181614121565b946020939093013593505050565b60008060006060848603121561418457600080fd5b833561418f81614121565b9250602084013561419f81614121565b929592945050506040919091013590565b6000602082840312156141c257600080fd5b813561197881614121565b815173ffffffffffffffffffffffffffffffffffffffff16815260208083015160ff169082015260408083015163ffffffff169082015260608101610af2565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156142835761428361420d565b604052919050565b600067ffffffffffffffff8211156142a5576142a561420d565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006142e46142df8461428b565b61423c565b90508281528383830111156142f857600080fd5b828260208301376000602084830101529392505050565b6000806040838503121561432257600080fd5b823561432d81614121565b9150602083013567ffffffffffffffff81111561434957600080fd5b8301601f8101851361435a57600080fd5b614369858235602084016142d1565b9150509250929050565b60008083601f84011261438557600080fd5b50813567ffffffffffffffff81111561439d57600080fd5b6020830191508360206060830285010111156143b857600080fd5b9250929050565b60008083601f8401126143d157600080fd5b50813567ffffffffffffffff8111156143e957600080fd5b6020830191508360208285010111156143b857600080fd5b600080600080600080600060a0888a03121561441c57600080fd5b873567ffffffffffffffff8082111561443457600080fd5b6144408b838c01614373565b909950975060208a013591508082111561445957600080fd5b506144668a828b016143bf565b909650945050604088013561447a81614121565b9250606088013561448a81614121565b9150608088013561449a81614121565b8091505092959891949750929550565b80151581146112a857600080fd5b600080604083850312156144cb57600080fd5b82356144d681614121565b915060208301356144e6816144aa565b809150509250929050565b60008060008060008060c0878903121561450a57600080fd5b863561451581614121565b9550602087013561452581614121565b945060408701359350606087013560ff8116811461454257600080fd5b9598949750929560808101359460a0909101359350915050565b6020808252825182820181905260009190848201906040850190845b818110156145d1576145be838551805173ffffffffffffffffffffffffffffffffffffffff16825260208082015160ff169083015260409081015163ffffffff16910152565b9284019260609290920191600101614578565b50909695505050505050565b6000806000806000608086880312156145f557600080fd5b853561460081614121565b9450602086013561461081614121565b935060408601359250606086013567ffffffffffffffff81111561463357600080fd5b61463f888289016143bf565b969995985093965092949392505050565b6000806020838503121561466357600080fd5b823567ffffffffffffffff81111561467a57600080fd5b61468685828601614373565b90969095509350505050565b600080604083850312156146a557600080fd5b82356146b081614121565b915060208301356144e681614121565b600181811c908216806146d457607f821691505b602082108103612c2c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561474e57600080fd5b815161197881614048565b600082601f83011261476a57600080fd5b611978838335602085016142d1565b60008060008060008060c0878903121561479257600080fd5b863567ffffffffffffffff808211156147aa57600080fd5b6147b68a838b01614759565b975060208901359150808211156147cc57600080fd5b6147d88a838b01614759565b965060408901359150808211156147ee57600080fd5b6147fa8a838b01614759565b9550606089013591508082111561481057600080fd5b61481c8a838b01614759565b9450608089013591508082111561483257600080fd5b61483e8a838b01614759565b935060a089013591508082111561485457600080fd5b5061486189828a01614759565b9150509295509295509295565b60006020828403121561488057600080fd5b815161197881614121565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011683010190509695505050505050565b60006020828403121561494b57600080fd5b815167ffffffffffffffff81111561496257600080fd5b8201601f8101841361497357600080fd5b80516149816142df8261428b565b81815285602083850101111561499657600080fd5b613fee826020830160208601614093565b8381526020810183905260a081016149f56040830184805173ffffffffffffffffffffffffffffffffffffffff16825260208082015160ff169083015260409081015163ffffffff16910152565b949350505050565b6020808252818101839052600090604080840186845b87811015614a60578135614a2681614121565b73ffffffffffffffffffffffffffffffffffffffff1683528185013585840152838201358484015260609283019290910190600101614a13565b5090979650505050505050565b600082614aa3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b600060208284031215614aba57600080fd5b8151611978816144aa565b600060208284031215614ad757600080fd5b5051919050565b601f8211156110cb57600081815260208120601f850160051c81016020861015614b055750805b601f850160051c820191505b81811015614b2457828155600101614b11565b505050505050565b815167ffffffffffffffff811115614b4657614b4661420d565b614b5a81614b5484546146c0565b84614ade565b602080601f831160018114614bad5760008415614b775750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614b24565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614bfa57888601518255948401946001909101908401614bdb565b5085821015614c3657878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008251614c58818460208701614093565b919091019291505056fea26469706673582212200bfb0be60a592c669e79d483768719bea2aa6f04d756a989444646671fbb8a8564736f6c63430008100033000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da7174
Deployed ByteCode
0x6080604052600436106102e75760003560e01c806370a0823111610184578063b20d7fa9116100d6578063d9d3e0901161008a578063e8a3d48511610064578063e8a3d48514610988578063e985e9c51461099d578063f2fde38b146109f357600080fd5b8063d9d3e09014610902578063dc276e571461093d578063e30c39781461095d57600080fd5b8063b88d4fde116100bb578063b88d4fde146108a2578063c87b56dd146108c2578063d1bfda66146108e257600080fd5b8063b20d7fa914610860578063b73cdd191461088057600080fd5b80638da5cb5b116101385780639d8aefc2116101125780639d8aefc2146107e5578063a0a8e460146107fa578063a22cb4651461084057600080fd5b80638da5cb5b1461077f57806395d89b41146107b05780639ab24eb0146107c557600080fd5b806374fd46551161016957806374fd46551461070457806379ba50971461073f5780637d9f6db51461075457600080fd5b806370a08231146106a157806370ae92d2146106c157600080fd5b80633a46b1a81161023d57806352d1902d116101f15780636097bf62116101cb5780636097bf62146106365780636352211e14610656578063703199701461067657600080fd5b806352d1902d146105e1578063587cde1e146105f65780635c19a95c1461061657600080fd5b806342842e0e1161022257806342842e0e1461058e57806342966c68146105ae5780634f1ef286146105ce57600080fd5b80633a46b1a8146104b45780633bcb43f3146104d457600080fd5b806318160ddd1161029f5780633644e515116102795780633644e5151461045f5780633659cfe614610474578063395db2cd1461049457600080fd5b806318160ddd146103f057806323452b9c1461042a57806323b872dd1461043f57600080fd5b8063081812fc116102d0578063081812fc14610343578063095ea7b3146103ab5780631249c58b146103cd57600080fd5b806301ffc9a7146102ec57806306fdde0314610321575b600080fd5b3480156102f857600080fd5b5061030c610307366004614076565b610a13565b60405190151581526020015b60405180910390f35b34801561032d57600080fd5b50610336610af8565b60405161031891906140b7565b34801561034f57600080fd5b5061038661035e366004614108565b6000908152600c602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610318565b3480156103b757600080fd5b506103cb6103c6366004614143565b610b86565b005b3480156103d957600080fd5b506103e2610ca4565b604051908152602001610318565b3480156103fc57600080fd5b506011547401000000000000000000000000000000000000000090046affffffffffffffffffffff166103e2565b34801561043657600080fd5b506103cb610db6565b34801561044b57600080fd5b506103cb61045a36600461416f565b610e88565b34801561046b57600080fd5b506103e26110d0565b34801561048057600080fd5b506103cb61048f3660046141b0565b611153565b3480156104a057600080fd5b506103cb6104af3660046141b0565b6112ab565b3480156104c057600080fd5b506103e26104cf366004614143565b61137f565b3480156104e057600080fd5b506105816104ef366004614108565b6040805160608082018352600080835260208084018290529284018190529384526013825292829020825193840183525473ffffffffffffffffffffffffffffffffffffffff8116845274010000000000000000000000000000000000000000810460ff16918401919091527501000000000000000000000000000000000000000000900463ffffffff169082015290565b60405161031891906141cd565b34801561059a57600080fd5b506103cb6105a936600461416f565b6115e3565b3480156105ba57600080fd5b506103cb6105c9366004614108565b611706565b6103cb6105dc36600461430f565b611760565b3480156105ed57600080fd5b506103e26118aa565b34801561060257600080fd5b506103866106113660046141b0565b611940565b34801561062257600080fd5b506103cb6106313660046141b0565b61197f565b34801561064257600080fd5b506103cb610651366004614401565b611989565b34801561066257600080fd5b50610386610671366004614108565b611bc6565b34801561068257600080fd5b5060125473ffffffffffffffffffffffffffffffffffffffff16610386565b3480156106ad57600080fd5b506103e26106bc3660046141b0565b611c22565b3480156106cd57600080fd5b506103e26106dc3660046141b0565b73ffffffffffffffffffffffffffffffffffffffff1660009081526007602052604090205490565b34801561071057600080fd5b506011547f0100000000000000000000000000000000000000000000000000000000000000900460ff166103e2565b34801561074b57600080fd5b506103cb611c9a565b34801561076057600080fd5b5060115473ffffffffffffffffffffffffffffffffffffffff16610386565b34801561078b57600080fd5b5060005462010000900473ffffffffffffffffffffffffffffffffffffffff16610386565b3480156107bc57600080fd5b50610336611da4565b3480156107d157600080fd5b506103e26107e03660046141b0565b611db1565b3480156107f157600080fd5b506103cb611e80565b34801561080657600080fd5b5060408051808201909152600581527f312e312e300000000000000000000000000000000000000000000000000000006020820152610336565b34801561084c57600080fd5b506103cb61085b3660046144b8565b611f6f565b34801561086c57600080fd5b506103cb61087b3660046144f1565b612006565b34801561088c57600080fd5b5061089561225b565b604051610318919061455c565b3480156108ae57600080fd5b506103cb6108bd3660046145dd565b6123c5565b3480156108ce57600080fd5b506103366108dd366004614108565b6124dd565b3480156108ee57600080fd5b506103cb6108fd366004614650565b612593565b34801561090e57600080fd5b506012547f0100000000000000000000000000000000000000000000000000000000000000900460ff166103e2565b34801561094957600080fd5b50610581610958366004614108565b612978565b34801561096957600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16610386565b34801561099457600080fd5b50610336612a26565b3480156109a957600080fd5b5061030c6109b8366004614692565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600d6020908152604080832093909416825291909152205460ff1690565b3480156109ff57600080fd5b506103cb610a0e3660046141b0565b612adc565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610aa657507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610af257507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60088054610b05906146c0565b80601f0160208091040260200160405190810160405280929190818152602001828054610b31906146c0565b8015610b7e5780601f10610b5357610100808354040283529160200191610b7e565b820191906000526020600020905b815481529060010190602001808311610b6157829003601f168201915b505050505081565b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff16338114801590610bec575073ffffffffffffffffffffffffffffffffffffffff81166000908152600d6020908152604080832033845290915290205460ff16155b15610c23576040517f3201fe7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600c602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60006002805403610ce1576040517fad2ce74900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805560115473ffffffffffffffffffffffffffffffffffffffff16338114610d37576040517f3b30aafa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fff0000000000000000000000ffffffffffffffffffffffffffffffffffffffff8116600174010000000000000000000000000000000000000000928390046affffffffffffffffffffff90811691820116909202179091559150610d9f82612b42565b610d3757610dad8183612c32565b50600160025590565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314610e0d576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546000805460405173ffffffffffffffffffffffffffffffffffffffff9384169362010000909204909116917f682679deecef4dcd49674845cc1e3a075fea9073680aa445a8207d5a4bdea3da91a3600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff848116911614610ee8576040517f9d2d273100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216610f35576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff841614801590610f8c575073ffffffffffffffffffffffffffffffffffffffff83166000908152600d6020908152604080832033845290915290205460ff16155b8015610fbc57506000818152600c602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b15610ff3576040517f3201fe7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8084166000818152600b6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905593861680835284832080546001019055858352600a825284832080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168317909155600c90925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a46110cb838383612d61565b505050565b6000600654461461114c57611147600354600454604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b5060055490565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e6322201ced0a4d6595968411285a39ccf9d59891630036111c2576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000e6322201ced0a4d6595968411285a39ccf9d598973ffffffffffffffffffffffffffffffffffffffff166112377f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611284576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61128d81612d7d565b6112a881604051806020016040528060008152506000612f62565b50565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314611302576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092556000805460405192936201000090910416917f4f2638f5949b9614ef8d5e268cb51348ad7f434a34812bf64b6e95014fbd357e9190a350565b60004282106113ba576040517f118818d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152600f6020526040812054908190036113f2576000915050610af2565b73ffffffffffffffffffffffffffffffffffffffff841660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85018085529281905292205467ffffffffffffffff168510611496576000908152602091909152604090205468010000000000000000900477ffffffffffffffffffffffffffffffffffffffffffffffff169150610af29050565b60008080526020839052604090205467ffffffffffffffff168510156114c25760009350505050610af2565b60408051808201909152600080825260208201819052829181905b828411156115a05760028385030484036000818152602088815260409182902082518084019093525467ffffffffffffffff81168084526801000000000000000090910477ffffffffffffffffffffffffffffffffffffffffffffffff1691830191909152919350915089900361157a576020015177ffffffffffffffffffffffffffffffffffffffffffffffff169650610af295505050505050565b805167ffffffffffffffff16891115611595578192506114dd565b6001820393506114dd565b505060009081526020939093525050604090205468010000000000000000900477ffffffffffffffffffffffffffffffffffffffffffffffff1691505092915050565b6115ee838383610e88565b813b151580156116cf57506040517f150b7a020000000000000000000000000000000000000000000000000000000080825233600483015273ffffffffffffffffffffffffffffffffffffffff858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af1158015611686573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116aa919061473c565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156110cb576040517f521005a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60115473ffffffffffffffffffffffffffffffffffffffff163314611757576040517f3b30aafa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112a8816130b5565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e6322201ced0a4d6595968411285a39ccf9d59891630036117cf576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000e6322201ced0a4d6595968411285a39ccf9d598973ffffffffffffffffffffffffffffffffffffffff166118447f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611891576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61189a82612d7d565b6118a682826001612f62565b5050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e6322201ced0a4d6595968411285a39ccf9d5989161461191b576040517f575bc92e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600e602052604081205490911680156119765780611978565b825b9392505050565b6112a8338261313a565b600054610100900460ff16158015806119a6575060005460ff1615155b80156119c25750303b1515806119c2575060005460ff16600114155b156119f9576040517f439a74c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611a5757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da71741614611ac6576040517fa2ddd97100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ace6131f8565b611ad782613240565b611ae188886132f9565b600080611af087890189614779565b5050505091509150611b0282826136b8565b50506012805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790925560118054928616929091169190911790558015611bbc57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f329060200160405180910390a15b5050505050505050565b6000818152600a602052604081205473ffffffffffffffffffffffffffffffffffffffff1680610af2576040517f9d2d273100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff8216611c71576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff166000908152600b602052604090205490565b60015473ffffffffffffffffffffffffffffffffffffffff163314611ceb576040517f065cd53100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805460405133926201000090920473ffffffffffffffffffffffffffffffffffffffff16917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a360018054600080547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff831662010000021790557fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b60098054610b05906146c0565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600f6020526040812054808203611de5576000611e5f565b73ffffffffffffffffffffffffffffffffffffffff831660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501845290915290205468010000000000000000900477ffffffffffffffffffffffffffffffffffffffffffffffff165b77ffffffffffffffffffffffffffffffffffffffffffffffff169392505050565b60115473ffffffffffffffffffffffffffffffffffffffff163314611ed1576040517f3b30aafa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601154604080517f61d027b30000000000000000000000000000000000000000000000000000000081529051611f6d9273ffffffffffffffffffffffffffffffffffffffff16916361d027b391600480830192602092919082900301816000875af1158015611f44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f68919061486e565b613712565b565b336000818152600d6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b83421115612040576040517f6ed6bef000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061204a6110d0565b73ffffffffffffffffffffffffffffffffffffffff88811660008181526007602090815260409182902080546001810190915582517f9ba0adc65ac9b85f9640562bd298ef1e78f86fbfbc6433772a69f08b092c5b238184015280840194909452938b166060840152608083019390935260a08083018a90528151808403909101815260c0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000060e083015260e282019290925261010281019190915261012201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa1580156121a0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116158061221a57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b15612251576040517fa3402a3800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bbc888861313a565b6011546060907f0100000000000000000000000000000000000000000000000000000000000000900460ff1660008167ffffffffffffffff8111156122a2576122a261420d565b60405190808252806020026020018201604052801561230b57816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122c05790505b50905060005b828110156123be576000818152601360209081526040918290208251606081018452905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff1692820192909252750100000000000000000000000000000000000000000090910463ffffffff169181019190915282518390839081106123ab576123ab61488b565b6020908102919091010152600101612311565b5092915050565b6123d0858585610e88565b833b1515801561249f57506040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff86169063150b7a02906124379033908a908990899089906004016148ba565b6020604051808303816000875af1158015612456573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247a919061473c565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156124d6576040517f521005a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6012546040517fc87b56dd0000000000000000000000000000000000000000000000000000000081526004810183905260609173ffffffffffffffffffffffffffffffffffffffff169063c87b56dd90602401600060405180830381865afa15801561254d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610af29190810190614939565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1633146125ea576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011547f0100000000000000000000000000000000000000000000000000000000000000900460ff1660008167ffffffffffffffff81111561262e5761262e61420d565b60405190808252806020026020018201604052801561269757816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161264c5790505b50905060005b8281101561274a576000818152601360209081526040918290208251606081018452905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff1692820192909252750100000000000000000000000000000000000000000090910463ffffffff169181019190915282518390839081106127375761273761488b565b602090810291909101015260010161269d565b50604080516064808252610ca0820190925260009160208201610c808036833701905050905060005b82518110156129005760008382815181106127905761279061488b565b6020908102919091018101516000848152601383526040812080547fffffffffffffff000000000000000000000000000000000000000000000000001690559181015190925060ff1690036127e557506128f8565b6000816020015160ff166064816127fe576127fe61470d565b0460ff1690506000805b836020015160ff168110156128f3575b85828151811061282a5761282a61488b565b60209081029190910101511561284b57600190910190606482069150612818565b600082815260146020526040902080547fffffffffffffff0000000000000000000000000000000000000000000000000016905585516001908790849081106128965761289661488b565b6020026020010190151590811515815250507f6309c4a2f8a0b726702416b909c21dce80f888bf255698775cd12336acd16b958286866040516128db939291906149a7565b60405180910390a16064828401069150600101612808565b505050505b600101612773565b50601180547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081169091556012805490911690556040517f6fb4c002bf55855ac8dd687342fe82b08a0a20ea80a7588566bfc6703bae213f9061296690879087906149fd565b60405180910390a16124d685856132f9565b6040805160608101825260008082526020820181905291810191909152601460006129a4606485614a6d565b815260208082019290925260409081016000208151606081018352905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff1693820193909352750100000000000000000000000000000000000000000090920463ffffffff169082015292915050565b601254604080517fe8a3d485000000000000000000000000000000000000000000000000000000008152905160609273ffffffffffffffffffffffffffffffffffffffff169163e8a3d4859160048083019260009291908290030181865afa158015612a96573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526111479190810190614939565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314612b33576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112a881613712565b3b151590565b600080612b50606484614a6d565b60008181526014602052604090205490915073ffffffffffffffffffffffffffffffffffffffff16612b855750600092915050565b6000818152601460205260409020547501000000000000000000000000000000000000000000900463ffffffff16421015612bf257600081815260146020526040902054612be99073ffffffffffffffffffffffffffffffffffffffff1684612c32565b50600192915050565b600090815260146020526040812080547fffffffffffffff0000000000000000000000000000000000000000000000000016905592915050565b50919050565b612c3c82826137de565b601180547fff0000000000000000000000ffffffffffffffffffffffffffffffffffffffff811674010000000000000000000000000000000000000000918290046affffffffffffffffffffff908116600101169091021790556012546040517f25b4e7be0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff909116906325b4e7be906024016020604051808303816000875af1158015612d07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d2b9190614aa8565b6118a6576040517f110e7d7800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110cb612d6d84611940565b612d7684611940565b600161391f565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e00576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000d310a3041dfcf14def5ccbc508668974b5da717473ffffffffffffffffffffffffffffffffffffffff16639bb8dcfd612e7a7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff91821660048201529084166024820152604401602060405180830381865afa158015612eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f0f9190614aa8565b6112a8576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615612f95576110cb83613b36565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561301a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261301791810190614ac5565b60015b613050576040517fc0bb20b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146130a9576040517f0849b49600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506110cb838383613bec565b6130be81613c17565b50601180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6affffffffffffffffffffff7401000000000000000000000000000000000000000080840482169290920116027fff0000000000000000000000ffffffffffffffffffffffffffffffffffffffff909116179055565b73ffffffffffffffffffffffffffffffffffffffff81166131585750805b600061316383611940565b73ffffffffffffffffffffffffffffffffffffffff8481166000818152600e602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a46110cb81836131f386611c22565b61391f565b600054610100900460ff16613239576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600255565b600054610100900460ff16613281576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffff0000000000000000000000000000000000000000ffff166201000073ffffffffffffffffffffffffffffffffffffffff84169081029190911782556040519091907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76908290a350565b600080805b8381101561364f57600085858381811061331a5761331a61488b565b905060600201602001359050806000036133345750613647565b928301926063841115613373576040517f91cc635e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60ff8316600081815260136020526040902060019094019387878581811061339d5761339d61488b565b6133b392602060609092020190810191506141b0565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff919091161781558787858181106134055761340561488b565b83547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff167501000000000000000000000000000000000000000000606092909202939093016040013563ffffffff16027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16919091177401000000000000000000000000000000000000000060ff861602178255506000836064816134ac576134ac61470d565b0490506000805b85811015613640576134c482613d39565b60008181526014602052604090819020865481547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff9092169182178355885460ff740100000000000000000000000000000000000000009182900416027fffffffffffffffffffffff00000000000000000000000000000000000000000090911690911717808255875463ffffffff75010000000000000000000000000000000000000000009182900416027fffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffff909116179055519092507fe9af43aba12c8c691c53cf87a0d60c9ff279731244b2186b92c6a90b535a2d2c906136289084908890889092835260208301919091525473ffffffffffffffffffffffffffffffffffffffff8116604083015260a081811c60ff16606084015260a89190911c63ffffffff1660808301520190565b60405180910390a160648284010691506001016134b3565b5050505050505b6001016132fe565b506012805460ff9384167f01000000000000000000000000000000000000000000000000000000000000009081027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92831617909255601180549390941690910291161790555050565b600054610100900460ff166136f9576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60086137058382614b2c565b5060096110cb8282614b2c565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808516936201000090930416917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a36000805473ffffffffffffffffffffffffffffffffffffffff80841662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff9092169190911790915560015416156112a857600180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905550565b73ffffffffffffffffffffffffffffffffffffffff821661382b576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff1615613887576040517fdfa4c0d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600b6020908152604080832080546001019055848352600a90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46118a660008383612d61565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561395b5750600081115b156110cb5773ffffffffffffffffffffffffffffffffffffffff831615613a495773ffffffffffffffffffffffffffffffffffffffff83166000908152600f60205260408120549080808315613a345750505073ffffffffffffffffffffffffffffffffffffffff841660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff850180855292529091205468010000000000000000810477ffffffffffffffffffffffffffffffffffffffffffffffff169067ffffffffffffffff165b613a4487858584868a8803613d7b565b505050505b73ffffffffffffffffffffffffffffffffffffffff8216156110cb5773ffffffffffffffffffffffffffffffffffffffff82166000908152600f60205260408120549080808315613b1d5750505073ffffffffffffffffffffffffffffffffffffffff831660009081526010602090815260408083207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff850180855292529091205468010000000000000000810477ffffffffffffffffffffffffffffffffffffffffffffffff169067ffffffffffffffff165b613b2d86858584868a8801613d7b565b50505050505050565b803b613b86576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401612f59565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b613bf583613ee8565b600082511180613c025750805b156110cb57613c118383613f3d565b50505050565b6000818152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff1680613c73576040517f29074bf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000818152600b6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055858352600a825280832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116909155600c9092528083208054909216909155518492907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a46118a681600084612d61565b60005b60008281526014602052604090205473ffffffffffffffffffffffffffffffffffffffff1615613d7757600190910190606482069150613d3c565b5090565b600085118015613d8a57504283145b15613df95773ffffffffffffffffffffffffffffffffffffffff861660009081526010602090815260408083208784529091529020805467ffffffffffffffff166801000000000000000077ffffffffffffffffffffffffffffffffffffffffffffffff841602179055613e8f565b73ffffffffffffffffffffffffffffffffffffffff8616600081815260106020908152604080832089845282528083204267ffffffffffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff871668010000000000000000027fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016179055928252600f905220805460010190555b604080518381526020810183905273ffffffffffffffffffffffffffffffffffffffff8816917fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724910160405180910390a2505050505050565b613ef181613b36565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9060200160405180910390a150565b6060823b613f77576040517f37f2022900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051613f9f9190614c46565b600060405180830381855af49150503d8060008114613fda576040519150601f19603f3d011682016040523d82523d6000602084013e613fdf565b606091505b5091509150613fee8282613ff7565b95945050505050565b60608215614006575080610af2565b8151156140165781518083602001fd5b6040517f62536b1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffff00000000000000000000000000000000000000000000000000000000811681146112a857600080fd5b60006020828403121561408857600080fd5b813561197881614048565b60005b838110156140ae578181015183820152602001614096565b50506000910152565b60208152600082518060208401526140d6816040850160208701614093565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561411a57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146112a857600080fd5b6000806040838503121561415657600080fd5b823561416181614121565b946020939093013593505050565b60008060006060848603121561418457600080fd5b833561418f81614121565b9250602084013561419f81614121565b929592945050506040919091013590565b6000602082840312156141c257600080fd5b813561197881614121565b815173ffffffffffffffffffffffffffffffffffffffff16815260208083015160ff169082015260408083015163ffffffff169082015260608101610af2565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156142835761428361420d565b604052919050565b600067ffffffffffffffff8211156142a5576142a561420d565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006142e46142df8461428b565b61423c565b90508281528383830111156142f857600080fd5b828260208301376000602084830101529392505050565b6000806040838503121561432257600080fd5b823561432d81614121565b9150602083013567ffffffffffffffff81111561434957600080fd5b8301601f8101851361435a57600080fd5b614369858235602084016142d1565b9150509250929050565b60008083601f84011261438557600080fd5b50813567ffffffffffffffff81111561439d57600080fd5b6020830191508360206060830285010111156143b857600080fd5b9250929050565b60008083601f8401126143d157600080fd5b50813567ffffffffffffffff8111156143e957600080fd5b6020830191508360208285010111156143b857600080fd5b600080600080600080600060a0888a03121561441c57600080fd5b873567ffffffffffffffff8082111561443457600080fd5b6144408b838c01614373565b909950975060208a013591508082111561445957600080fd5b506144668a828b016143bf565b909650945050604088013561447a81614121565b9250606088013561448a81614121565b9150608088013561449a81614121565b8091505092959891949750929550565b80151581146112a857600080fd5b600080604083850312156144cb57600080fd5b82356144d681614121565b915060208301356144e6816144aa565b809150509250929050565b60008060008060008060c0878903121561450a57600080fd5b863561451581614121565b9550602087013561452581614121565b945060408701359350606087013560ff8116811461454257600080fd5b9598949750929560808101359460a0909101359350915050565b6020808252825182820181905260009190848201906040850190845b818110156145d1576145be838551805173ffffffffffffffffffffffffffffffffffffffff16825260208082015160ff169083015260409081015163ffffffff16910152565b9284019260609290920191600101614578565b50909695505050505050565b6000806000806000608086880312156145f557600080fd5b853561460081614121565b9450602086013561461081614121565b935060408601359250606086013567ffffffffffffffff81111561463357600080fd5b61463f888289016143bf565b969995985093965092949392505050565b6000806020838503121561466357600080fd5b823567ffffffffffffffff81111561467a57600080fd5b61468685828601614373565b90969095509350505050565b600080604083850312156146a557600080fd5b82356146b081614121565b915060208301356144e681614121565b600181811c908216806146d457607f821691505b602082108103612c2c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561474e57600080fd5b815161197881614048565b600082601f83011261476a57600080fd5b611978838335602085016142d1565b60008060008060008060c0878903121561479257600080fd5b863567ffffffffffffffff808211156147aa57600080fd5b6147b68a838b01614759565b975060208901359150808211156147cc57600080fd5b6147d88a838b01614759565b965060408901359150808211156147ee57600080fd5b6147fa8a838b01614759565b9550606089013591508082111561481057600080fd5b61481c8a838b01614759565b9450608089013591508082111561483257600080fd5b61483e8a838b01614759565b935060a089013591508082111561485457600080fd5b5061486189828a01614759565b9150509295509295509295565b60006020828403121561488057600080fd5b815161197881614121565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011683010190509695505050505050565b60006020828403121561494b57600080fd5b815167ffffffffffffffff81111561496257600080fd5b8201601f8101841361497357600080fd5b80516149816142df8261428b565b81815285602083850101111561499657600080fd5b613fee826020830160208601614093565b8381526020810183905260a081016149f56040830184805173ffffffffffffffffffffffffffffffffffffffff16825260208082015160ff169083015260409081015163ffffffff16910152565b949350505050565b6020808252818101839052600090604080840186845b87811015614a60578135614a2681614121565b73ffffffffffffffffffffffffffffffffffffffff1683528185013585840152838201358484015260609283019290910190600101614a13565b5090979650505050505050565b600082614aa3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b600060208284031215614aba57600080fd5b8151611978816144aa565b600060208284031215614ad757600080fd5b5051919050565b601f8211156110cb57600081815260208120601f850160051c81016020861015614b055750805b601f850160051c820191505b81811015614b2457828155600101614b11565b505050505050565b815167ffffffffffffffff811115614b4657614b4661420d565b614b5a81614b5484546146c0565b84614ade565b602080601f831160018114614bad5760008415614b775750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614b24565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614bfa57888601518255948401946001909101908401614bdb565b5085821015614c3657878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008251614c58818460208701614093565b919091019291505056fea26469706673582212200bfb0be60a592c669e79d483768719bea2aa6f04d756a989444646671fbb8a8564736f6c63430008100033