Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 199 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Claim | 20470448 | 2 days ago | IN | 0 ETH | 0.00120058 | ||||
Claim | 20463814 | 3 days ago | IN | 0 ETH | 0.0016603 | ||||
Claim | 20446322 | 6 days ago | IN | 0 ETH | 0.00011885 | ||||
Claim | 20440131 | 7 days ago | IN | 0 ETH | 0.00019079 | ||||
Claim | 20437681 | 7 days ago | IN | 0 ETH | 0.00031738 | ||||
Claim | 20430865 | 8 days ago | IN | 0 ETH | 0.00019398 | ||||
Claim | 20419859 | 9 days ago | IN | 0 ETH | 0.00133712 | ||||
Claim | 20413294 | 10 days ago | IN | 0 ETH | 0.00053452 | ||||
Claim | 20413232 | 10 days ago | IN | 0 ETH | 0.00062967 | ||||
Claim | 20412999 | 10 days ago | IN | 0 ETH | 0.00180085 | ||||
Claim | 20412357 | 10 days ago | IN | 0 ETH | 0.00017011 | ||||
Claim | 20411578 | 11 days ago | IN | 0 ETH | 0.00025427 | ||||
Claim | 20407279 | 11 days ago | IN | 0 ETH | 0.00016497 | ||||
Claim | 20405298 | 11 days ago | IN | 0 ETH | 0.00012377 | ||||
Claim | 20403328 | 12 days ago | IN | 0 ETH | 0.00011284 | ||||
Claim | 20403311 | 12 days ago | IN | 0 ETH | 0.00012159 | ||||
Claim | 20400988 | 12 days ago | IN | 0 ETH | 0.00027394 | ||||
Claim | 20400988 | 12 days ago | IN | 0 ETH | 0.00027674 | ||||
Claim | 20400872 | 12 days ago | IN | 0 ETH | 0.00028086 | ||||
Claim | 20395867 | 13 days ago | IN | 0 ETH | 0.00015851 | ||||
Claim | 20393588 | 13 days ago | IN | 0 ETH | 0.00020338 | ||||
Claim | 20391283 | 13 days ago | IN | 0 ETH | 0.00076415 | ||||
Claim | 20388659 | 14 days ago | IN | 0 ETH | 0.00023835 | ||||
Claim | 20387897 | 14 days ago | IN | 0 ETH | 0.00046799 | ||||
Claim | 20363061 | 17 days ago | IN | 0 ETH | 0.00078412 |
Latest 21 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
20375932 | 16 days ago | Contract Creation | 0 ETH | |||
20375932 | 16 days ago | Contract Creation | 0 ETH | |||
20211706 | 38 days ago | Contract Creation | 0 ETH | |||
20211703 | 38 days ago | Contract Creation | 0 ETH | |||
19875250 | 86 days ago | Contract Creation | 0 ETH | |||
19875250 | 86 days ago | Contract Creation | 0 ETH | |||
19691138 | 111 days ago | Contract Creation | 0 ETH | |||
19691134 | 111 days ago | Contract Creation | 0 ETH | |||
19691118 | 111 days ago | Contract Creation | 0 ETH | |||
19475639 | 142 days ago | Contract Creation | 0 ETH | |||
19475639 | 142 days ago | Contract Creation | 0 ETH | |||
19475623 | 142 days ago | Contract Creation | 0 ETH | |||
19432645 | 148 days ago | Contract Creation | 0 ETH | |||
19289841 | 168 days ago | Contract Creation | 0 ETH | |||
19276366 | 169 days ago | Contract Creation | 0 ETH | |||
19271994 | 170 days ago | Contract Creation | 0 ETH | |||
19271994 | 170 days ago | Contract Creation | 0 ETH | |||
19211534 | 179 days ago | Contract Creation | 0 ETH | |||
19183412 | 182 days ago | Contract Creation | 0 ETH | |||
19094932 | 195 days ago | Contract Creation | 0 ETH | |||
18989257 | 210 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
MerkleDistributorPeriphery
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {IMerkleDistributorWithDeadline} from "src/uniswap/interfaces/IMerkleDistributorWithDeadline.sol"; import {MerkleDistributorWithDeadline} from "src/uniswap/MerkleDistributorWithDeadline.sol"; import {IMerkleDistributorPeriphery} from "src/interfaces/IMerkleDistributorPeriphery.sol"; import {IOwnable2Step} from "src/interfaces/IOwnable2Step.sol"; import {Amount} from "src/types/Amount.sol"; import {Id} from "src/types/Id.sol"; import {Index} from "src/types/Index.sol"; import {MerkleProof} from "src/types/MerkleProof.sol"; import {MerkleRoot} from "src/types/MerkleRoot.sol"; import {Number} from "src/types/Number.sol"; import {Timestamp} from "src/types/Timestamp.sol"; /// @title A periphery contract implementation to manage multiple Merkle Distributor contracts. /// @author Timelord contract MerkleDistributorPeriphery is IMerkleDistributorPeriphery, Ownable2Step { using SafeERC20 for IERC20; /// @dev Mapping storage from id to the distributor contract addresses. IMerkleDistributorWithDeadline[] private storedDistributors; mapping(Id => address) private ownerships; mapping(IMerkleDistributorWithDeadline distributor => address) private ownershipGivenDistributors; address private queuedDeployer; IERC20 private queuedToken; Amount private queuedTotalAmount; MerkleRoot private queuedMerkleRoot; Timestamp private queuedEndTime; /// @notice Sets the contract deployer as the owner. constructor(address chosenOwner) Ownable(chosenOwner) {} /// @inheritdoc IMerkleDistributorPeriphery function merkleDistributor( Id id ) external view override returns (IMerkleDistributorWithDeadline) { return storedDistributors[Id.unwrap(id)]; } /// @inheritdoc IMerkleDistributorPeriphery function totalMerkleDistributors() external view override returns (Number) { return Number.wrap(storedDistributors.length); } function ownerGivenId(Id id) external view override returns (address) { if (Id.unwrap(id) >= storedDistributors.length) return address(0); address ownership = ownerships[id]; return ownership == address(0) ? owner() : ownership; } /// @inheritdoc IMerkleDistributorPeriphery function areClaimed( Query[] calldata queries ) external view override returns (bool[] memory results) { uint256 length = queries.length; results = new bool[](length); for (uint256 i; i < length; ) { Query memory query = queries[i]; results[i] = query.distributor.isClaimed(Index.unwrap(query.index)); unchecked { i++; } } } function queued() external view override returns ( address deployer, IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) { deployer = queuedDeployer; token = queuedToken; totalAmount = queuedTotalAmount; merkleRoot = queuedMerkleRoot; endTime = queuedEndTime; } function queue( address deployer, IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) external override onlyOwner { if (deployer == address(0)) revert CannotQueueWithDeployerAsZeroAddress(); if (address(token) == address(0)) revert CannotQueueWithTokenAsZeroAddress(); if (totalAmount.isZero()) revert CannotQueueWithTotalAmountAsZero(); if (endTime <= Timestamp.wrap(block.timestamp)) revert CannotQueueWithEndTimeInThePast(); queuedDeployer = deployer; queuedToken = token; queuedTotalAmount = totalAmount; queuedMerkleRoot = merkleRoot; queuedEndTime = endTime; emit Queue(deployer, token, totalAmount, merkleRoot, endTime); } function unqueue() external override onlyOwner { queuedDeployer = address(0); queuedToken = IERC20(address(0)); queuedTotalAmount = Amount.wrap(0); queuedMerkleRoot = MerkleRoot.wrap(0); queuedEndTime = Timestamp.wrap(0); emit Unqueue(); } function createFromQueue( IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) external override returns (Id id, IMerkleDistributorWithDeadline distributor) { if (msg.sender != queuedDeployer) revert OnlyAuthorizedOwner(msg.sender); if (address(token) != address(queuedToken)) revert InconsistentQueuedToken(); if (Amount.unwrap(totalAmount) != Amount.unwrap(queuedTotalAmount)) revert InconsistentQueuedTotalAmount(); if (MerkleRoot.unwrap(merkleRoot) != MerkleRoot.unwrap(merkleRoot)) revert InconsistentQueuedMerkleRoot(); if (Timestamp.unwrap(endTime) != Timestamp.unwrap(queuedEndTime)) revert InconsistentQueuedEndTime(); if (queuedEndTime <= Timestamp.wrap(block.timestamp)) revert CannotCreateWithEndTimeInThePast(); id = Id.wrap(storedDistributors.length); distributor = IMerkleDistributorWithDeadline( Create2.deploy( 0, bytes32(Id.unwrap(id)), abi.encodePacked( type(MerkleDistributorWithDeadline).creationCode, abi.encode( address(token), MerkleRoot.unwrap(merkleRoot), Timestamp.unwrap(endTime) ) ) ) ); storedDistributors.push(distributor); queuedDeployer = address(0); queuedToken = IERC20(address(0)); queuedTotalAmount = Amount.wrap(0); queuedMerkleRoot = MerkleRoot.wrap(0); queuedEndTime = Timestamp.wrap(0); ownerships[id] = msg.sender; ownershipGivenDistributors[distributor] = msg.sender; token.safeTransferFrom( msg.sender, address(distributor), Amount.unwrap(totalAmount) ); emit CreateFromQueue( id, distributor, token, totalAmount, merkleRoot, endTime ); } /// @inheritdoc IMerkleDistributorPeriphery function create( IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) external override onlyOwner returns (Id id, IMerkleDistributorWithDeadline distributor) { if (address(token) == address(0)) revert CannotCreateWithTokenAsZeroAddress(); if (totalAmount.isZero()) revert CannotCreateWithTotalAmountAsZero(); if (endTime <= Timestamp.wrap(block.timestamp)) revert CannotCreateWithEndTimeInThePast(); id = Id.wrap(storedDistributors.length); distributor = IMerkleDistributorWithDeadline( Create2.deploy( 0, bytes32(Id.unwrap(id)), abi.encodePacked( type(MerkleDistributorWithDeadline).creationCode, abi.encode( address(token), MerkleRoot.unwrap(merkleRoot), Timestamp.unwrap(endTime) ) ) ) ); storedDistributors.push(distributor); token.safeTransferFrom( msg.sender, address(distributor), Amount.unwrap(totalAmount) ); emit Create(id, distributor, token, totalAmount, merkleRoot, endTime); } /// @inheritdoc IMerkleDistributorPeriphery function claim(Order[] calldata orders) external override { uint256 length = orders.length; for (uint256 i; i < length; ) { Order memory order = orders[i]; MerkleProof[] memory merkleProof = order.merkleProof; uint256 lengthOfMerkleProof = merkleProof.length; bytes32[] memory merkleProofInBytes32 = new bytes32[]( lengthOfMerkleProof ); for (uint256 j; j < lengthOfMerkleProof; ) { merkleProofInBytes32[j] = MerkleProof.unwrap(merkleProof[j]); unchecked { j++; } } order.distributor.claim( Index.unwrap(order.index), msg.sender, Amount.unwrap(order.amount), merkleProofInBytes32 ); unchecked { i++; } } } /// @inheritdoc IMerkleDistributorPeriphery function withdraw( IMerkleDistributorWithDeadline[] calldata distributors ) external override returns (Amount[] memory amounts) { uint256 length = distributors.length; amounts = new Amount[](length); for (uint256 i; i < length; ) { IMerkleDistributorWithDeadline distributor = distributors[i]; address deployer = ownershipGivenDistributors[distributor]; if (deployer == address(0)) deployer = owner(); if (deployer != msg.sender) revert OnlyAuthorizedOwner(msg.sender); IERC20 token = IERC20(distributor.token()); Amount amount = Amount.wrap(token.balanceOf(address(distributor))); if (!amount.isZero()) { amounts[i] = amount; distributor.withdraw(); token.safeTransfer(msg.sender, Amount.unwrap(amount)); emit Withdraw(distributor, amount); } unchecked { i++; } } } /// @inheritdoc IOwnable2Step function owner() public view override(Ownable, IOwnable2Step) returns (address) { return super.owner(); } /// @inheritdoc IOwnable2Step function renounceOwnership() public override(Ownable, IOwnable2Step) { super.renounceOwnership(); } /// @inheritdoc IOwnable2Step function pendingOwner() public view override(Ownable2Step, IOwnable2Step) returns (address) { return super.pendingOwner(); } /// @inheritdoc IOwnable2Step function transferOwnership( address newOwner ) public override(Ownable2Step, IOwnable2Step) { super.transferOwnership(newOwner); } /// @inheritdoc IOwnable2Step function acceptOwnership() public override(Ownable2Step, IOwnable2Step) { super.acceptOwnership(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol) pragma solidity ^0.8.20; import {Ownable} from "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is specified at deployment time in the constructor for `Ownable`. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); if (pendingOwner() != sender) { revert OwnableUnauthorizedAccount(sender); } _transferOwnership(sender); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol) pragma solidity ^0.8.20; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev Not enough balance for performing a CREATE2 deploy. */ error Create2InsufficientBalance(uint256 balance, uint256 needed); /** * @dev There's no code to deploy. */ error Create2EmptyBytecode(); /** * @dev The deployment failed. */ error Create2FailedDeployment(); /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { if (address(this).balance < amount) { revert Create2InsufficientBalance(address(this).balance, amount); } if (bytecode.length == 0) { revert Create2EmptyBytecode(); } /// @solidity memory-safe-assembly assembly { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) } if (addr == address(0)) { revert Create2FailedDeployment(); } } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) // Get free memory pointer // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | // |-------------------|---------------------------------------------------------------------------| // | bytecodeHash | CCCCCCCCCCCCC...CC | // | salt | BBBBBBBBBBBBB...BB | // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | // | 0xFF | FF | // |-------------------|---------------------------------------------------------------------------| // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | mstore(add(ptr, 0x40), bytecodeHash) mstore(add(ptr, 0x20), salt) mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff mstore8(start, 0xff) addr := keccak256(start, 85) } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity =0.8.20; import {IMerkleDistributor} from "./IMerkleDistributor.sol"; interface IMerkleDistributorWithDeadline is IMerkleDistributor { function owner() external view returns (address); function endTime() external returns (uint256); function withdraw() external; }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity =0.8.20; import {MerkleDistributor} from "./MerkleDistributor.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; error EndTimeInPast(); error ClaimWindowFinished(); error NoWithdrawDuringClaim(); contract MerkleDistributorWithDeadline is MerkleDistributor, Ownable { using SafeERC20 for IERC20; uint256 public immutable endTime; constructor( address token_, bytes32 merkleRoot_, uint256 endTime_ ) MerkleDistributor(token_, merkleRoot_) Ownable(msg.sender) { if (endTime_ <= block.timestamp) revert EndTimeInPast(); endTime = endTime_; } function claim( uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof ) public override { if (block.timestamp > endTime) revert ClaimWindowFinished(); super.claim(index, account, amount, merkleProof); } function withdraw() external onlyOwner { if (block.timestamp < endTime) revert NoWithdrawDuringClaim(); IERC20(token).safeTransfer( msg.sender, IERC20(token).balanceOf(address(this)) ); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IMerkleDistributorWithDeadline} from "src/uniswap/interfaces/IMerkleDistributorWithDeadline.sol"; import {IOwnable2Step} from "src/interfaces/IOwnable2Step.sol"; import {Amount} from "src/types/Amount.sol"; import {Id} from "src/types/Id.sol"; import {Index} from "src/types/Index.sol"; import {MerkleProof} from "src/types/MerkleProof.sol"; import {MerkleRoot} from "src/types/MerkleRoot.sol"; import {Number} from "src/types/Number.sol"; import {Timestamp} from "src/types/Timestamp.sol"; /// @title A periphery contract to deploy and manage Merkle Distributor contracts. /// @author Timelord /// @dev The owner can deploy new Merkle Distributor contracts and token transfer. /// @dev Users can claim rewards from multiple Distributor contracts. interface IMerkleDistributorPeriphery is IOwnable2Step { event Queue( address indexed deployer, IERC20 indexed token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ); event Unqueue(); event CreateFromQueue( Id indexed id, IMerkleDistributorWithDeadline indexed distributor, IERC20 indexed token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ); /// @dev Event emitted when the contract deploys a new distributor contract. /// @notice Only emitted once for a unique id and distributor address. /// @param id The id of the newly deployed distributor contract. Id increments per new deployment. /// The id parameter is indexed, to query for one specific event for the given id. /// @param distributor The address of the newly deployed distributor contract. /// The distributor parameter is indexed, to query for one specific event for the given address. /// @param token The address of the ERC20 token reward. /// The token parameter is indexed, to query for all events where the reward is the ERC20 token. /// @param totalAmount The total amount of ERC20 token reward. /// @param merkleRoot The 32 bytes merkle root. /// @param endTime The deadline for claiming the rewards in unix timestamp. event Create( Id indexed id, IMerkleDistributorWithDeadline indexed distributor, IERC20 indexed token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ); /// @dev Event emitted when the owner withdraws the remaining rewards after deadline from a distributor contract. /// @notice Only emitted once for a unique id and distributor address. /// @param distributor The address of the distributor contract where the rewards were withdrawn. /// The distributor parameter is indexed, to query for one specific withdraw event for the given address. /// @param amount The amount of remaining ERC20 token reward withdrawn. event Withdraw( IMerkleDistributorWithDeadline indexed distributor, Amount amount ); error CannotQueueWithDeployerAsZeroAddress(); error CannotQueueWithTokenAsZeroAddress(); error CannotQueueWithTotalAmountAsZero(); error CannotQueueWithEndTimeInThePast(); /// @dev Reverts with this error when calling the create function with token address as zero. error CannotCreateWithTokenAsZeroAddress(); /// @dev Reverts with this error when calling the create function with total amount as zero. error CannotCreateWithTotalAmountAsZero(); /// @dev Reverts with this error when calling the create function with end time in the past. error CannotCreateWithEndTimeInThePast(); error OnlyAuthorizedOwner(address account); error InconsistentQueuedToken(); error InconsistentQueuedTotalAmount(); error InconsistentQueuedMerkleRoot(); error InconsistentQueuedEndTime(); /// @dev View the address given the id of the distributor contract. /// @notice Returns the zero address if the distributor contract is not deployed given the id. /// @param id The id of the distributor address. function merkleDistributor( Id id ) external view returns (IMerkleDistributorWithDeadline); /// @dev View the total number of distributor contracts deployed. function totalMerkleDistributors() external view returns (Number); function ownerGivenId(Id id) external view returns (address); /// @dev A record of a single query for areClaimed function call. /// @param distributor The address of the distributor contract being queried. /// @param index The index of the mapping of user address and reward amount where the merkle root is generated from. struct Query { IMerkleDistributorWithDeadline distributor; Index index; } /// @dev Checks that the reward from multiple distributors are claimed given the index of the mapping. /// @param queries The list of queries. /// @return results The list of boolean results. /// @notice The length of queries and results will always be equal. /// @notice When the query distributor address does not exist or the index does not exist for the mapping, it will return false. function areClaimed( Query[] calldata queries ) external view returns (bool[] memory results); function queued() external view returns ( address deployer, IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ); function queue( address deployer, IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) external; function unqueue() external; function createFromQueue( IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) external returns (Id id, IMerkleDistributorWithDeadline distributor); /// @dev The owner deploys a new distributor contract and transfer the necessary ERC20 tokens to the distributor. /// @dev Emits the Create event. /// @notice Can only be called by the owner. /// @notice The owner must first approve this contract for token transfer. /// @param token The address of the ERC20 token reward. /// Reverts when the address does not follow ERC20 standard or is zero address. /// Reverts with CannotCreateWithTokenAsZeroAddress when the address is the zero address. /// @param totalAmount The total amount of ERC20 token reward being distributed. /// Reverts when there is not enough ERC20 token from the owner. /// Reverts with CannotCreateWithTotalAmountAsZero when the totalAmount is zero. /// @param merkleRoot The merkle root of the distributor contract for claiming verification. /// @param endTime The deadline of distribution and claim in unix timestamp. /// Reverts when with CannotCreateWithEndTimeInThePast the block timestamp is greater than the endTime on function call. /// @return id The id of the newly deployed distributor contract. /// @return distributor The address of the newly deployed distributor contract. function create( IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime ) external returns (Id id, IMerkleDistributorWithDeadline distributor); /// @dev A record of a single order for claim function call. /// @param distributor The address of the distributor contract being claimed. /// @param index The index of the mapping of user address and reward amount where the merkle root is generated from. /// @param amount The exact amount of ERC20 token address being claimed. /// @notice The amount must be exactly equal to the mapping of user address and reward amount. /// @param merkleProof A list of 32 bytes for verification of claiming. struct Order { IMerkleDistributorWithDeadline distributor; Index index; Amount amount; MerkleProof[] merkleProof; } /// @dev Msg sender claims the rewards given the list of orders. /// @dev Transfer the ERC20 token rewards to the correct user. /// @notice Reverts when any of the orders fail. /// @notice User can alternatively claim the rewards directly from the individual distributor contracts, but one at a time. /// @notice The contract does not emit any events. Instead the individual distributor contracts will emit the Claimed events. /// Ingest these events by getting the address of the deployed distributor contracts from the Create event. /// @param orders The list of orders for claiming. function claim(Order[] calldata orders) external; /// @dev The owner withdraws the remaining rewards of the distributor contracts. /// @dev Can only be called when the distributor contracts have reached the endTime deadline. /// @dev Emits Withdraw event for each withdrawal of distributor contracts. /// @notice Reverts when any withdrawals fail. /// @param distributors The list of distributor addresses to withdraw from. /// @return amounts The list of amount of token withdrawn. /// Distributors and amounts will always have the same length. /// The token denomination of amount is the token reward of the distributor of the same index. function withdraw( IMerkleDistributorWithDeadline[] calldata distributors ) external returns (Amount[] memory amounts); }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @title Interface for Two Step Ownable contract /// @author Timelord interface IOwnable2Step { /// @dev Returns the address of the owner. /// @notice Returns zero when there is no owner. function owner() external view returns (address); /// @dev Returns the address of the pending owner. /// @notice Returns zero when there is no pending owner. function pendingOwner() external view returns (address); /// @dev Renounce ownership of the contract. /// @dev Can only be called by the owner. /// @dev The address of the owner will change to zero address. /// @notice This transaction cannot be reversed. /// @notice Owner must make sure any remaining rewards in the distributors are all withdrawn. function renounceOwnership() external; /// @dev Transfer the ownership to another address. /// @dev Can only be called by the owner. /// @dev Does not transfer the ownership immediately, instead set the pending owner first. /// @notice The pending owner must accept ownership to finalize the ownership transfer. /// @param newOwner The address of the new pending owner. function transferOwnership(address newOwner) external; /// @dev The new pending owner accepts the ownership transfer. /// @notice Can only be called by the pending owner. function acceptOwnership() external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev Represents the ERC20 token amount. type Amount is uint256; using {add as +, sub as -, isZero} for Amount global; function add(Amount a, Amount b) pure returns (Amount sum) { sum = Amount.wrap(Amount.unwrap(a) + Amount.unwrap(b)); } function sub(Amount a, Amount b) pure returns (Amount difference) { difference = Amount.wrap(Amount.unwrap(a) - Amount.unwrap(b)); } function isZero(Amount a) pure returns (bool result) { result = Amount.unwrap(a) == 0; }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev Unique id for identification. type Id is uint256;
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev The index for mapping of user addresses and reward amount. type Index is uint256;
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev Represents the individual 32 bytes from a merkle proof of the distributor contract. type MerkleProof is bytes32;
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev Represents the 32 bytes merkle root of the distributor contract. type MerkleRoot is bytes32;
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev Represents the number of contracts. type Number is uint256;
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.20; /// @dev Represents the number of seconds in unix timestamp. type Timestamp is uint256; using {add as +, sub as -, equal as ==, notEqual as !=, lessThan as <, lessThanOrEqual as <=, greaterThan as >, greaterThanOrEqual as >=} for Timestamp global; function add(Timestamp a, Timestamp b) pure returns (Timestamp sum) { sum = Timestamp.wrap(Timestamp.unwrap(a) + Timestamp.unwrap(b)); } function sub(Timestamp a, Timestamp b) pure returns (Timestamp difference) { difference = Timestamp.wrap(Timestamp.unwrap(a) - Timestamp.unwrap(b)); } function equal(Timestamp a, Timestamp b) pure returns (bool result) { result = Timestamp.unwrap(a) == Timestamp.unwrap(b); } function notEqual(Timestamp a, Timestamp b) pure returns (bool result) { result = Timestamp.unwrap(a) != Timestamp.unwrap(b); } function lessThan(Timestamp a, Timestamp b) pure returns (bool result) { result = Timestamp.unwrap(a) < Timestamp.unwrap(b); } function lessThanOrEqual(Timestamp a, Timestamp b) pure returns (bool result) { result = Timestamp.unwrap(a) <= Timestamp.unwrap(b); } function greaterThan(Timestamp a, Timestamp b) pure returns (bool result) { result = Timestamp.unwrap(a) > Timestamp.unwrap(b); } function greaterThanOrEqual(Timestamp a, Timestamp b) pure returns (bool result) { result = Timestamp.unwrap(a) >= Timestamp.unwrap(b); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity =0.8.20; // Allows anyone to claim a token if they exist in a merkle root. interface IMerkleDistributor { // Returns the address of the token distributed by this contract. function token() external view returns (address); // Returns the merkle root of the merkle tree containing account balances available to claim. function merkleRoot() external view returns (bytes32); // Returns true if the index has been marked claimed. function isClaimed(uint256 index) external view returns (bool); // Claim the given amount of the token to the given address. Reverts if the inputs are invalid. function claim( uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof ) external; // This event is triggered whenever a call to #claim succeeds. event Claimed(uint256 index, address account, uint256 amount); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity =0.8.20; import {IERC20, SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {MerkleProof} from "openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol"; import {IMerkleDistributor} from "./interfaces/IMerkleDistributor.sol"; error AlreadyClaimed(); error InvalidProof(); contract MerkleDistributor is IMerkleDistributor { using SafeERC20 for IERC20; address public immutable override token; bytes32 public immutable override merkleRoot; // This is a packed array of booleans. mapping(uint256 => uint256) private claimedBitMap; constructor(address token_, bytes32 merkleRoot_) { token = token_; merkleRoot = merkleRoot_; } function isClaimed(uint256 index) public view override returns (bool) { uint256 claimedWordIndex = index / 256; uint256 claimedBitIndex = index % 256; uint256 claimedWord = claimedBitMap[claimedWordIndex]; uint256 mask = (1 << claimedBitIndex); return claimedWord & mask == mask; } function _setClaimed(uint256 index) private { uint256 claimedWordIndex = index / 256; uint256 claimedBitIndex = index % 256; claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex); } function claim( uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof ) public virtual override { if (isClaimed(index)) revert AlreadyClaimed(); // Verify the merkle proof. bytes32 node = keccak256(abi.encodePacked(index, account, amount)); if (!MerkleProof.verify(merkleProof, merkleRoot, node)) revert InvalidProof(); // Mark it claimed and send the token. _setClaimed(index); IERC20(token).safeTransfer(account, amount); emit Claimed(index, account, amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.20; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the Merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates Merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** *@dev The multiproof provided is not valid. */ error MerkleProofInvalidMultiproof(); /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Sorts the pair (a, b) and hashes the result. */ function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } /** * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory. */ function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
{ "remappings": [ "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"chosenOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CannotCreateWithEndTimeInThePast","type":"error"},{"inputs":[],"name":"CannotCreateWithTokenAsZeroAddress","type":"error"},{"inputs":[],"name":"CannotCreateWithTotalAmountAsZero","type":"error"},{"inputs":[],"name":"CannotQueueWithDeployerAsZeroAddress","type":"error"},{"inputs":[],"name":"CannotQueueWithEndTimeInThePast","type":"error"},{"inputs":[],"name":"CannotQueueWithTokenAsZeroAddress","type":"error"},{"inputs":[],"name":"CannotQueueWithTotalAmountAsZero","type":"error"},{"inputs":[],"name":"Create2EmptyBytecode","type":"error"},{"inputs":[],"name":"Create2FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"Create2InsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InconsistentQueuedEndTime","type":"error"},{"inputs":[],"name":"InconsistentQueuedMerkleRoot","type":"error"},{"inputs":[],"name":"InconsistentQueuedToken","type":"error"},{"inputs":[],"name":"InconsistentQueuedTotalAmount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OnlyAuthorizedOwner","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"Id","name":"id","type":"uint256"},{"indexed":true,"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"Amount","name":"totalAmount","type":"uint256"},{"indexed":false,"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"Timestamp","name":"endTime","type":"uint256"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"Id","name":"id","type":"uint256"},{"indexed":true,"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"Amount","name":"totalAmount","type":"uint256"},{"indexed":false,"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"Timestamp","name":"endTime","type":"uint256"}],"name":"CreateFromQueue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"deployer","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"Amount","name":"totalAmount","type":"uint256"},{"indexed":false,"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"Timestamp","name":"endTime","type":"uint256"}],"name":"Queue","type":"event"},{"anonymous":false,"inputs":[],"name":"Unqueue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"},{"indexed":false,"internalType":"Amount","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"},{"internalType":"Index","name":"index","type":"uint256"}],"internalType":"struct IMerkleDistributorPeriphery.Query[]","name":"queries","type":"tuple[]"}],"name":"areClaimed","outputs":[{"internalType":"bool[]","name":"results","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"},{"internalType":"Index","name":"index","type":"uint256"},{"internalType":"Amount","name":"amount","type":"uint256"},{"internalType":"MerkleProof[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct IMerkleDistributorPeriphery.Order[]","name":"orders","type":"tuple[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"Amount","name":"totalAmount","type":"uint256"},{"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"internalType":"Timestamp","name":"endTime","type":"uint256"}],"name":"create","outputs":[{"internalType":"Id","name":"id","type":"uint256"},{"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"Amount","name":"totalAmount","type":"uint256"},{"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"internalType":"Timestamp","name":"endTime","type":"uint256"}],"name":"createFromQueue","outputs":[{"internalType":"Id","name":"id","type":"uint256"},{"internalType":"contract IMerkleDistributorWithDeadline","name":"distributor","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"Id","name":"id","type":"uint256"}],"name":"merkleDistributor","outputs":[{"internalType":"contract IMerkleDistributorWithDeadline","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Id","name":"id","type":"uint256"}],"name":"ownerGivenId","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deployer","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"Amount","name":"totalAmount","type":"uint256"},{"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"internalType":"Timestamp","name":"endTime","type":"uint256"}],"name":"queue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"queued","outputs":[{"internalType":"address","name":"deployer","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"Amount","name":"totalAmount","type":"uint256"},{"internalType":"MerkleRoot","name":"merkleRoot","type":"bytes32"},{"internalType":"Timestamp","name":"endTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalMerkleDistributors","outputs":[{"internalType":"Number","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unqueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMerkleDistributorWithDeadline[]","name":"distributors","type":"address[]"}],"name":"withdraw","outputs":[{"internalType":"Amount[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b506040516200252f3803806200252f8339810160408190526200003491620000e5565b806001600160a01b0381166200006457604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006f8162000077565b505062000117565b600180546001600160a01b0319169055620000928162000095565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215620000f857600080fd5b81516001600160a01b03811681146200011057600080fd5b9392505050565b61240880620001276000396000f3fe60806040523480156200001157600080fd5b5060043610620001145760003560e01c80639f52c9ca11620000a3578063c5c26558116200006e578063c5c26558146200027e578063e30c39781462000295578063f12aa09a146200029f578063f2fde38b14620002b657600080fd5b80639f52c9ca1462000220578063ba7229eb146200022a578063bd5dec981462000241578063c3568a95146200026757600080fd5b8063715018a611620000e4578063715018a614620001c057806379ba509714620001ca5780638b934a2714620001d45780638da5cb5b14620001fa57600080fd5b8062acdd4f146200011957806316049ddf146200012f578063282493ed146200017257806357ed373614620001a7575b600080fd5b6002546040519081526020015b60405180910390f35b600554600654600754600854600954604080516001600160a01b039687168152959094166020860152928401919091526060830152608082015260a00162000126565b620001896200018336600462001288565b620002cd565b604080519283526001600160a01b0390911660208301520162000126565b620001be620001b8366004620012c6565b62000494565b005b620001be620005be565b620001be620005ca565b620001eb620001e53660046200131c565b620005d4565b60405162000126919062001396565b6000546001600160a01b03165b6040516001600160a01b03909116815260200162000126565b620001be6200070d565b620002076200023b366004620013de565b6200076d565b620002586200025236600462001447565b620007bc565b6040516200012691906200148d565b620001896200027836600462001288565b62000a6c565b620002076200028f366004620013de565b62000c57565b6200020762000c8a565b620001be620002b036600462001447565b62000c9f565b620001be620002c7366004620014c7565b62000e16565b600080620002da62000e24565b6001600160a01b038616620003025760405163bb62d70760e01b815260040160405180910390fd5b8462000321576040516344c3b8e360e11b815260040160405180910390fd5b6200032c8342101590565b156200034b576040516372f6d1bf60e01b815260040160405180910390fd5b600254604051909250620003d49060009084906200036c6020820162001264565b601f1982820381018352601f9091011660408181526001600160a01b038c16602083015281018990526060810188905260800160408051601f1981840301815290829052620003bf929160200162001519565b60405160208183030381529060405262000e53565b600280546001810182556000919091527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b0319166001600160a01b03838116919091179091559091506200043790871633838862000ed7565b60408051868152602081018690529081018490526001600160a01b03808816919083169084907f77450914c9c2d659c31a927d51d59a36a4dcfe6a146353409650477094916e1a906060015b60405180910390a494509492505050565b6200049e62000e24565b6001600160a01b038516620004c6576040516338e017d160e11b815260040160405180910390fd5b6001600160a01b038416620004ed5760405162eee51d60e51b815260040160405180910390fd5b826200050c57604051633444b7bf60e11b815260040160405180910390fd5b620005178142101590565b15620005365760405163dcc2fe8360e01b815260040160405180910390fd5b600580546001600160a01b03199081166001600160a01b038881169182179093556006805490921692871692831790915560078590556008849055600983905560408051868152602081018690529081018490527fece3ccb6842de39912f776098f6f443f67ea50787ecf8ac491008e9908fdeb589060600160405180910390a35050505050565b620005c862000f40565b565b620005c862000f56565b6060818067ffffffffffffffff811115620005f357620005f36200153a565b6040519080825280602002602001820160405280156200061d578160200160208202803683370190505b50915060005b818110156200070057600085858381811062000643576200064362001550565b9050604002018036038101906200065b9190620015c6565b80516020820151604051639e34070f60e01b815260048101919091529192506001600160a01b031690639e34070f90602401602060405180830381865afa158015620006ab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006d1919062001624565b848381518110620006e657620006e662001550565b911515602092830291909101909101525060010162000623565b505092915050565b905090565b6200071762000e24565b600580546001600160a01b031990811690915560068054909116905560006007819055600881905560098190556040517fd1174d377eff8dd393a08859565c12d4609a69ed639a4435abed670c76a060149190a1565b60025460009082106200078257506000919050565b6000828152600360205260409020546001600160a01b03168015620007a85780620007b5565b6000546001600160a01b03165b9392505050565b6060818067ffffffffffffffff811115620007db57620007db6200153a565b60405190808252806020026020018201604052801562000805578160200160208202803683370190505b50915060005b81811015620007005760008585838181106200082b576200082b62001550565b9050602002016020810190620008429190620014c7565b6001600160a01b0380821660009081526004602052604090205491925016806200087457506000546001600160a01b03165b6001600160a01b0381163314620008a557604051637235955160e11b81523360048201526024015b60405180910390fd5b6000826001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620008e6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200090c919062001648565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918316906370a0823190602401602060405180830381865afa15801562000959573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200097f919062001668565b9050801562000a5b57808786815181106200099e576200099e62001550565b602002602001018181525050836001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620009e657600080fd5b505af1158015620009fb573d6000803e3d6000fd5b5062000a16925050506001600160a01b038316338362000fa1565b836001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243648260405162000a5291815260200190565b60405180910390a25b5050600190920191506200080b9050565b60055460009081906001600160a01b0316331462000aa057604051637235955160e11b81523360048201526024016200089c565b6006546001600160a01b0387811691161462000acf57604051635ec6434160e11b815260040160405180910390fd5b600754851462000af25760405163102cd90f60e21b815260040160405180910390fd5b600954831462000b1557604051636f97330f60e11b815260040160405180910390fd5b600954421062000b38576040516372f6d1bf60e01b815260040160405180910390fd5b60025460405190925062000b599060009084906200036c6020820162001264565b60028054600181019091557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b038084166001600160a01b031992831681179093556005805483169055600680548316905560006007819055600881905560098190558681526003602090815260408083208054861633908117909155958352600490915290208054909216831790915591925062000c0691881690838862000ed7565b60408051868152602081018690529081018490526001600160a01b03808816919083169084907fe4de786a856841c14cb168cd00d835e79d5d30ff7ac07ba0ef064eb6b2e13cfb9060600162000483565b60006002828154811062000c6f5762000c6f62001550565b6000918252602090912001546001600160a01b031692915050565b6000620007086001546001600160a01b031690565b8060005b8181101562000e1057600084848381811062000cc35762000cc362001550565b905060200281019062000cd7919062001682565b62000ce290620016a3565b606081015180519192509060008167ffffffffffffffff81111562000d0b5762000d0b6200153a565b60405190808252806020026020018201604052801562000d35578160200160208202803683370190505b50905060005b8281101562000d8a5783818151811062000d595762000d5962001550565b602002602001015182828151811062000d765762000d7662001550565b602090810291909101015260010162000d3b565b5083600001516001600160a01b0316632e7ba6ef8560200151338760400151856040518563ffffffff1660e01b815260040162000dcb949392919062001787565b600060405180830381600087803b15801562000de657600080fd5b505af115801562000dfb573d6000803e3d6000fd5b50506001909601955062000ca3945050505050565b50505050565b62000e218162000fd9565b50565b6000546001600160a01b03163314620005c85760405163118cdaa760e01b81523360048201526024016200089c565b60008347101562000e815760405163392efb2b60e21b8152476004820152602481018590526044016200089c565b815160000362000ea457604051631328927760e21b815260040160405180910390fd5b8282516020840186f590506001600160a01b038116620007b557604051633a0ba96160e11b815260040160405180910390fd5b6040516001600160a01b03848116602483015283811660448301526064820183905262000e109186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506200104c565b62000f4a62000e24565b620005c86000620010b6565b338062000f6262000c8a565b6001600160a01b03161462000f965760405163118cdaa760e01b81526001600160a01b03821660048201526024016200089c565b62000e2181620010b6565b6040516001600160a01b0383811660248301526044820183905262000fd491859182169063a9059cbb9060640162000f0d565b505050565b62000fe362000e24565b600180546001600160a01b0319166001600160a01b038316908117909155620010146000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000620010636001600160a01b03841683620010d1565b905080516000141580156200108b57508080602001905181019062001089919062001624565b155b1562000fd457604051635274afe760e01b81526001600160a01b03841660048201526024016200089c565b600180546001600160a01b031916905562000e2181620010e1565b6060620007b58383600062001131565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b606081471015620011585760405163cd78605960e01b81523060048201526024016200089c565b600080856001600160a01b03168486604051620011769190620017ed565b60006040518083038185875af1925050503d8060008114620011b5576040519150601f19603f3d011682016040523d82523d6000602084013e620011ba565b606091505b5091509150620011cc868383620011d6565b9695505050505050565b606082620011ef57620011e9826200123a565b620007b5565b81511580156200120757506001600160a01b0384163b155b156200123257604051639996b31560e01b81526001600160a01b03851660048201526024016200089c565b5080620007b5565b8051156200124b5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b610bd780620017fc83390190565b6001600160a01b038116811462000e2157600080fd5b600080600080608085870312156200129f57600080fd5b8435620012ac8162001272565b966020860135965060408601359560600135945092505050565b600080600080600060a08688031215620012df57600080fd5b8535620012ec8162001272565b94506020860135620012fe8162001272565b94979496505050506040830135926060810135926080909101359150565b600080602083850312156200133057600080fd5b823567ffffffffffffffff808211156200134957600080fd5b818501915085601f8301126200135e57600080fd5b8135818111156200136e57600080fd5b8660208260061b85010111156200138457600080fd5b60209290920196919550909350505050565b6020808252825182820181905260009190848201906040850190845b81811015620013d2578351151583529284019291840191600101620013b2565b50909695505050505050565b600060208284031215620013f157600080fd5b5035919050565b60008083601f8401126200140b57600080fd5b50813567ffffffffffffffff8111156200142457600080fd5b6020830191508360208260051b85010111156200144057600080fd5b9250929050565b600080602083850312156200145b57600080fd5b823567ffffffffffffffff8111156200147357600080fd5b6200148185828601620013f8565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015620013d257835183529284019291840191600101620014a9565b600060208284031215620014da57600080fd5b8135620007b58162001272565b6000815160005b818110156200150a5760208185018101518683015201620014ee565b50600093019283525090919050565b6000620015326200152b8386620014e7565b84620014e7565b949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6040516080810167ffffffffffffffff811182821017156200158c576200158c6200153a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715620015be57620015be6200153a565b604052919050565b600060408284031215620015d957600080fd5b6040516040810181811067ffffffffffffffff82111715620015ff57620015ff6200153a565b60405282356200160f8162001272565b81526020928301359281019290925250919050565b6000602082840312156200163757600080fd5b81518015158114620007b557600080fd5b6000602082840312156200165b57600080fd5b8151620007b58162001272565b6000602082840312156200167b57600080fd5b5051919050565b60008235607e198336030181126200169957600080fd5b9190910192915050565b600060808236031215620016b657600080fd5b620016c062001566565b8235620016cd8162001272565b81526020838101358183015260408085013590830152606084013567ffffffffffffffff80821115620016ff57600080fd5b9085019036601f8301126200171357600080fd5b8135818111156200172857620017286200153a565b8060051b91506200173b84830162001592565b81815291830184019184810190368411156200175657600080fd5b938501935b8385101562001776578435825293850193908501906200175b565b606087015250939695505050505050565b600060808201868352602060018060a01b038716818501528560408501526080606085015281855180845260a086019150828701935060005b81811015620017de57845183529383019391830191600101620017c0565b50909998505050505050505050565b6000620007b58284620014e756fe60e060405234801561001057600080fd5b50604051610bd7380380610bd783398101604081905261002f916100ee565b6001600160a01b03831660805260a0829052338061006757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100708161009c565b50428111610091576040516372e54d4d60e01b815260040160405180910390fd5b60c052506101319050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008060006060848603121561010357600080fd5b83516001600160a01b038116811461011a57600080fd5b602085015160409095015190969495509392505050565b60805160a05160c051610a4f6101886000396000818160ec015281816101a201526101ff01526000818160b2015261043c01526000818161017e01528181610261015281816102d6015261049a0152610a4f6000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063715018a611610066578063715018a6146101165780638da5cb5b1461011e5780639e34070f14610143578063f2fde38b14610166578063fc0c546a1461017957600080fd5b80632e7ba6ef146100985780632eb4a7ab146100ad5780633197cbb6146100e75780633ccfd60b1461010e575b600080fd5b6100ab6100a6366004610869565b6101a0565b005b6100d47f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6100d47f000000000000000000000000000000000000000000000000000000000000000081565b6100ab6101f5565b6100ab6102ff565b6001546001600160a01b03165b6040516001600160a01b0390911681526020016100de565b610156610151366004610900565b610311565b60405190151581526020016100de565b6100ab610174366004610919565b610352565b61012b7f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000004211156101e15760405163d365f61160e01b815260040160405180910390fd5b6101ee8585858585610395565b5050505050565b6101fd610512565b7f000000000000000000000000000000000000000000000000000000000000000042101561023e57604051630ee56a2b60e41b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526102fd9033906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156102a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102cc9190610934565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016919061053f565b565b610307610512565b6102fd6000610596565b60008061032061010084610963565b9050600061033061010085610977565b60009283526020839052604090922054600190921b9182169091149392505050565b61035a610512565b6001600160a01b03811661038957604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61039281610596565b50565b61039e85610311565b156103bc57604051630c8d9eab60e31b815260040160405180910390fd5b60408051602081018790526bffffffffffffffffffffffff19606087901b1691810191909152605481018490526000906074016040516020818303038152906040528051906020012090506104678383808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152507f000000000000000000000000000000000000000000000000000000000000000092508591506105e89050565b610484576040516309bde33960e01b815260040160405180910390fd5b61048d86610600565b6104c16001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016868661053f565b604080518781526001600160a01b03871660208201529081018590527f4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed0269060600160405180910390a1505050505050565b6001546001600160a01b031633146102fd5760405163118cdaa760e01b8152336004820152602401610380565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261059190849061063e565b505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000826105f585846106a1565b1490505b9392505050565b600061060e61010083610963565b9050600061061e61010084610977565b6000928352602083905260409092208054600190931b9092179091555050565b60006106536001600160a01b038416836106ee565b90508051600014158015610678575080806020019051810190610676919061098b565b155b1561059157604051635274afe760e01b81526001600160a01b0384166004820152602401610380565b600081815b84518110156106e6576106d2828683815181106106c5576106c56109ad565b60200260200101516106fc565b9150806106de816109c3565b9150506106a6565b509392505050565b60606105f98383600061072b565b60008183106107185760008281526020849052604090206105f9565b60008381526020839052604090206105f9565b6060814710156107505760405163cd78605960e01b8152306004820152602401610380565b600080856001600160a01b0316848660405161076c91906109ea565b60006040518083038185875af1925050503d80600081146107a9576040519150601f19603f3d011682016040523d82523d6000602084013e6107ae565b606091505b50915091506107be8683836107c8565b9695505050505050565b6060826107dd576107d882610824565b6105f9565b81511580156107f457506001600160a01b0384163b155b1561081d57604051639996b31560e01b81526001600160a01b0385166004820152602401610380565b50806105f9565b8051156108345780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461086457600080fd5b919050565b60008060008060006080868803121561088157600080fd5b853594506108916020870161084d565b935060408601359250606086013567ffffffffffffffff808211156108b557600080fd5b818801915088601f8301126108c957600080fd5b8135818111156108d857600080fd5b8960208260051b85010111156108ed57600080fd5b9699959850939650602001949392505050565b60006020828403121561091257600080fd5b5035919050565b60006020828403121561092b57600080fd5b6105f98261084d565b60006020828403121561094657600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b6000826109725761097261094d565b500490565b6000826109865761098661094d565b500690565b60006020828403121561099d57600080fd5b815180151581146105f957600080fd5b634e487b7160e01b600052603260045260246000fd5b6000600182016109e357634e487b7160e01b600052601160045260246000fd5b5060010190565b6000825160005b81811015610a0b57602081860181015185830152016109f1565b50600092019182525091905056fea26469706673582212202a0118383b366e23655fcf98e5c30a3b4f7e12513319ce3d5a1c1458067b212764736f6c63430008140033a2646970667358221220ee2ff0f9e1ebf09b10055c3e2d8b9148d6b5f80864e55bebde04cbd4bbcb474c64736f6c634300081400330000000000000000000000000705faa3e7c39217bc4d9d386c323d4df880949c
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620001145760003560e01c80639f52c9ca11620000a3578063c5c26558116200006e578063c5c26558146200027e578063e30c39781462000295578063f12aa09a146200029f578063f2fde38b14620002b657600080fd5b80639f52c9ca1462000220578063ba7229eb146200022a578063bd5dec981462000241578063c3568a95146200026757600080fd5b8063715018a611620000e4578063715018a614620001c057806379ba509714620001ca5780638b934a2714620001d45780638da5cb5b14620001fa57600080fd5b8062acdd4f146200011957806316049ddf146200012f578063282493ed146200017257806357ed373614620001a7575b600080fd5b6002546040519081526020015b60405180910390f35b600554600654600754600854600954604080516001600160a01b039687168152959094166020860152928401919091526060830152608082015260a00162000126565b620001896200018336600462001288565b620002cd565b604080519283526001600160a01b0390911660208301520162000126565b620001be620001b8366004620012c6565b62000494565b005b620001be620005be565b620001be620005ca565b620001eb620001e53660046200131c565b620005d4565b60405162000126919062001396565b6000546001600160a01b03165b6040516001600160a01b03909116815260200162000126565b620001be6200070d565b620002076200023b366004620013de565b6200076d565b620002586200025236600462001447565b620007bc565b6040516200012691906200148d565b620001896200027836600462001288565b62000a6c565b620002076200028f366004620013de565b62000c57565b6200020762000c8a565b620001be620002b036600462001447565b62000c9f565b620001be620002c7366004620014c7565b62000e16565b600080620002da62000e24565b6001600160a01b038616620003025760405163bb62d70760e01b815260040160405180910390fd5b8462000321576040516344c3b8e360e11b815260040160405180910390fd5b6200032c8342101590565b156200034b576040516372f6d1bf60e01b815260040160405180910390fd5b600254604051909250620003d49060009084906200036c6020820162001264565b601f1982820381018352601f9091011660408181526001600160a01b038c16602083015281018990526060810188905260800160408051601f1981840301815290829052620003bf929160200162001519565b60405160208183030381529060405262000e53565b600280546001810182556000919091527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b0319166001600160a01b03838116919091179091559091506200043790871633838862000ed7565b60408051868152602081018690529081018490526001600160a01b03808816919083169084907f77450914c9c2d659c31a927d51d59a36a4dcfe6a146353409650477094916e1a906060015b60405180910390a494509492505050565b6200049e62000e24565b6001600160a01b038516620004c6576040516338e017d160e11b815260040160405180910390fd5b6001600160a01b038416620004ed5760405162eee51d60e51b815260040160405180910390fd5b826200050c57604051633444b7bf60e11b815260040160405180910390fd5b620005178142101590565b15620005365760405163dcc2fe8360e01b815260040160405180910390fd5b600580546001600160a01b03199081166001600160a01b038881169182179093556006805490921692871692831790915560078590556008849055600983905560408051868152602081018690529081018490527fece3ccb6842de39912f776098f6f443f67ea50787ecf8ac491008e9908fdeb589060600160405180910390a35050505050565b620005c862000f40565b565b620005c862000f56565b6060818067ffffffffffffffff811115620005f357620005f36200153a565b6040519080825280602002602001820160405280156200061d578160200160208202803683370190505b50915060005b818110156200070057600085858381811062000643576200064362001550565b9050604002018036038101906200065b9190620015c6565b80516020820151604051639e34070f60e01b815260048101919091529192506001600160a01b031690639e34070f90602401602060405180830381865afa158015620006ab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006d1919062001624565b848381518110620006e657620006e662001550565b911515602092830291909101909101525060010162000623565b505092915050565b905090565b6200071762000e24565b600580546001600160a01b031990811690915560068054909116905560006007819055600881905560098190556040517fd1174d377eff8dd393a08859565c12d4609a69ed639a4435abed670c76a060149190a1565b60025460009082106200078257506000919050565b6000828152600360205260409020546001600160a01b03168015620007a85780620007b5565b6000546001600160a01b03165b9392505050565b6060818067ffffffffffffffff811115620007db57620007db6200153a565b60405190808252806020026020018201604052801562000805578160200160208202803683370190505b50915060005b81811015620007005760008585838181106200082b576200082b62001550565b9050602002016020810190620008429190620014c7565b6001600160a01b0380821660009081526004602052604090205491925016806200087457506000546001600160a01b03165b6001600160a01b0381163314620008a557604051637235955160e11b81523360048201526024015b60405180910390fd5b6000826001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620008e6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200090c919062001648565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918316906370a0823190602401602060405180830381865afa15801562000959573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200097f919062001668565b9050801562000a5b57808786815181106200099e576200099e62001550565b602002602001018181525050836001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620009e657600080fd5b505af1158015620009fb573d6000803e3d6000fd5b5062000a16925050506001600160a01b038316338362000fa1565b836001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243648260405162000a5291815260200190565b60405180910390a25b5050600190920191506200080b9050565b60055460009081906001600160a01b0316331462000aa057604051637235955160e11b81523360048201526024016200089c565b6006546001600160a01b0387811691161462000acf57604051635ec6434160e11b815260040160405180910390fd5b600754851462000af25760405163102cd90f60e21b815260040160405180910390fd5b600954831462000b1557604051636f97330f60e11b815260040160405180910390fd5b600954421062000b38576040516372f6d1bf60e01b815260040160405180910390fd5b60025460405190925062000b599060009084906200036c6020820162001264565b60028054600181019091557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b038084166001600160a01b031992831681179093556005805483169055600680548316905560006007819055600881905560098190558681526003602090815260408083208054861633908117909155958352600490915290208054909216831790915591925062000c0691881690838862000ed7565b60408051868152602081018690529081018490526001600160a01b03808816919083169084907fe4de786a856841c14cb168cd00d835e79d5d30ff7ac07ba0ef064eb6b2e13cfb9060600162000483565b60006002828154811062000c6f5762000c6f62001550565b6000918252602090912001546001600160a01b031692915050565b6000620007086001546001600160a01b031690565b8060005b8181101562000e1057600084848381811062000cc35762000cc362001550565b905060200281019062000cd7919062001682565b62000ce290620016a3565b606081015180519192509060008167ffffffffffffffff81111562000d0b5762000d0b6200153a565b60405190808252806020026020018201604052801562000d35578160200160208202803683370190505b50905060005b8281101562000d8a5783818151811062000d595762000d5962001550565b602002602001015182828151811062000d765762000d7662001550565b602090810291909101015260010162000d3b565b5083600001516001600160a01b0316632e7ba6ef8560200151338760400151856040518563ffffffff1660e01b815260040162000dcb949392919062001787565b600060405180830381600087803b15801562000de657600080fd5b505af115801562000dfb573d6000803e3d6000fd5b50506001909601955062000ca3945050505050565b50505050565b62000e218162000fd9565b50565b6000546001600160a01b03163314620005c85760405163118cdaa760e01b81523360048201526024016200089c565b60008347101562000e815760405163392efb2b60e21b8152476004820152602481018590526044016200089c565b815160000362000ea457604051631328927760e21b815260040160405180910390fd5b8282516020840186f590506001600160a01b038116620007b557604051633a0ba96160e11b815260040160405180910390fd5b6040516001600160a01b03848116602483015283811660448301526064820183905262000e109186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506200104c565b62000f4a62000e24565b620005c86000620010b6565b338062000f6262000c8a565b6001600160a01b03161462000f965760405163118cdaa760e01b81526001600160a01b03821660048201526024016200089c565b62000e2181620010b6565b6040516001600160a01b0383811660248301526044820183905262000fd491859182169063a9059cbb9060640162000f0d565b505050565b62000fe362000e24565b600180546001600160a01b0319166001600160a01b038316908117909155620010146000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000620010636001600160a01b03841683620010d1565b905080516000141580156200108b57508080602001905181019062001089919062001624565b155b1562000fd457604051635274afe760e01b81526001600160a01b03841660048201526024016200089c565b600180546001600160a01b031916905562000e2181620010e1565b6060620007b58383600062001131565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b606081471015620011585760405163cd78605960e01b81523060048201526024016200089c565b600080856001600160a01b03168486604051620011769190620017ed565b60006040518083038185875af1925050503d8060008114620011b5576040519150601f19603f3d011682016040523d82523d6000602084013e620011ba565b606091505b5091509150620011cc868383620011d6565b9695505050505050565b606082620011ef57620011e9826200123a565b620007b5565b81511580156200120757506001600160a01b0384163b155b156200123257604051639996b31560e01b81526001600160a01b03851660048201526024016200089c565b5080620007b5565b8051156200124b5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b610bd780620017fc83390190565b6001600160a01b038116811462000e2157600080fd5b600080600080608085870312156200129f57600080fd5b8435620012ac8162001272565b966020860135965060408601359560600135945092505050565b600080600080600060a08688031215620012df57600080fd5b8535620012ec8162001272565b94506020860135620012fe8162001272565b94979496505050506040830135926060810135926080909101359150565b600080602083850312156200133057600080fd5b823567ffffffffffffffff808211156200134957600080fd5b818501915085601f8301126200135e57600080fd5b8135818111156200136e57600080fd5b8660208260061b85010111156200138457600080fd5b60209290920196919550909350505050565b6020808252825182820181905260009190848201906040850190845b81811015620013d2578351151583529284019291840191600101620013b2565b50909695505050505050565b600060208284031215620013f157600080fd5b5035919050565b60008083601f8401126200140b57600080fd5b50813567ffffffffffffffff8111156200142457600080fd5b6020830191508360208260051b85010111156200144057600080fd5b9250929050565b600080602083850312156200145b57600080fd5b823567ffffffffffffffff8111156200147357600080fd5b6200148185828601620013f8565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015620013d257835183529284019291840191600101620014a9565b600060208284031215620014da57600080fd5b8135620007b58162001272565b6000815160005b818110156200150a5760208185018101518683015201620014ee565b50600093019283525090919050565b6000620015326200152b8386620014e7565b84620014e7565b949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6040516080810167ffffffffffffffff811182821017156200158c576200158c6200153a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715620015be57620015be6200153a565b604052919050565b600060408284031215620015d957600080fd5b6040516040810181811067ffffffffffffffff82111715620015ff57620015ff6200153a565b60405282356200160f8162001272565b81526020928301359281019290925250919050565b6000602082840312156200163757600080fd5b81518015158114620007b557600080fd5b6000602082840312156200165b57600080fd5b8151620007b58162001272565b6000602082840312156200167b57600080fd5b5051919050565b60008235607e198336030181126200169957600080fd5b9190910192915050565b600060808236031215620016b657600080fd5b620016c062001566565b8235620016cd8162001272565b81526020838101358183015260408085013590830152606084013567ffffffffffffffff80821115620016ff57600080fd5b9085019036601f8301126200171357600080fd5b8135818111156200172857620017286200153a565b8060051b91506200173b84830162001592565b81815291830184019184810190368411156200175657600080fd5b938501935b8385101562001776578435825293850193908501906200175b565b606087015250939695505050505050565b600060808201868352602060018060a01b038716818501528560408501526080606085015281855180845260a086019150828701935060005b81811015620017de57845183529383019391830191600101620017c0565b50909998505050505050505050565b6000620007b58284620014e756fe60e060405234801561001057600080fd5b50604051610bd7380380610bd783398101604081905261002f916100ee565b6001600160a01b03831660805260a0829052338061006757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100708161009c565b50428111610091576040516372e54d4d60e01b815260040160405180910390fd5b60c052506101319050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008060006060848603121561010357600080fd5b83516001600160a01b038116811461011a57600080fd5b602085015160409095015190969495509392505050565b60805160a05160c051610a4f6101886000396000818160ec015281816101a201526101ff01526000818160b2015261043c01526000818161017e01528181610261015281816102d6015261049a0152610a4f6000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063715018a611610066578063715018a6146101165780638da5cb5b1461011e5780639e34070f14610143578063f2fde38b14610166578063fc0c546a1461017957600080fd5b80632e7ba6ef146100985780632eb4a7ab146100ad5780633197cbb6146100e75780633ccfd60b1461010e575b600080fd5b6100ab6100a6366004610869565b6101a0565b005b6100d47f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6100d47f000000000000000000000000000000000000000000000000000000000000000081565b6100ab6101f5565b6100ab6102ff565b6001546001600160a01b03165b6040516001600160a01b0390911681526020016100de565b610156610151366004610900565b610311565b60405190151581526020016100de565b6100ab610174366004610919565b610352565b61012b7f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000004211156101e15760405163d365f61160e01b815260040160405180910390fd5b6101ee8585858585610395565b5050505050565b6101fd610512565b7f000000000000000000000000000000000000000000000000000000000000000042101561023e57604051630ee56a2b60e41b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526102fd9033906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156102a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102cc9190610934565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016919061053f565b565b610307610512565b6102fd6000610596565b60008061032061010084610963565b9050600061033061010085610977565b60009283526020839052604090922054600190921b9182169091149392505050565b61035a610512565b6001600160a01b03811661038957604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61039281610596565b50565b61039e85610311565b156103bc57604051630c8d9eab60e31b815260040160405180910390fd5b60408051602081018790526bffffffffffffffffffffffff19606087901b1691810191909152605481018490526000906074016040516020818303038152906040528051906020012090506104678383808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152507f000000000000000000000000000000000000000000000000000000000000000092508591506105e89050565b610484576040516309bde33960e01b815260040160405180910390fd5b61048d86610600565b6104c16001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016868661053f565b604080518781526001600160a01b03871660208201529081018590527f4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed0269060600160405180910390a1505050505050565b6001546001600160a01b031633146102fd5760405163118cdaa760e01b8152336004820152602401610380565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261059190849061063e565b505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000826105f585846106a1565b1490505b9392505050565b600061060e61010083610963565b9050600061061e61010084610977565b6000928352602083905260409092208054600190931b9092179091555050565b60006106536001600160a01b038416836106ee565b90508051600014158015610678575080806020019051810190610676919061098b565b155b1561059157604051635274afe760e01b81526001600160a01b0384166004820152602401610380565b600081815b84518110156106e6576106d2828683815181106106c5576106c56109ad565b60200260200101516106fc565b9150806106de816109c3565b9150506106a6565b509392505050565b60606105f98383600061072b565b60008183106107185760008281526020849052604090206105f9565b60008381526020839052604090206105f9565b6060814710156107505760405163cd78605960e01b8152306004820152602401610380565b600080856001600160a01b0316848660405161076c91906109ea565b60006040518083038185875af1925050503d80600081146107a9576040519150601f19603f3d011682016040523d82523d6000602084013e6107ae565b606091505b50915091506107be8683836107c8565b9695505050505050565b6060826107dd576107d882610824565b6105f9565b81511580156107f457506001600160a01b0384163b155b1561081d57604051639996b31560e01b81526001600160a01b0385166004820152602401610380565b50806105f9565b8051156108345780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461086457600080fd5b919050565b60008060008060006080868803121561088157600080fd5b853594506108916020870161084d565b935060408601359250606086013567ffffffffffffffff808211156108b557600080fd5b818801915088601f8301126108c957600080fd5b8135818111156108d857600080fd5b8960208260051b85010111156108ed57600080fd5b9699959850939650602001949392505050565b60006020828403121561091257600080fd5b5035919050565b60006020828403121561092b57600080fd5b6105f98261084d565b60006020828403121561094657600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b6000826109725761097261094d565b500490565b6000826109865761098661094d565b500690565b60006020828403121561099d57600080fd5b815180151581146105f957600080fd5b634e487b7160e01b600052603260045260246000fd5b6000600182016109e357634e487b7160e01b600052601160045260246000fd5b5060010190565b6000825160005b81811015610a0b57602081860181015185830152016109f1565b50600092019182525091905056fea26469706673582212202a0118383b366e23655fcf98e5c30a3b4f7e12513319ce3d5a1c1458067b212764736f6c63430008140033a2646970667358221220ee2ff0f9e1ebf09b10055c3e2d8b9148d6b5f80864e55bebde04cbd4bbcb474c64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000705faa3e7c39217bc4d9d386c323d4df880949c
-----Decoded View---------------
Arg [0] : chosenOwner (address): 0x0705FaA3e7C39217bc4D9d386c323D4Df880949c
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000000705faa3e7c39217bc4d9d386c323d4df880949c
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.