Skip to content

Commit 503ae97

Browse files
committed
test: add MetaTokenDistributor
1 parent 9c26884 commit 503ae97

File tree

6 files changed

+229
-53
lines changed

6 files changed

+229
-53
lines changed

src/tokenDistribution/MetaTokenDistributor.sol

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ contract MetaTokenDistributor is IMetaTokenDistributor {
2020
IVestingParams private _vestingParams;
2121
uint64 private _vestingStartTime;
2222

23+
mapping(VestingType vestingType => address vesting) private _vestingAddresses;
24+
25+
modifier validateTime(VestingType vestingType) {
26+
if (block.timestamp < _vestingStartTime) {
27+
revert VestingStartTimeHasNotArrived();
28+
}
29+
30+
_;
31+
}
32+
2333
constructor(address vestingParams, uint64 vestingStartTime) {
2434
if (vestingStartTime < block.timestamp) {
2535
revert InvalidVestingStartTime();
@@ -35,38 +45,45 @@ contract MetaTokenDistributor is IMetaTokenDistributor {
3545
_vestingImpl = address(new Vesting());
3646
_META = IERC20(address(new MetaToken(address(this))));
3747

38-
if (_META.balanceOf(address(this)) != _META.totalSupply()) {
39-
revert IncorrectMetaAmountForDistribution();
40-
}
48+
assert(_META.balanceOf(address(this)) == _META.totalSupply());
4149

4250
emit MetaTokenDeployed(address(_META));
4351
}
4452

45-
function startVesting(VestingType vestingType) external returns (address vesting) {
46-
// TODO: механизм, который позволит создавать вестинг для каждого типа единожды
47-
if (block.timestamp < _vestingStartTime) {
48-
revert VestingStartTimeHasNotArrived();
53+
function startVesting(VestingType vestingType) external validateTime(vestingType) returns (address vesting) {
54+
if (_vestingAddresses[vestingType] != address(0)) {
55+
revert VestingHasAlreadyStarted();
4956
}
5057

51-
// TODO: Может быть клонировано с Clones.cloneWithImmutableArgs
52-
vesting = Clones.clone(_vestingImpl);
53-
54-
// TODO: Можно две функции объединить
5558
(
59+
Schedule memory schedule,
5660
Beneficiary[] memory beneficiaries,
57-
Schedule memory schedule
58-
) = _vestingParams.getBeneficiariesAndSchedule(vestingType);
59-
60-
(uint256 vestingAmount,,,,) = _vestingParams.getVestingParams(vestingType);
61+
uint256 vestingTotalAmount
62+
) = _vestingParams.getVestingParams(vestingType);
6163

62-
_META.safeTransfer(address(vesting), vestingAmount);
64+
vesting = Clones.clone(_vestingImpl);
6365

66+
_META.safeTransfer(vesting, vestingTotalAmount);
6467
IVesting(vesting).initialize(_META, schedule, beneficiaries);
6568

69+
_vestingAddresses[vestingType] = vesting;
70+
6671
emit VestingStarted(vesting);
6772
}
6873

69-
function getMETA() external view returns (address) {
74+
function getMETAAddress() external view returns (address) {
7075
return address(_META);
7176
}
77+
78+
function getVestingAddress(VestingType vestingType) external view returns (address) {
79+
return _vestingAddresses[vestingType];
80+
}
81+
82+
function getVestingParamsAddress() external view returns (address) {
83+
return address(_vestingParams);
84+
}
85+
86+
function getVestingStartTime() external view returns (uint64) {
87+
return _vestingStartTime;
88+
}
7289
}

src/tokenDistribution/interfaces/IMetaTokenDistributor.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ interface IMetaTokenDistributor {
99

1010
error InvalidVestingStartTime();
1111
error VestingStartTimeHasNotArrived();
12-
error IncorrectMetaAmountForDistribution();
1312
error ZeroAddress();
13+
error VestingHasAlreadyStarted();
1414

1515
function startVesting(VestingType vestingType) external returns (address vesting);
16+
function getMETAAddress() external view returns (address);
17+
function getVestingAddress(VestingType vestingType) external view returns (address);
18+
function getVestingParamsAddress() external view returns (address);
19+
function getVestingStartTime() external view returns (uint64);
1620
}

src/tokenDistribution/vesting/VestingParams.sol

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ contract VestingParams is IVestingParams {
3232
_validateAndStoreBeneficiaries(VestingType.LIQUIDITY, liquidityBeneficiaries, LIQUIDITY_TOTAL_AMOUNT);
3333
}
3434

35-
function getBeneficiariesAndSchedule(VestingType vestingType) external view returns (Beneficiary[] memory beneficiaries, Schedule memory schedule) {
36-
beneficiaries = getBeneficiaries(vestingType);
37-
schedule = getSchedule(vestingType);
38-
}
39-
4035
/// @dev Portions 100% equal 10_000. It's _BASIS_POINTS, which declared in Vesting contract
4136
function getSchedule(VestingType vestingType) public view returns (Schedule memory schedule) {
4237
(
@@ -45,7 +40,7 @@ contract VestingParams is IVestingParams {
4540
uint256 tgePortion,
4641
uint256 vestingStartMonth,
4742
uint256 vestingPortion
48-
) = _getVestingParams(vestingType);
43+
) = getVestingConstant(vestingType);
4944

5045
Period[] memory periods = new Period[](totalPeriods);
5146

@@ -74,22 +69,8 @@ contract VestingParams is IVestingParams {
7469
return _beneficiaries[vestingType];
7570
}
7671

77-
function getVestingParams(VestingType vestingType)
78-
external
79-
pure
80-
returns ( // TODO: описать возвращаемые значения в natspec
81-
uint256 totalAmount,
82-
uint256 totalPeriods,
83-
uint256 tgePortions,
84-
uint256 vestingStartMonth,
85-
uint256 vestingPortion
86-
)
87-
{
88-
return _getVestingParams(vestingType);
89-
}
90-
91-
function _getVestingParams(VestingType vestingType)
92-
private
72+
function getVestingConstant(VestingType vestingType)
73+
public
9374
pure
9475
returns ( // TODO: описать возвращаемые значения в natspec
9576
uint256 totalAmount,
@@ -120,6 +101,24 @@ contract VestingParams is IVestingParams {
120101
}
121102
}
122103

104+
function getVestingParams(VestingType vestingType)
105+
external
106+
view
107+
returns (
108+
Schedule memory,
109+
Beneficiary[] memory,
110+
uint256 totalAmount
111+
)
112+
{
113+
(totalAmount,,,,) = getVestingConstant(vestingType);
114+
115+
return (
116+
getSchedule(vestingType),
117+
getBeneficiaries(vestingType),
118+
totalAmount
119+
);
120+
}
121+
123122
function _validateAndStoreBeneficiaries(
124123
VestingType vestingType,
125124
Beneficiary[] memory beneficiaries,

src/tokenDistribution/vesting/interfaces/IVestingParams.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ interface IVestingParams {
1111

1212
function getSchedule(VestingType vestingType) external view returns (Schedule memory schedule);
1313
function getBeneficiaries(VestingType vestingType) external view returns (Beneficiary[] memory);
14-
function getBeneficiariesAndSchedule(VestingType vestingType) external view returns (Beneficiary[] memory beneficiaries, Schedule memory schedule);
15-
function getVestingParams(VestingType vestingType)
14+
function getVestingConstant(VestingType vestingType)
1615
external
1716
pure
1817
returns (
@@ -22,4 +21,12 @@ interface IVestingParams {
2221
uint256 vestingStartMonth,
2322
uint256 vestingPortion
2423
);
24+
function getVestingParams(VestingType vestingType)
25+
external
26+
view
27+
returns (
28+
Schedule memory schedule,
29+
Beneficiary[] memory beneficiaries,
30+
uint256 totalAmount
31+
);
2532
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.28;
3+
4+
import {Test, console} from "forge-std/Test.sol";
5+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6+
7+
import {MetaTokenDistributor, IMetaTokenDistributor} from "src/tokenDistribution/MetaTokenDistributor.sol";
8+
import {VestingParams} from "src/tokenDistribution/vesting/VestingParams.sol";
9+
import {Beneficiary, VestingType, Schedule} from "src/tokenDistribution/utils/Common.sol";
10+
11+
contract MetaTokenDistributorTest is Test {
12+
uint256 public constant MONTH = 30 days;
13+
uint64 public vestingStartTime;
14+
15+
VestingParams vestingParams;
16+
MetaTokenDistributor distributor;
17+
18+
address teamBeneficiary1;
19+
address teamBeneficiary2;
20+
address liquidityBeneficiary;
21+
22+
function setUp() external {
23+
vestingStartTime = uint64(block.timestamp + MONTH);
24+
(Beneficiary[] memory teamBeneficiaries, Beneficiary[] memory liquidityBeneficiaries)
25+
= _generateBeneficiaries();
26+
27+
vestingParams = new VestingParams(teamBeneficiaries, liquidityBeneficiaries);
28+
distributor = new MetaTokenDistributor(address(vestingParams), vestingStartTime);
29+
}
30+
31+
// region - Deploy -
32+
33+
function test_deploy() external view {
34+
assertEq(distributor.getVestingParamsAddress(), address(vestingParams));
35+
assertEq(distributor.getVestingStartTime(), vestingStartTime);
36+
37+
assertNotEq(distributor.getMETAAddress(), address(0));
38+
}
39+
40+
function test_deploy_revertIfInvalidVestingStartTime(uint64 invalidVestingStartTime) external {
41+
// simulate timestamp
42+
vm.warp(1000);
43+
44+
vm.assume(invalidVestingStartTime < block.timestamp);
45+
46+
vm.expectRevert(IMetaTokenDistributor.InvalidVestingStartTime.selector);
47+
48+
distributor = new MetaTokenDistributor(address(vestingParams), invalidVestingStartTime);
49+
}
50+
51+
function test_deploy_revertIfZeroAddress() external {
52+
53+
vm.expectRevert(IMetaTokenDistributor.ZeroAddress.selector);
54+
55+
distributor = new MetaTokenDistributor(address(0), vestingStartTime);
56+
}
57+
58+
function test_deploy_emitMetaTokenDeployed() external {
59+
vm.expectEmit(true, true, true, false); // data is not checking
60+
emit IMetaTokenDistributor.MetaTokenDeployed(address(0));
61+
62+
distributor = new MetaTokenDistributor(address(vestingParams), vestingStartTime);
63+
}
64+
65+
// endregion
66+
67+
// region - Start vesting -
68+
69+
function test_startVesting() external {
70+
vm.warp(vestingStartTime);
71+
IERC20 META = IERC20(distributor.getMETAAddress());
72+
73+
address vesting = distributor.startVesting(VestingType.TEAM);
74+
75+
assertEq(META.balanceOf(vesting), vestingParams.TEAM_TOTAL_AMOUNT());
76+
assertEq(META.balanceOf(address(distributor)), META.totalSupply() - vestingParams.TEAM_TOTAL_AMOUNT());
77+
assertNotEq(vesting, address(0));
78+
assertNotEq(distributor.getVestingAddress(VestingType.TEAM), address(0));
79+
}
80+
81+
function test_startVesting_emitVestingStarted() external {
82+
vm.warp(vestingStartTime);
83+
84+
// data is not checking
85+
vm.expectEmit(true, true, true, false);
86+
emit IMetaTokenDistributor.VestingStarted(address(0));
87+
88+
distributor.startVesting(VestingType.TEAM);
89+
}
90+
91+
function test_startVesting_revertIfVestingHasAlreadyStarted() external {
92+
vm.warp(vestingStartTime);
93+
94+
distributor.startVesting(VestingType.TEAM);
95+
96+
vm.expectRevert(IMetaTokenDistributor.VestingHasAlreadyStarted.selector);
97+
98+
distributor.startVesting(VestingType.TEAM);
99+
}
100+
101+
function test_startVesting_revertIfVestingStartTimeHasNotArrived(uint64 invalidVestingStartTime) external {
102+
vm.assume(invalidVestingStartTime < vestingStartTime);
103+
104+
vm.expectRevert(IMetaTokenDistributor.VestingStartTimeHasNotArrived.selector);
105+
106+
distributor.startVesting(VestingType.TEAM);
107+
}
108+
109+
// endregion
110+
111+
// region - Service functions -
112+
113+
function _generateBeneficiaries() private returns (Beneficiary[] memory teamBeneficiaries, Beneficiary[] memory liquidityBeneficiaries) {
114+
teamBeneficiary1 = makeAddr("teamBeneficiary1");
115+
teamBeneficiary2 = makeAddr("teamBeneficiary2");
116+
liquidityBeneficiary = makeAddr("liquidityBeneficiary");
117+
118+
teamBeneficiaries = new Beneficiary[](2);
119+
teamBeneficiaries[0] = Beneficiary({
120+
account: teamBeneficiary1,
121+
amount: 60_000_000e18
122+
});
123+
teamBeneficiaries[1] = Beneficiary({
124+
account: teamBeneficiary2,
125+
amount: 40_000_000e18
126+
});
127+
128+
liquidityBeneficiaries = new Beneficiary[](1);
129+
liquidityBeneficiaries[0] = Beneficiary({
130+
account: liquidityBeneficiary,
131+
amount: 287_500_000e18
132+
});
133+
}
134+
135+
// endregion
136+
}

0 commit comments

Comments
 (0)