Skip to content

Commit ede4679

Browse files
Merge pull request #104 from lista-dao/feature/lunchPoolOperator
add BatchManagementUtils.sol
2 parents c8be75b + a9d4593 commit ede4679

5 files changed

Lines changed: 211 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.10;
3+
4+
interface IClisBNBLaunchPoolDistributor {
5+
function setEpochMerkleRoot(uint64 _epochId, bytes32 _merkleRoot, address _token, uint256 _startTime, uint256 _endTime, uint256 _totalAmount) external;
6+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.10;
3+
4+
import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
5+
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
6+
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
7+
8+
import { IClisBNBLaunchPoolDistributor } from "../interfaces/IClisBNBLaunchPoolDistributor.sol";
9+
10+
contract BatchManagementUtils is AccessControlEnumerableUpgradeable, UUPSUpgradeable {
11+
struct MerkleRootInfo {
12+
address distributor;
13+
uint64 epochId;
14+
bytes32 merkleRoot;
15+
address token;
16+
uint256 startTime;
17+
uint256 endTime;
18+
uint256 totalAmount;
19+
}
20+
21+
mapping(address => bool) public distributors;
22+
23+
bytes32 public constant MANAGER = keccak256("MANAGER");
24+
bytes32 public constant OPERATOR = keccak256("OPERATOR");
25+
26+
event SetDistributor(address indexed distributor, bool status);
27+
28+
/// @custom:oz-upgrades-unsafe-allow constructor
29+
constructor() {
30+
_disableInitializers();
31+
}
32+
33+
/** @dev Initializer function to set up roles and initial state.
34+
* @param admin The address to be granted the DEFAULT_ADMIN_ROLE.
35+
* @param manager The address to be granted the MANAGER role.
36+
*
37+
* Requirements:
38+
* - `admin` must not be the zero address.
39+
* - `manager` must not be the zero address.
40+
*/
41+
function initialize(address admin, address manager) public initializer {
42+
require(admin != address(0), "admin is zero address");
43+
require(manager != address(0), "manager is zero address");
44+
__AccessControlEnumerable_init();
45+
46+
_grantRole(DEFAULT_ADMIN_ROLE, admin);
47+
_grantRole(MANAGER, manager);
48+
}
49+
50+
/**
51+
* @dev Set market fee for multiple markets in a single transaction.
52+
* @param infos The array of MerkleRootInfo structs.
53+
* Requirements:
54+
* - Caller must have the OPERATOR role for the specified `distributor` contract
55+
* - `infos` array must be non-empty.
56+
* - Each distributor in `infos` must be whitelisted.
57+
*/
58+
function batchSetEpochMerkleRoot(MerkleRootInfo[] memory infos) external {
59+
require(infos.length > 0, "No MerkleRootInfo provided");
60+
61+
for (uint256 i = 0; i < infos.length; i++) {
62+
_setEpochMerkleRoot(infos[i]);
63+
}
64+
}
65+
66+
function _setEpochMerkleRoot(MerkleRootInfo memory info) internal {
67+
require(distributors[info.distributor], "Distributor not valid");
68+
require(IAccessControl(info.distributor).hasRole(OPERATOR, msg.sender), "Not operator of distributor");
69+
70+
IClisBNBLaunchPoolDistributor distributor = IClisBNBLaunchPoolDistributor(info.distributor);
71+
distributor.setEpochMerkleRoot(
72+
info.epochId,
73+
info.merkleRoot,
74+
info.token,
75+
info.startTime,
76+
info.endTime,
77+
info.totalAmount
78+
);
79+
}
80+
81+
function setDistributor(address distributor, bool status) external onlyRole(MANAGER) {
82+
require(distributor != address(0), "distributor is zero address");
83+
require(distributors[distributor] != status, "status already set");
84+
distributors[distributor] = status;
85+
emit SetDistributor(distributor, status);
86+
}
87+
88+
89+
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
90+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
pragma solidity ^0.8.10;
2+
3+
import { Script, console } from "forge-std/Script.sol";
4+
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
5+
import { BatchManagementUtils } from "../../../contracts/utils/BatchManagementUtils.sol";
6+
7+
contract BatchManagementUtilsDeploy is Script {
8+
address admin = 0x07D274a68393E8b8a2CCf19A2ce4Ba3518735253;
9+
address manager = 0x8d388136d578dCD791D081c6042284CED6d9B0c6;
10+
address distributor1 = 0x81a62B329CC8939494d8613F614171a9955A46e8;
11+
address distributor2 = 0x8b7d334d243b74D63C4b963893267A0F5240F990;
12+
13+
function run() public {
14+
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
15+
address deployer = vm.addr(deployerPrivateKey);
16+
console.log("Deployer: ", deployer);
17+
vm.startBroadcast(deployerPrivateKey);
18+
19+
// Deploy implementation
20+
BatchManagementUtils impl = new BatchManagementUtils();
21+
console.log("Implementation: ", address(impl));
22+
23+
ERC1967Proxy proxy = new ERC1967Proxy(
24+
address(impl),
25+
abi.encodeWithSelector(impl.initialize.selector, deployer, deployer)
26+
);
27+
28+
console.log("Proxy: ", address(proxy));
29+
30+
BatchManagementUtils batchManagementUtils = BatchManagementUtils(address(proxy));
31+
32+
batchManagementUtils.setDistributor(distributor1, true);
33+
batchManagementUtils.setDistributor(distributor2, true);
34+
35+
batchManagementUtils.grantRole(batchManagementUtils.DEFAULT_ADMIN_ROLE(), admin);
36+
batchManagementUtils.grantRole(batchManagementUtils.MANAGER(), manager);
37+
38+
batchManagementUtils.revokeRole(batchManagementUtils.MANAGER(), deployer);
39+
batchManagementUtils.grantRole(batchManagementUtils.DEFAULT_ADMIN_ROLE(), deployer);
40+
41+
console.log("Deployment and setup complete.");
42+
vm.stopBroadcast();
43+
}
44+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
pragma solidity ^0.8.10;
2+
3+
import { Script, console } from "forge-std/Script.sol";
4+
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
5+
import { ClisBNBLaunchPoolDistributor } from "../../../contracts/dao/ClisBNBLaunchPoolDistributor.sol";
6+
7+
contract ClisBNBLaunchPoolDistributorDeploy is Script {
8+
function run() public {
9+
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
10+
address deployer = vm.addr(deployerPrivateKey);
11+
console.log("Deployer: ", deployer);
12+
vm.startBroadcast(deployerPrivateKey);
13+
14+
// Deploy implementation
15+
ClisBNBLaunchPoolDistributor impl = new ClisBNBLaunchPoolDistributor();
16+
console.log("Implementation: ", address(impl));
17+
18+
vm.stopBroadcast();
19+
}
20+
}

test/dao/ClisBNBLaunchPoolDistributor.t.sol

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import "forge-std/console.sol";
66

77
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
88
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
9+
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
910

1011
import "../../contracts/dao/ClisBNBLaunchPoolDistributor.sol";
12+
import "../../contracts/utils/BatchManagementUtils.sol";
1113

1214
contract ClisBNBLaunchPoolDistributorTest is Test {
1315

@@ -20,6 +22,7 @@ contract ClisBNBLaunchPoolDistributorTest is Test {
2022
address operator;
2123

2224
ClisBNBLaunchPoolDistributor cliBNBLaunchPoolDistributor;
25+
BatchManagementUtils batchManagementUtils;
2326

2427
uint256 mainnet;
2528

@@ -73,9 +76,28 @@ contract ClisBNBLaunchPoolDistributorTest is Test {
7376

7477
cliBNBLaunchPoolDistributor = ClisBNBLaunchPoolDistributor(payable(address(clisBNBLPDistributorProxy)));
7578

79+
BatchManagementUtils batchUtilsImpl = new BatchManagementUtils();
80+
ERC1967Proxy batchUtilsProxy = new ERC1967Proxy(
81+
address(batchUtilsImpl),
82+
abi.encodeWithSelector(
83+
batchManagementUtils.initialize.selector,
84+
admin,
85+
manager
86+
)
87+
);
88+
batchManagementUtils = BatchManagementUtils(address(batchUtilsProxy));
89+
90+
vm.startPrank(manager);
91+
batchManagementUtils.setDistributor(address(cliBNBLaunchPoolDistributor), true);
92+
vm.stopPrank();
93+
7694
vm.startPrank(admin);
7795
cliBNBLaunchPoolDistributor.grantRole(cliBNBLaunchPoolDistributor.OPERATOR(), operator);
96+
cliBNBLaunchPoolDistributor.grantRole(cliBNBLaunchPoolDistributor.OPERATOR(), address(batchManagementUtils));
7897
vm.stopPrank();
98+
99+
100+
79101
}
80102

81103
function test_setUp() public {
@@ -264,4 +286,33 @@ contract ClisBNBLaunchPoolDistributorTest is Test {
264286
vm.expectRevert("User already claimed");
265287
cliBNBLaunchPoolDistributor.claim(0, 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2, 123e18, proof);
266288
}
289+
290+
function test_batch_setEpochMerkleRoot_ok() public {
291+
uint64[] memory epochIds = new uint64[](1);
292+
epochIds[0] = 0;
293+
ClisBNBLaunchPoolDistributor.Epoch[] memory before = cliBNBLaunchPoolDistributor.getEpochs(epochIds);
294+
assertEq(0, before[0].startTime);
295+
296+
BatchManagementUtils.MerkleRootInfo[] memory infos = new BatchManagementUtils.MerkleRootInfo[](1);
297+
infos[0] = BatchManagementUtils.MerkleRootInfo({
298+
distributor: address(cliBNBLaunchPoolDistributor),
299+
epochId: 0,
300+
merkleRoot: root,
301+
token: address(lista),
302+
startTime: block.timestamp + 10,
303+
endTime: block.timestamp + 1000,
304+
totalAmount: 1737e18
305+
});
306+
307+
vm.startPrank(operator);
308+
batchManagementUtils.batchSetEpochMerkleRoot(infos);
309+
vm.stopPrank();
310+
311+
ClisBNBLaunchPoolDistributor.Epoch[] memory actual = cliBNBLaunchPoolDistributor.getEpochs(epochIds);
312+
assertEq(root, actual[0].merkleRoot);
313+
assertEq(address(lista), actual[0].token);
314+
assertEq(block.timestamp + 10, actual[0].startTime);
315+
assertEq(block.timestamp + 1000, actual[0].endTime);
316+
assertEq(1737e18, actual[0].totalAmount);
317+
}
267318
}

0 commit comments

Comments
 (0)