Address Contract Partially Verified
Address
0xDc55B003e88cf9F3537A3D823AFC9797fDeEb326
Balance
0 ETH
Nonce
1
Code Size
4181 bytes
Creator
0xb5Ed650e...5E41 at tx 0x71e5492e...e56e7f
Indexed Transactions
0
Contract Bytecode
4181 bytes
0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80638da5cb5b116100715780638da5cb5b1461026357806393a10984146102ad57806394f3f81d146102cb5780639e34070f1461030f578063ecda10f514610355578063fc0c546a14610373576100a9565b806305ab421d146100ae57806324ba5884146100fc5780632e7ba6ef146101545780632eb4a7ab1461020157806335b281531461021f575b600080fd5b6100fa600480360360408110156100c457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103bd565b005b61013e6004803603602081101561011257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106bb565b6040518082815260200191505060405180910390f35b6101ff6004803603608081101561016a57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156101bb57600080fd5b8201836020820111156101cd57600080fd5b803590602001918460208302840111640100000000831117156101ef57600080fd5b90919293919293905050506106d3565b005b610209610a35565b6040518082815260200191505060405180910390f35b6102616004803603602081101561023557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a59565b005b61026b610b9a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102b5610bbe565b6040518082815260200191505060405180910390f35b61030d600480360360208110156102e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bc5565b005b61033b6004803603602081101561032557600080fd5b8101908080359060200190929190505050610d06565b604051808215151515815260200191505060405180910390f35b61035d610d58565b6040518082815260200191505060405180910390f35b61037b610d7c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61048e60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146104893373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000b5ed650ef207e051453b68a2138d7cb67cc85e4173ffffffffffffffffffffffffffffffffffffffff16146104817f00000000000000000000000000000000000000000000000000000000651490376276a700610da0565b421015610e06565b610e13565b6104e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180610fae602b913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610569576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180610fd96021913960400191505060405180910390fd5b7f0000000000000000000000006243d8cea23066d098a15582d81a598b4e8391f473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561061057600080fd5b505af1158015610624573d6000803e3d6000fd5b505050506040513d602081101561063a57600080fd5b8101908080519060200190929190505050507f09bd3894cb7ab22415416dac0fecc519855a4b0842f1c9115e562ef557ab577b8282604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15050565b60006020528060005260406000206000915090505481565b6106dc85610d06565b15610732576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180610ffa6026913960400191505060405180910390fd5b6000858585604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018281526020019350505050604051602081830303815290604052805190602001209050610808838380806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050507fcd251b77eb123682fffced00d740b11c838ad92d6e682121623ab403431f33ab83610e20565b61087a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4d65726b6c654469737472696275746f722f696e76616c69642d70726f6f660081525060200191505060405180910390fd5b61088386610ed8565b7f0000000000000000000000006243d8cea23066d098a15582d81a598b4e8391f473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86866040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561092a57600080fd5b505af115801561093e573d6000803e3d6000fd5b505050506040513d602081101561095457600080fd5b81019080805190602001909291905050506109ba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180610f2f6021913960400191505060405180910390fd5b7f4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed026868686604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a1505050505050565b7fcd251b77eb123682fffced00d740b11c838ad92d6e682121623ab403431f33ab81565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610af0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180610f7f602f913960400191505060405180910390fd5b60016000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f599a298163e1678bb1c676052a8930bf0b8a1261ed6e01b8a2391e55f700010281604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b7f000000000000000000000000b5ed650ef207e051453b68a2138d7cb67cc85e4181565b6276a70081565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610c5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180610f7f602f913960400191505060405180910390fd5b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f8834a87e641e9716be4f34527af5d23e11624f1ddeefede6ad75a9acfc31b90381604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b6000806101008381610d1457fe5b04905060006101008481610d2457fe5b0690506000600160008481526020019081526020016000205490506000826001901b90508081831614945050505050919050565b7f000000000000000000000000000000000000000000000000000000006514903781565b7f0000000000000000000000006243d8cea23066d098a15582d81a598b4e8391f481565b6000828284019150811015610e00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180610f50602f913960400191505060405180910390fd5b92915050565b6000818316905092915050565b6000818317905092915050565b60008082905060008090505b8551811015610eca576000868281518110610e4357fe5b60200260200101519050808311610e8a5782816040516020018083815260200182815260200192505050604051602081830303815290604052805190602001209250610ebc565b808360405160200180838152602001828152602001925050506040516020818303038152906040528051906020012092505b508080600101915050610e2c565b508381149150509392505050565b60006101008281610ee557fe5b04905060006101008381610ef557fe5b069050806001901b600160008481526020019081526020016000205417600160008481526020019081526020016000208190555050505056fe4d65726b6c654469737472696275746f722f7472616e736665722d6661696c65644d65726b6c654469737472696275746f72466163746f72792f6164642d75696e742d75696e742d6f766572666c6f774d65726b6c654469737472696275746f72466163746f72792f6163636f756e742d6e6f742d617574686f72697a65644d65726b6c654469737472696275746f72466163746f72792f63616e6e6f742d73656e642d746f6b656e734d65726b6c654469737472696275746f72466163746f72792f6e756c6c2d6473744d65726b6c654469737472696275746f722f64726f702d616c72656164792d636c61696d6564a2646970667358221220528f15fdf759afd9c80c6e566c7284f43679cac830e946499c4a21591ba9ff2764736f6c63430006070033
Verified Source Code Partial Match
Compiler: v0.6.7+commit.b8d736ae
EVM: istanbul
Optimization: No
MerkleDistributor.sol 371 lines
/**
*Submitted for verification at Etherscan.io on 2021-04-03
*/
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.7;
// Allows anyone to claim a token if they exist in a merkle root
abstract contract IMerkleDistributor {
// Time from the moment this contract is deployed and until the owner can withdraw leftover tokens
uint256 public constant timelapseUntilWithdrawWindow = 90 days;
// Returns the address of the token distributed by this contract
function token() virtual external view returns (address);
// Returns the merkle root of the merkle tree containing account balances available to claim
function merkleRoot() virtual external view returns (bytes32);
// Returns the timestamp when this contract was deployed
function deploymentTime() virtual external view returns (uint256);
// Returns the address for the owner of this contract
function owner() virtual external view returns (address);
// Returns true if the index has been marked claimed
function isClaimed(uint256 index) virtual external view returns (bool);
// Send tokens to an address without that address claiming them
function sendTokens(address dst, uint256 tokenAmount) virtual external;
// 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) virtual external;
// This event is triggered whenever an address is added to the set of authed addresses
event AddAuthorization(address account);
// This event is triggered whenever an address is removed from the set of authed addresses
event RemoveAuthorization(address account);
// This event is triggered whenever a call to #claim succeeds
event Claimed(uint256 index, address account, uint256 amount);
// This event is triggered whenever some tokens are sent to an address without that address claiming them
event SendTokens(address dst, uint256 tokenAmount);
}
/**
* @dev These functions deal with verification of Merkle trees (hash trees),
*/
library MerkleProof {
/**
* @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) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
// Hash(current element of the proof + current computed hash)
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
// Check if the computed hash (root) is equal to the provided root
return computedHash == root;
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}
contract MerkleDistributor is IMerkleDistributor {
// --- Auth ---
mapping (address => uint) public authorizedAccounts;
/**
* @notice Add auth to an account
* @param account Account to add auth to
*/
function addAuthorization(address account) virtual external isAuthorized {
authorizedAccounts[account] = 1;
emit AddAuthorization(account);
}
/**
* @notice Remove auth from an account
* @param account Account to remove auth from
*/
function removeAuthorization(address account) virtual external isAuthorized {
authorizedAccounts[account] = 0;
emit RemoveAuthorization(account);
}
/**
* @notice Checks whether msg.sender can call an authed function
**/
modifier isAuthorized {
require(authorizedAccounts[msg.sender] == 1, "MerkleDistributorFactory/account-not-authorized");
_;
}
/*
* @notify Checks whether an address can send tokens out of this contract
*/
modifier canSendTokens {
require(
either(authorizedAccounts[msg.sender] == 1, both(owner == msg.sender, now >= addition(deploymentTime, timelapseUntilWithdrawWindow))),
"MerkleDistributorFactory/cannot-send-tokens"
);
_;
}
// The token being distributed
address public immutable override token;
// The owner of this contract
address public immutable override owner;
// The merkle root of all addresses that get a distribution
bytes32 public immutable override merkleRoot;
// Timestamp when this contract was deployed
uint256 public immutable override deploymentTime;
// This is a packed array of booleans
mapping(uint256 => uint256) private claimedBitMap;
constructor(address token_, bytes32 merkleRoot_) public {
authorizedAccounts[msg.sender] = 1;
owner = msg.sender;
token = token_;
merkleRoot = merkleRoot_;
deploymentTime = now;
emit AddAuthorization(msg.sender);
}
// --- Math ---
function addition(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "MerkleDistributorFactory/add-uint-uint-overflow");
}
// --- Boolean Logic ---
function either(bool x, bool y) internal pure returns (bool z) {
assembly{ z := or(x, y)}
}
function both(bool x, bool y) internal pure returns (bool z) {
assembly{ z := and(x, y)}
}
// --- Administration ---
/*
* @notice Send tokens to an authorized address
* @param dst The address to send tokens to
* @param tokenAmount The amount of tokens to send
*/
function sendTokens(address dst, uint256 tokenAmount) external override canSendTokens {
require(dst != address(0), "MerkleDistributorFactory/null-dst");
IERC20(token).transfer(dst, tokenAmount);
emit SendTokens(dst, tokenAmount);
}
/*
* @notice View function returning whether an address has already claimed their tokens
* @param index The position of the address inside the merkle tree
*/
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;
}
/*
* @notice Mark an address as having claimed their distribution
* @param index The position of the address inside the merkle tree
*/
function _setClaimed(uint256 index) private {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
}
/*
* @notice Claim your distribution
* @param index The position of the address inside the merkle tree
* @param account The actual address from the tree
* @param amount The amount being distributed
* @param merkleProof The merkle path used to prove that the address is in the tree and can claim amount tokens
*/
function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external override {
require(!isClaimed(index), 'MerkleDistributor/drop-already-claimed');
// Verify the merkle proof
bytes32 node = keccak256(abi.encodePacked(index, account, amount));
require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDistributor/invalid-proof');
// Mark it claimed and send the token
_setClaimed(index);
require(IERC20(token).transfer(account, amount), 'MerkleDistributor/transfer-failed');
emit Claimed(index, account, amount);
}
}
contract MerkleDistributorFactory {
// --- Auth ---
mapping (address => uint) public authorizedAccounts;
/**
* @notice Add auth to an account
* @param account Account to add auth to
*/
function addAuthorization(address account) virtual external isAuthorized {
authorizedAccounts[account] = 1;
emit AddAuthorization(account);
}
/**
* @notice Remove auth from an account
* @param account Account to remove auth from
*/
function removeAuthorization(address account) virtual external isAuthorized {
authorizedAccounts[account] = 0;
emit RemoveAuthorization(account);
}
/**
* @notice Checks whether msg.sender can call an authed function
**/
modifier isAuthorized {
require(authorizedAccounts[msg.sender] == 1, "MerkleDistributorFactory/account-not-authorized");
_;
}
// --- Variables ---
// Number of distributors created
uint256 public nonce;
// The token that's being distributed by every merkle distributor
address public distributedToken;
// Mapping of ID => distributor address
mapping(uint256 => address) public distributors;
// Tokens left to distribute to every distributor
mapping(uint256 => uint256) public tokensToDistribute;
// --- Events ---
event AddAuthorization(address account);
event RemoveAuthorization(address account);
event DeployDistributor(uint256 id, address distributor, uint256 tokenAmount);
event SendTokensToDistributor(uint256 id);
constructor(address distributedToken_) public {
require(distributedToken_ != address(0), "MerkleDistributorFactory/null-distributed-token");
authorizedAccounts[msg.sender] = 1;
distributedToken = distributedToken_;
emit AddAuthorization(msg.sender);
}
// --- Math ---
function addition(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "MerkleDistributorFactory/add-uint-uint-overflow");
}
// --- Core Logic ---
/*
* @notice Deploy a new merkle distributor
* @param merkleRoot The merkle root used in the distributor
*/
function deployDistributor(bytes32 merkleRoot, uint256 tokenAmount) external isAuthorized {
require(tokenAmount > 0, "MerkleDistributorFactory/null-token-amount");
nonce = addition(nonce, 1);
address newDistributor = address(new MerkleDistributor(distributedToken, merkleRoot));
distributors[nonce] = newDistributor;
tokensToDistribute[nonce] = tokenAmount;
emit DeployDistributor(nonce, newDistributor, tokenAmount);
}
/*
* @notice Send tokens to a distributor
* @param nonce The nonce/id of the distributor to send tokens to
*/
function sendTokensToDistributor(uint256 id) external isAuthorized {
require(tokensToDistribute[id] > 0, "MerkleDistributorFactory/nothing-to-send");
uint256 tokensToSend = tokensToDistribute[id];
tokensToDistribute[id] = 0;
IERC20(distributedToken).transfer(distributors[id], tokensToSend);
emit SendTokensToDistributor(id);
}
/*
* @notice Sent distributedToken tokens out of this contract and to a custom destination
* @param dst The address that will receive tokens
* @param tokenAmount The token amount to send
*/
function sendTokensToCustom(address dst, uint256 tokenAmount) external isAuthorized {
require(dst != address(0), "MerkleDistributorFactory/null-dst");
IERC20(distributedToken).transfer(dst, tokenAmount);
}
/*
* @notice This contract gives up on being an authorized address inside a specific distributor contract
*/
function dropDistributorAuth(uint256 id) external isAuthorized {
MerkleDistributor(distributors[id]).removeAuthorization(address(this));
}
/*
* @notice Send tokens from a distributor contract to this contract
*/
function getBackTokensFromDistributor(uint256 id, uint256 tokenAmount) external isAuthorized {
MerkleDistributor(distributors[id]).sendTokens(address(this), tokenAmount);
}
}
Read Contract
authorizedAccounts 0x24ba5884 → uint256
deploymentTime 0xecda10f5 → uint256
isClaimed 0x9e34070f → bool
merkleRoot 0x2eb4a7ab → bytes32
owner 0x8da5cb5b → address
timelapseUntilWithdrawWindow 0x93a10984 → uint256
token 0xfc0c546a → address
Write Contract 4 functions
These functions modify contract state and require a wallet transaction to execute.
addAuthorization 0x35b28153
address account
claim 0x2e7ba6ef
uint256 index
address account
uint256 amount
bytes32[] merkleProof
removeAuthorization 0x94f3f81d
address account
sendTokens 0x05ab421d
address dst
uint256 tokenAmount
Recent Transactions
No transactions found for this address