Skip to content

Commit 036e181

Browse files
committed
feat: Allow double-checking distributors
1 parent b45ecf8 commit 036e181

5 files changed

Lines changed: 125 additions & 122 deletions

File tree

script/Init.s.sol

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,47 +19,8 @@ contract BETHScript is Script {
1919
WORM public worm;
2020
Staking public staking;
2121

22-
uint256 constant PREMINE = 5851677.070643683978082748 ether;
23-
2422
function setUp() public {}
2523

26-
function newNonVested(uint256 id, address owner, uint256 amount) internal pure returns (Distributor.Share memory) {
27-
return Distributor.Share({
28-
id: id, owner: owner, tge: amount, startTime: 0, initialAmount: 0, amountPerSecond: 0, totalCap: amount
29-
});
30-
}
31-
32-
function newVested(
33-
uint256 id,
34-
address owner,
35-
uint256 tgeBips,
36-
uint256 amount,
37-
uint256 blockTimestamp,
38-
uint256 cliffPeriodInMonths,
39-
uint256 vestingInMonths
40-
) internal pure returns (Distributor.Share memory) {
41-
require(tgeBips <= 10000, "TGE should be in basis points");
42-
require(cliffPeriodInMonths <= vestingInMonths, "Cliff period incorrect");
43-
uint256 startTime = blockTimestamp + (cliffPeriodInMonths * 4 weeks);
44-
uint256 tgeAmount = amount * tgeBips / 10000;
45-
uint256 amountAfterTge = amount - tgeAmount;
46-
uint256 initialAmount = amountAfterTge * cliffPeriodInMonths / vestingInMonths;
47-
uint256 amountPerSecond = (amountAfterTge - initialAmount) / ((vestingInMonths - cliffPeriodInMonths) * 30 days);
48-
return Distributor.Share({
49-
id: id,
50-
owner: owner,
51-
tge: tgeAmount,
52-
startTime: startTime,
53-
initialAmount: initialAmount,
54-
amountPerSecond: amountPerSecond,
55-
totalCap: amount
56-
});
57-
}
58-
59-
function ofPremine(uint256 bipsA, uint256 bipsB) internal pure returns (uint256) {
60-
return PREMINE * bipsA * bipsB / 100_000_000;
61-
}
62-
6324
function run() public {
6425
vm.startBroadcast();
6526

script/Mainnet.s.sol

Lines changed: 25 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {SpendVerifier} from "../src/SpendVerifier.sol";
1010
import {IVerifier} from "../src/interfaces/IVerifier.sol";
1111
import {IRewardPool} from "../src/interfaces/IRewardPool.sol";
1212
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
13-
import {Distributor} from "../src/distributors/Distributor.sol";
13+
import {Distributor, newVested} from "../src/distributors/Distributor.sol";
1414
import {StaticDistributor} from "../src/distributors/StaticDistributor.sol";
1515
import {DynamicDistributor} from "../src/distributors/DynamicDistributor.sol";
1616

@@ -23,109 +23,63 @@ contract BETHScript is Script {
2323

2424
function setUp() public {}
2525

26-
function newNonVested(uint256 id, address owner, uint256 amount) internal pure returns (Distributor.Share memory) {
27-
return Distributor.Share({
28-
id: id, owner: owner, tge: amount, startTime: 0, initialAmount: 0, amountPerSecond: 0, totalCap: amount
29-
});
30-
}
31-
32-
function newVested(
33-
uint256 id,
34-
address owner,
35-
uint256 tgeBips,
36-
uint256 amount,
37-
uint256 blockTimestamp,
38-
uint256 cliffPeriodInMonths,
39-
uint256 vestingInMonths
40-
) internal pure returns (Distributor.Share memory) {
41-
require(tgeBips <= 10000, "TGE should be in basis points");
42-
require(cliffPeriodInMonths <= vestingInMonths, "Cliff period incorrect");
43-
uint256 startTime = blockTimestamp + (cliffPeriodInMonths * 4 weeks);
44-
uint256 tgeAmount = amount * tgeBips / 10000;
45-
uint256 amountAfterTge = amount - tgeAmount;
46-
uint256 initialAmount = amountAfterTge * cliffPeriodInMonths / vestingInMonths;
47-
uint256 amountPerSecond = (amountAfterTge - initialAmount) / ((vestingInMonths - cliffPeriodInMonths) * 30 days);
48-
return Distributor.Share({
49-
id: id,
50-
owner: owner,
51-
tge: tgeAmount,
52-
startTime: startTime,
53-
initialAmount: initialAmount,
54-
amountPerSecond: amountPerSecond,
55-
totalCap: amount
56-
});
57-
}
58-
5926
function ofPremine(uint256 bipsA, uint256 bipsB) internal pure returns (uint256) {
6027
return PREMINE * bipsA * bipsB / 100_000_000;
6128
}
6229

6330
function run() public {
6431
vm.startBroadcast();
6532

66-
uint256 startingTimestamp = block.timestamp;
33+
uint256 startingTimestamp = 1771855200; // 23 Feb 2PM UTC
6734

6835
address bethMainnet = 0x5624344235607940d4d4EE76Bf8817d403EB9Cf8;
6936
address eip7503DotEth = 0x8DC77b145d7009752D6947B3CF6D983caFA1C0Bb;
7037
address keyvankDotEth = 0x372abB165e3283C4E71ce68eFBA2934FEA5bFF45;
7138

72-
uint256 numStaticShares = 7;
39+
uint256 numStaticShares = 9;
7340
Distributor.Share[] memory staticShares = new Distributor.Share[](numStaticShares);
7441

75-
staticShares[0] = newNonVested(0, eip7503DotEth, ofPremine(4000, 10000)); // 40% LP/ICO
42+
uint256 lpIcoTotal = ofPremine(4000, 10000);
7643

7744
// Team member #1
78-
staticShares[1] = newVested(1, keyvankDotEth, 0, ofPremine(2400, 8000), block.timestamp, 6, 36);
45+
staticShares[0] = newVested(1, keyvankDotEth, 0, ofPremine(2400, 9000), startingTimestamp, 6, 36);
7946
// Team member #2
80-
staticShares[2] = newVested(2, keyvankDotEth, 0, ofPremine(2400, 1000), block.timestamp, 6, 36);
81-
// Team member #2
82-
staticShares[3] = newVested(3, keyvankDotEth, 0, ofPremine(2400, 1000), block.timestamp, 6, 36);
47+
staticShares[1] = newVested(
48+
2, address(0xBF44aa57e71a9dBa931058064438876AC7841bfa), 0, ofPremine(2400, 425), startingTimestamp, 6, 36
49+
);
50+
// Team member #3
51+
staticShares[2] = newVested(3, eip7503DotEth, 0, ofPremine(2400, 300), startingTimestamp, 6, 36);
52+
// Team member #4
53+
staticShares[3] = newVested(
54+
4, address(0x82C11504D17E26653214741f93e8400F15f88B13), 0, ofPremine(2400, 200), startingTimestamp, 6, 36
55+
);
56+
// Team member #5
57+
staticShares[4] = newVested(5, eip7503DotEth, 0, ofPremine(2400, 50), startingTimestamp, 6, 36);
58+
// Team member #6
59+
staticShares[5] = newVested(6, eip7503DotEth, 0, ofPremine(2400, 25), startingTimestamp, 6, 36);
8360

8461
// Advisors
85-
staticShares[4] = newVested(4, eip7503DotEth, 0, ofPremine(100, 10000), block.timestamp, 6, 36);
62+
staticShares[6] = newVested(7, eip7503DotEth, 0, ofPremine(100, 10000), startingTimestamp, 6, 36);
8663

8764
// Private investor
88-
staticShares[5] = newVested(5, eip7503DotEth, 0, ofPremine(800, 10000), block.timestamp, 6, 36);
65+
staticShares[7] = newVested(
66+
8, address(0x3693c57508606cC7f26b7Db3827a8eCebC628c66), 0, ofPremine(800, 10000), startingTimestamp, 6, 36
67+
);
8968

9069
// Foundation treasury
91-
staticShares[6] = newVested(6, eip7503DotEth, 50, ofPremine(1200, 10000), block.timestamp, 3, 36);
70+
staticShares[8] = newVested(9, eip7503DotEth, 500, ofPremine(1200, 10000), startingTimestamp, 3, 36);
9271

9372
uint256 staticsPremine = 0;
9473
for (uint256 i = 0; i < numStaticShares; i++) {
9574
staticsPremine += staticShares[i].totalCap;
9675
}
97-
uint256 dynamicsPremine = PREMINE - staticsPremine;
76+
uint256 dynamicsPremine = PREMINE - staticsPremine - lpIcoTotal;
9877

9978
require(877751 ether <= dynamicsPremine && dynamicsPremine <= 877752 ether, "Dynamics premine not in range!");
10079

101-
beth = BETH(bethMainnet);
102-
103-
require(staticsPremine + dynamicsPremine == PREMINE, "Invalid premine!");
104-
worm = new WORM(IERC20(beth), msg.sender, staticsPremine + dynamicsPremine, startingTimestamp);
105-
require(worm.balanceOf(msg.sender) == PREMINE, "Invalid WORM amount minted for deployer!");
106-
107-
staking = new Staking(IERC20(worm), IERC20(beth), 7 days, startingTimestamp);
108-
beth.initRewardPool(IRewardPool(staking));
109-
11080
StaticDistributor staticDist = new StaticDistributor(IERC20(worm), staticShares);
111-
worm.transfer(address(staticDist), staticsPremine);
112-
require(worm.balanceOf(address(staticDist)) == staticsPremine, "Invalid WORM balance for static distributor!");
113-
require(
114-
worm.balanceOf(msg.sender) == PREMINE - staticsPremine,
115-
"Invalid WORM balance after transfer to static distributor!"
116-
);
117-
11881
DynamicDistributor dynamicDist =
119-
new DynamicDistributor(IERC20(worm), block.timestamp + (3 * 30 days), address(0xa11ce));
120-
worm.transfer(address(dynamicDist), dynamicsPremine);
121-
require(worm.balanceOf(address(dynamicDist)) == dynamicsPremine, "Invalid WORM balance for static distributor!");
122-
require(worm.balanceOf(msg.sender) == 0, "Invalid WORM balance after transfer to dynamic distributor!");
123-
124-
require(worm.totalSupply() == PREMINE, "Invalid WORM total supply!");
125-
126-
console.log("BETH", address(beth));
127-
console.log("WORM", address(worm));
128-
console.log("Staking", address(staking));
82+
new DynamicDistributor(IERC20(worm), startingTimestamp + (3 * 30 days), address(0xa11ce));
12983

13084
vm.stopBroadcast();
13185
}

src/distributors/Distributor.sol

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
66
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
77
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
88

9+
function newVested(
10+
uint256 id,
11+
address owner,
12+
uint256 tgeBips,
13+
uint256 amount,
14+
uint256 blockTimestamp,
15+
uint256 cliffPeriodInMonths,
16+
uint256 vestingInMonths
17+
) pure returns (Distributor.Share memory) {
18+
require(tgeBips <= 10000, "TGE should be in basis points");
19+
require(cliffPeriodInMonths <= vestingInMonths, "Cliff period incorrect");
20+
uint256 startTime = blockTimestamp + (cliffPeriodInMonths * 30 days);
21+
uint256 tgeAmount = amount * tgeBips / 10000;
22+
uint256 amountAfterTge = amount - tgeAmount;
23+
uint256 initialAmount = amountAfterTge * cliffPeriodInMonths / vestingInMonths;
24+
uint256 amountPerSecond = (amountAfterTge - initialAmount) / ((vestingInMonths - cliffPeriodInMonths) * 30 days);
25+
return Distributor.Share({
26+
id: id,
27+
owner: owner,
28+
tge: tgeAmount,
29+
tgeStartTime: 0,
30+
startTime: startTime,
31+
initialAmount: initialAmount,
32+
amountPerSecond: amountPerSecond,
33+
totalCap: amount
34+
});
35+
}
36+
937
contract Distributor is ReentrancyGuard {
1038
using SafeERC20 for IERC20;
1139

@@ -18,9 +46,10 @@ contract Distributor is ReentrancyGuard {
1846
struct Share {
1947
uint256 id; // Unique identifier for the share
2048
address owner; // Address that can claim this share's emissions
21-
uint256 tge; // Amount claimable right after reveal
22-
uint256 startTime; // Timestamp when share starts
23-
uint256 initialAmount; // Amount of token immediately released
49+
uint256 tgeStartTime; // TGE amount claimable after this time
50+
uint256 tge; // Amount claimable without waiting for cliff
51+
uint256 startTime; // Timestamp when cliff period ends and lump sum is released
52+
uint256 initialAmount; // Amount of token immediately released (Cliff lump sum)
2453
uint256 amountPerSecond; // Amount of token generated per second
2554
uint256 totalCap; // Maximum total amount that can ever be claimed
2655
}
@@ -36,20 +65,33 @@ contract Distributor is ReentrancyGuard {
3665
}
3766

3867
/**
39-
* @notice Computes the total tokens that should be claimable for a share at the current time.
68+
* @notice Computes the total tokens that should be claimable for a share at the given time.
4069
* @param _shareId ID of the share.
70+
* @param _timestamp Timestamp.
4171
* @return claimable Total amount that *should* have been emitted so far.
4272
*/
43-
function calculateClaimable(uint256 _shareId) public view returns (uint256) {
73+
function calculateClaimableAtTimestamp(uint256 _shareId, uint256 _timestamp) public view returns (uint256) {
4474
Share storage share = shares[_shareId];
45-
uint256 claimable = share.tge;
46-
if (block.timestamp >= share.startTime) {
75+
uint256 claimable = 0;
76+
if (_timestamp >= share.tgeStartTime) {
77+
claimable += share.tge;
78+
}
79+
if (_timestamp >= share.startTime) {
4780
claimable += share.initialAmount;
48-
claimable += share.amountPerSecond * (block.timestamp - share.startTime);
81+
claimable += share.amountPerSecond * (_timestamp - share.startTime);
4982
}
5083
return Math.min(claimable, share.totalCap);
5184
}
5285

86+
/**
87+
* @notice Computes the total tokens that should be claimable for a share at the current time.
88+
* @param _shareId ID of the share.
89+
* @return claimable Total amount that *should* have been emitted so far.
90+
*/
91+
function calculateClaimable(uint256 _shareId) public view returns (uint256) {
92+
return calculateClaimableAtTimestamp(_shareId, block.timestamp);
93+
}
94+
5395
/**
5496
* @notice Changes the owner of a share.
5597
* @param _shareId ID of the share.

test/DynamicDistributor.t.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ contract DynamicDistributorTest is Test {
5252
Distributor.Share memory share = Distributor.Share({
5353
id: 1,
5454
owner: alice,
55+
tgeStartTime: 0,
5556
tge: 100 ether,
5657
startTime: START,
5758
initialAmount: 0,
@@ -63,14 +64,15 @@ contract DynamicDistributorTest is Test {
6364

6465
vm.prank(alice);
6566
distributor.reveal(share, sig);
66-
(, address owner,,,,,) = distributor.shares(1);
67+
(, address owner,,,,,,) = distributor.shares(1);
6768
assertEq(owner, alice);
6869
}
6970

7071
function test_RevealFailsWithInvalidSigner() public {
7172
Distributor.Share memory share = Distributor.Share({
7273
id: 1,
7374
owner: alice,
75+
tgeStartTime: 0,
7476
tge: 0,
7577
startTime: START,
7678
initialAmount: 0,
@@ -92,6 +94,7 @@ contract DynamicDistributorTest is Test {
9294
Distributor.Share memory share = Distributor.Share({
9395
id: 1,
9496
owner: alice,
97+
tgeStartTime: 0,
9598
tge: 0,
9699
startTime: START,
97100
initialAmount: 0,
@@ -109,6 +112,7 @@ contract DynamicDistributorTest is Test {
109112
Distributor.Share memory share = Distributor.Share({
110113
id: 1,
111114
owner: alice,
115+
tgeStartTime: 0,
112116
tge: 50 ether,
113117
startTime: START,
114118
initialAmount: 50 ether,
@@ -133,6 +137,7 @@ contract DynamicDistributorTest is Test {
133137
Distributor.Share memory share = Distributor.Share({
134138
id: 1,
135139
owner: alice,
140+
tgeStartTime: 0,
136141
tge: 0,
137142
startTime: START,
138143
initialAmount: 0,

0 commit comments

Comments
 (0)