文件 1 的 5:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 2 的 5:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 3 的 5:MerkleProof.sol
pragma solidity >=0.6.0 <0.8.0;
library MerkleProof {
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) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
return computedHash == root;
}
}
文件 4 的 5:MerkleRedeem.sol
pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MerkleRedeem is Ownable {
IERC20 public token;
event Claimed(address _claimant, uint256 _balance);
mapping(uint => bytes32) public weekMerkleRoots;
mapping(uint => mapping(address => bool)) public claimed;
constructor(
address _token
) public {
token = IERC20(_token);
}
function disburse(
address _liquidityProvider,
uint _balance
)
private
{
if (_balance > 0) {
emit Claimed(_liquidityProvider, _balance);
require(token.transfer(_liquidityProvider, _balance), "ERR_TRANSFER_FAILED");
}
}
function claimWeek(
address _liquidityProvider,
uint _week,
uint _claimedBalance,
bytes32[] memory _merkleProof
)
public
{
require(!claimed[_week][_liquidityProvider]);
require(verifyClaim(_liquidityProvider, _week, _claimedBalance, _merkleProof), 'Incorrect merkle proof');
claimed[_week][_liquidityProvider] = true;
disburse(_liquidityProvider, _claimedBalance);
}
struct Claim {
uint week;
uint balance;
bytes32[] merkleProof;
}
function claimWeeks(
address _liquidityProvider,
Claim[] memory claims
)
public
{
uint totalBalance = 0;
Claim memory claim ;
for(uint i = 0; i < claims.length; i++) {
claim = claims[i];
require(!claimed[claim.week][_liquidityProvider]);
require(verifyClaim(_liquidityProvider, claim.week, claim.balance, claim.merkleProof), 'Incorrect merkle proof');
totalBalance += claim.balance;
claimed[claim.week][_liquidityProvider] = true;
}
disburse(_liquidityProvider, totalBalance);
}
function claimStatus(
address _liquidityProvider,
uint _begin,
uint _end
)
external
view
returns (bool[] memory)
{
uint size = 1 + _end - _begin;
bool[] memory arr = new bool[](size);
for(uint i = 0; i < size; i++) {
arr[i] = claimed[_begin + i][_liquidityProvider];
}
return arr;
}
function merkleRoots(
uint _begin,
uint _end
)
external
view
returns (bytes32[] memory)
{
uint size = 1 + _end - _begin;
bytes32[] memory arr = new bytes32[](size);
for(uint i = 0; i < size; i++) {
arr[i] = weekMerkleRoots[_begin + i];
}
return arr;
}
function verifyClaim(
address _liquidityProvider,
uint _week,
uint _claimedBalance,
bytes32[] memory _merkleProof
)
public
view
returns (bool valid)
{
bytes32 leaf = keccak256(abi.encodePacked(_liquidityProvider, _claimedBalance));
return MerkleProof.verify(_merkleProof, weekMerkleRoots[_week], leaf);
}
function seedAllocations(
uint _week,
bytes32 _merkleRoot,
uint _totalAllocation
)
external
onlyOwner
{
require(weekMerkleRoots[_week] == bytes32(0), "cannot rewrite merkle root");
weekMerkleRoots[_week] = _merkleRoot;
require(token.transferFrom(msg.sender, address(this), _totalAllocation), "ERR_TRANSFER_FAILED");
}
}
文件 5 的 5:Ownable.sol
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
{
"compilationTarget": {
"src/MerkleRedeem.sol": "MerkleRedeem"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_claimant","type":"address"},{"indexed":false,"internalType":"uint256","name":"_balance","type":"uint256"}],"name":"Claimed","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"},{"inputs":[{"internalType":"address","name":"_liquidityProvider","type":"address"},{"internalType":"uint256","name":"_begin","type":"uint256"},{"internalType":"uint256","name":"_end","type":"uint256"}],"name":"claimStatus","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidityProvider","type":"address"},{"internalType":"uint256","name":"_week","type":"uint256"},{"internalType":"uint256","name":"_claimedBalance","type":"uint256"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"}],"name":"claimWeek","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidityProvider","type":"address"},{"components":[{"internalType":"uint256","name":"week","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct MerkleRedeem.Claim[]","name":"claims","type":"tuple[]"}],"name":"claimWeeks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_begin","type":"uint256"},{"internalType":"uint256","name":"_end","type":"uint256"}],"name":"merkleRoots","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_week","type":"uint256"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_totalAllocation","type":"uint256"}],"name":"seedAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidityProvider","type":"address"},{"internalType":"uint256","name":"_week","type":"uint256"},{"internalType":"uint256","name":"_claimedBalance","type":"uint256"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"}],"name":"verifyClaim","outputs":[{"internalType":"bool","name":"valid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"weekMerkleRoots","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}]