pragmasolidity ^0.5.16;import"openzeppelin-solidity-2.3.0/contracts/token/ERC20/IERC20.sol";
import"synthetix-2.43.1/contracts/Owned.sol";
import"openzeppelin-solidity-2.3.0/contracts/cryptography/MerkleProof.sol";
import"synthetix-2.43.1/contracts/Pausable.sol";
/**
* Contract which implements a merkle airdrop for a given token
* Based on an account balance snapshot stored in a merkle tree
*/contractAirdropisOwned, Pausable{
IERC20 public token;
bytes32public root; // merkle tree rootuint256public startTime;
mapping(uint256=>uint256) public _claimed;
constructor(address _owner,
IERC20 _token,
bytes32 _root
) publicOwned(_owner) Pausable() {
token = _token;
root = _root;
startTime =block.timestamp;
}
// Check if a given reward has already been claimedfunctionclaimed(uint256 index) publicviewreturns (uint256 claimedBlock, uint256 claimedMask) {
claimedBlock = _claimed[index /256];
claimedMask = (uint256(1) <<uint256(index %256));
require((claimedBlock & claimedMask) ==0, "Tokens have already been claimed");
}
// helper for the dappfunctioncanClaim(uint256 index) externalviewreturns (bool) {
uint256 claimedBlock = _claimed[index /256];
uint256 claimedMask = (uint256(1) <<uint256(index %256));
return ((claimedBlock & claimedMask) ==0);
}
// Get airdrop tokens assigned to address// Requires sending merkle proof to the functionfunctionclaim(uint256 index,
uint256 amount,
bytes32[] memory merkleProof
) publicnotPaused{
require(token.balanceOf(address(this)) > amount, "Contract doesnt have enough tokens");
// Make sure the tokens have not already been redeemed
(uint256 claimedBlock, uint256 claimedMask) = claimed(index);
_claimed[index /256] = claimedBlock | claimedMask;
// Compute the merkle leaf from index, recipient and amountbytes32 leaf =keccak256(abi.encodePacked(index, msg.sender, amount));
// verify the proof is validrequire(MerkleProof.verify(merkleProof, root, leaf), "Proof is not valid");
// Redeem!
token.transfer(msg.sender, amount);
emit Claim(msg.sender, amount, block.timestamp);
}
function_selfDestruct(addresspayable beneficiary) externalonlyOwner{
//only callable a year after end timerequire(block.timestamp> (startTime +365days), "Contract can only be selfdestruct after a year");
token.transfer(beneficiary, token.balanceOf(address(this)));
selfdestruct(beneficiary);
}
eventClaim(address claimer, uint256 amount, uint timestamp);
}
Contract Source Code
File 2 of 5: IERC20.sol
pragmasolidity ^0.5.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (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.
*/functiontransfer(address recipient, uint256 amount) externalreturns (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.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > 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.
*/functionapprove(address spender, uint256 amount) externalreturns (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.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed 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.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 3 of 5: MerkleProof.sol
pragmasolidity ^0.5.0;/**
* @dev These functions deal with verification of Merkle trees (hash trees),
*/libraryMerkleProof{
/**
* @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.
*/functionverify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internalpurereturns (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 rootreturn computedHash == root;
}
}
Contract Source Code
File 4 of 5: Owned.sol
pragmasolidity ^0.5.16;// https://docs.synthetix.io/contracts/source/contracts/ownedcontractOwned{
addresspublic owner;
addresspublic nominatedOwner;
constructor(address _owner) public{
require(_owner !=address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
functionnominateNewOwner(address _owner) externalonlyOwner{
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
functionacceptOwnership() external{
require(msg.sender== nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner =address(0);
}
modifieronlyOwner{
_onlyOwner();
_;
}
function_onlyOwner() privateview{
require(msg.sender== owner, "Only the contract owner may perform this action");
}
eventOwnerNominated(address newOwner);
eventOwnerChanged(address oldOwner, address newOwner);
}
Contract Source Code
File 5 of 5: Pausable.sol
pragmasolidity ^0.5.16;// Inheritanceimport"./Owned.sol";
// https://docs.synthetix.io/contracts/source/contracts/pausablecontractPausableisOwned{
uintpublic lastPauseTime;
boolpublic paused;
constructor() internal{
// This contract is abstract, and thus cannot be instantiated directlyrequire(owner !=address(0), "Owner must be set");
// Paused will be false, and lastPauseTime will be 0 upon initialisation
}
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/functionsetPaused(bool _paused) externalonlyOwner{
// Ensure we're actually changing the state before we do anythingif (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.if (paused) {
lastPauseTime =now;
}
// Let everyone know that our pause state has changed.emit PauseChanged(paused);
}
eventPauseChanged(bool isPaused);
modifiernotPaused{
require(!paused, "This action cannot be performed while the contract is paused");
_;
}
}