Skip to content

Commit ec4c0af

Browse files
committed
feat: added MerkleRedeem contract migrated to Solidity v0.8
1 parent 6684af3 commit ec4c0af

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

contracts/deploy/00-home-chain-arbitration.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
119119
await execute("KlerosCore", { from: deployer, log: true }, "changeCurrencyRates", pnk, 12225583, 12);
120120
await execute("KlerosCore", { from: deployer, log: true }, "changeCurrencyRates", dai, 60327783, 11);
121121
await execute("KlerosCore", { from: deployer, log: true }, "changeCurrencyRates", weth, 1, 1);
122+
123+
await deploy("MerkleRedeem", {
124+
from: deployer,
125+
args: [pnk],
126+
log: true,
127+
});
122128
};
123129

124130
deployArbitration.tags = ["Arbitration"];
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
/**
4+
* Original code taken from: https://github.com/balancer-labs/erc20-redeemable/blob/13d478a043ec7bfce7abefe708d027dfe3e2ea84/merkle/contracts/MerkleRedeem.sol
5+
* Only comments and events were added, some variable names changed for clarity and the compiler version was upgraded to 0.8.x.
6+
*/
7+
pragma solidity 0.8.18;
8+
9+
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
10+
import "@openzeppelin/contracts/access/Ownable.sol";
11+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
12+
13+
/// @title Distribution of tokens in a recurrent fashion.
14+
contract MerkleRedeem is Ownable {
15+
// ************************************* //
16+
// * Enums / Structs * //
17+
// ************************************* //
18+
19+
struct Claim {
20+
uint week; // The week the claim is related to.
21+
uint balance; // The amount being claimed.
22+
bytes32[] merkleProof; // The merkle proof for the claim, sorted from the leaf to the root of the tree.
23+
}
24+
25+
// ************************************* //
26+
// * Events * //
27+
// ************************************* //
28+
29+
/// @dev To be emitted when a claim is made.
30+
/// @param _claimant The address of the claimant.
31+
/// @param _balance The amount being claimed.
32+
event Claimed(address _claimant, uint256 _balance);
33+
34+
// ************************************* //
35+
// * Storage * //
36+
// ************************************* //
37+
38+
IERC20 public token; // The address of the token being distributed.
39+
mapping(uint => bytes32) public weekMerkleRoots; // The merkle roots of each week. weekMerkleRoots[week].
40+
mapping(uint => mapping(address => bool)) public claimed; // Keeps track of the claim status for the given period and claimant. claimed[period][claimant].
41+
42+
// ************************************* //
43+
// * Constructor * //
44+
// ************************************* //
45+
46+
/// @param _token The address of the token being distributed.
47+
constructor(address _token) {
48+
token = IERC20(_token);
49+
}
50+
51+
// ************************************* //
52+
// * State Modifiers * //
53+
// ************************************* //
54+
55+
/// @notice Seeds a new round for the airdrop.
56+
/// @dev Will transfer tokens from the owner to this contract.
57+
/// @param _week The airdrop week.
58+
/// @param _merkleRoot The merkle root of the claims for that period.
59+
/// @param _totalAllocation The amount of tokens allocated for the distribution.
60+
function seedAllocations(uint _week, bytes32 _merkleRoot, uint _totalAllocation) external onlyOwner {
61+
require(weekMerkleRoots[_week] == bytes32(0), "cannot rewrite merkle root");
62+
weekMerkleRoots[_week] = _merkleRoot;
63+
64+
require(token.transferFrom(msg.sender, address(this), _totalAllocation), "ERR_TRANSFER_FAILED");
65+
}
66+
67+
/// @notice Makes a claim for a given claimant in a week.
68+
/// @param _liquidityProvider The address of the claimant.
69+
/// @param _week The week for the claim.
70+
/// @param _claimedBalance The amount being claimed.
71+
/// @param _merkleProof The merkle proof for the claim, sorted from the leaf to the root of the tree.
72+
function claimWeek(
73+
address _liquidityProvider,
74+
uint _week,
75+
uint _claimedBalance,
76+
bytes32[] memory _merkleProof
77+
) public {
78+
require(!claimed[_week][_liquidityProvider]);
79+
require(verifyClaim(_liquidityProvider, _week, _claimedBalance, _merkleProof), "Incorrect merkle proof");
80+
81+
claimed[_week][_liquidityProvider] = true;
82+
disburse(_liquidityProvider, _claimedBalance);
83+
}
84+
85+
/// @notice Makes multiple claims for a given claimant.
86+
/// @param _liquidityProvider The address of the claimant.
87+
/// @param claims An array of claims containing the week, balance and the merkle proof.
88+
function claimWeeks(address _liquidityProvider, Claim[] memory claims) public {
89+
uint totalBalance = 0;
90+
Claim memory claim;
91+
for (uint i = 0; i < claims.length; i++) {
92+
claim = claims[i];
93+
94+
require(!claimed[claim.week][_liquidityProvider]);
95+
require(
96+
verifyClaim(_liquidityProvider, claim.week, claim.balance, claim.merkleProof),
97+
"Incorrect merkle proof"
98+
);
99+
100+
totalBalance += claim.balance;
101+
claimed[claim.week][_liquidityProvider] = true;
102+
}
103+
disburse(_liquidityProvider, totalBalance);
104+
}
105+
106+
/// @notice Gets the claim status for given claimant from `_begin` to `_end` weeks.
107+
/// @param _liquidityProvider The address of the claimant.
108+
/// @param _begin The week to start with (inclusive).
109+
/// @param _end The week to end with (inclusive).
110+
function claimStatus(address _liquidityProvider, uint _begin, uint _end) external view returns (bool[] memory) {
111+
uint size = 1 + _end - _begin;
112+
bool[] memory arr = new bool[](size);
113+
for (uint i = 0; i < size; i++) {
114+
arr[i] = claimed[_begin + i][_liquidityProvider];
115+
}
116+
return arr;
117+
}
118+
119+
/// @notice Gets all merkle roots for from `_begin` to `_end` weeks.
120+
/// @param _begin The week to start with (inclusive).
121+
/// @param _end The week to end with (inclusive).
122+
function merkleRoots(uint _begin, uint _end) external view returns (bytes32[] memory) {
123+
uint size = 1 + _end - _begin;
124+
bytes32[] memory arr = new bytes32[](size);
125+
for (uint i = 0; i < size; i++) {
126+
arr[i] = weekMerkleRoots[_begin + i];
127+
}
128+
return arr;
129+
}
130+
131+
/// @notice Verifies a claim.
132+
/// @param _liquidityProvider The address of the claimant.
133+
/// @param _week The week for the claim.
134+
/// @param _claimedBalance The amount being claimed.
135+
/// @param _merkleProof The merkle proof for the claim, sorted from the leaf to the root of the tree.
136+
function verifyClaim(
137+
address _liquidityProvider,
138+
uint _week,
139+
uint _claimedBalance,
140+
bytes32[] memory _merkleProof
141+
) public view returns (bool valid) {
142+
bytes32 leaf = keccak256(abi.encodePacked(_liquidityProvider, _claimedBalance));
143+
return MerkleProof.verify(_merkleProof, weekMerkleRoots[_week], leaf);
144+
}
145+
146+
// ************************************* //
147+
// * Internal * //
148+
// ************************************* //
149+
150+
/// @dev Effectively pays a claimant.
151+
/// @param _liquidityProvider The address of the claimant.
152+
/// @param _balance The amount being claimed.
153+
function disburse(address _liquidityProvider, uint _balance) private {
154+
if (_balance > 0) {
155+
emit Claimed(_liquidityProvider, _balance);
156+
require(token.transfer(_liquidityProvider, _balance), "ERR_TRANSFER_FAILED");
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)