update cec distributor

This commit is contained in:
CounterFire2023 2024-09-07 22:24:03 +08:00
parent 8d1bf19299
commit d7c52b020f
18 changed files with 1094 additions and 2155 deletions

1
.gitignore vendored
View File

@ -31,3 +31,4 @@ ignition/deployments/chain-31337
/contracts/types/
openzeppelin
imtbl
bin

View File

@ -1,3 +1,5 @@
{
"solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404"
"solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404",
"slither.solcPath": "",
"slither.hiddenDetectors": []
}

View File

@ -17,18 +17,39 @@ contract CECDistributor is ReentrancyGuard, Pausable, Ownable, Governable {
using SafeERC20 for IERC20;
mapping(address account => uint256 amount) public balanceMap;
// unlock time for this distributor
uint256 public unlockTime;
mapping(address account => uint256 amount) public releaseMap;
string public name;
IERC20 public immutable cecToken;
uint256 public constant DURATION = 86400 * 30;
uint256 private releaseAllMonth;
uint256 public start = 0;
// release ratio when tge
uint256 public tgeRatio;
uint256 public constant TGE_PRECISION = 1000000;
//
uint256 public lockDuration;
address public wallet;
event EventBalanceUpdated(address indexed account, uint256 amount);
event EventUnlockTimeUpdated(uint256 unlockTime);
event EventCECClaimed(address indexed user, address indexed to, uint256 amount);
event EventChangeAddress(address oldAddr, address newAddr);
constructor(address _cecToken, uint256 _unlockTime) {
constructor(
string memory _name,
address _cecToken,
address _wallet,
uint256 _lockDuration,
uint256 _releaseAllMonth,
uint256 _tgeRatio
) {
name = _name;
cecToken = IERC20(_cecToken);
unlockTime = _unlockTime;
wallet = _wallet;
lockDuration = _lockDuration;
releaseAllMonth = _releaseAllMonth;
tgeRatio = _tgeRatio;
}
/**
@ -58,9 +79,9 @@ contract CECDistributor is ReentrancyGuard, Pausable, Ownable, Governable {
_unpause();
}
function updateBalance(address account, uint256 amount) external onlyOwner {
balanceMap[account] = amount;
emit EventBalanceUpdated(account, amount);
function setStart(uint256 newStart) external ownerOrGov {
require(newStart > 0 && start == 0, "CECDistributor: it's already initialized");
start = newStart;
}
function updateBalances(address[] calldata accounts, uint256[] calldata amounts) external onlyOwner {
@ -71,24 +92,49 @@ contract CECDistributor is ReentrancyGuard, Pausable, Ownable, Governable {
}
}
function updateUnlockTime(uint256 _unlockTime) external onlyOwner {
unlockTime = _unlockTime;
emit EventUnlockTimeUpdated(_unlockTime);
function calcClaimAmount(address user) public view whenNotPaused returns (uint256) {
require(balanceMap[user] > 0, "CECDistributor: not in whitelist");
require(block.timestamp >= start, "CECDistributor: not in claim time");
uint256 claimAmount = 0;
uint256 tgeAmount = 0;
if (tgeRatio > 0) {
tgeAmount = ((balanceMap[user] * tgeRatio) / TGE_PRECISION);
claimAmount += tgeAmount;
}
if (block.timestamp > start + lockDuration) {
uint256 monthNum = (block.timestamp - start - lockDuration) / DURATION;
if (monthNum <= releaseAllMonth) {
claimAmount += (((balanceMap[user] - tgeAmount) * monthNum) / releaseAllMonth);
} else {
claimAmount = balanceMap[user];
}
}
claimAmount -= releaseMap[user];
return claimAmount;
}
function withdrawToken(address to, uint256 amount) external onlyOwner {
require(to != address(0), "CECDistributor: invalid address");
cecToken.safeTransfer(to, amount);
}
function claim(address to) external nonReentrant whenNotPaused {
require(block.timestamp > unlockTime, "CECDistributor: not unlock time");
function claim(address to) external nonReentrant whenNotPaused returns (uint256) {
require(start > 0, "CECDistributor: start isn't init");
require(to != address(0), "CECDistributor: invalid address");
address _user = _msgSender();
uint256 amount = balanceMap[_user];
require(amount > 0, "CECDistributor: no balance");
balanceMap[_user] = 0;
cecToken.safeTransfer(to, amount);
uint256 amount = calcClaimAmount(_user);
if (amount > 0) {
releaseMap[_user] = amount;
cecToken.safeTransferFrom(wallet, to, amount);
emit EventCECClaimed(_user, to, amount);
}
return amount;
}
function changeAddress(address from, address to) external {
require(balanceMap[to] == 0, "CECDistributor: new addr is in whitelist");
require(balanceMap[from] > 0, "CECDistributor: not in whitelist");
address _sender = _msgSender();
require(_sender == owner() || _sender == gov || _sender == from, "CECDistributor: sender not allowed");
balanceMap[to] = balanceMap[from];
balanceMap[from] = 0;
releaseMap[to] = releaseMap[from];
releaseMap[from] = 0;
emit EventChangeAddress(from, to);
}
}

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
contract CFTimelockController is TimelockController {
constructor(
uint256 minDelay,
address[] memory proposers,
address[] memory executors,
address admin
) TimelockController(minDelay, proposers, executors, admin) {}
}

View File

@ -13,7 +13,7 @@ contract Governable {
_;
}
function setGov(address _gov) external onlyGov {
function setGov(address _gov) external virtual onlyGov {
gov = _gov;
}
}

View File

@ -0,0 +1,36 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { updateArray } from "../scripts/utils"
const deployCECDistributor: DeployFunction =
async function (hre: HardhatRuntimeEnvironment) {
const provider = hre.ethers.provider;
const from = await (await provider.getSigner()).getAddress();
const config = require(`../config/config_${hre.network.name}`);
const { admin, proposers, executors } = config.admins
const { cec } = config.staking
const params: any[] = [ cec, ]
const ret = await hre.deployments.deploy("CECDistributor", {
from,
args: params,
log: true,
});
console.log("==CECDistributor addr=", ret.address);
updateArray({
name: "CECDistributor",
type: "logic",
json: "assets/contracts/CECDistributor.json",
address: ret.address,
network: hre.network.name,
});
// verify the contract
await hre.run("verify:verify", {
address: ret.address,
constructorArguments: params,
});
};
deployCECDistributor.tags = ["CECDistributor"];
export default deployCECDistributor;

View File

@ -0,0 +1,35 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { updateArray } from "../scripts/utils"
const deployTimelock: DeployFunction =
async function (hre: HardhatRuntimeEnvironment) {
const provider = hre.ethers.provider;
const from = await (await provider.getSigner()).getAddress();
const config = require(`../config/config_${hre.network.name}`);
const { admin, proposers, executors } = config.admins
const params: any[] = [3600*24, proposers, executors, admin]
const ret = await hre.deployments.deploy("CFTimelockController", {
from,
args: params,
log: true,
});
console.log("==TimelockController addr=", ret.address);
updateArray({
name: "TimelockController",
type: "logic",
json: "assets/contracts/CFTimelockController.json",
address: ret.address,
network: hre.network.name,
});
// verify the contract
await hre.run("verify:verify", {
address: ret.address,
constructorArguments: params,
});
};
deployTimelock.tags = ["CFTimelockController"];
export default deployTimelock;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -17,12 +17,6 @@
"json": "assets/contracts/GameItemMall.json",
"address": "0xaE08adb5278B107D2501e7c61907e41FEf3887D7"
},
{
"name": "TestCEC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xe34c5ea0c3083d11a735dc0609533b92130319f5"
},
{
"name": "TokenClaim",
"type": "logic",
@ -76,5 +70,11 @@
"type": "logic",
"json": "assets/contracts/RewardRouter.json",
"address": "0x775d7Dbc06835c78437C8783fE11937E46F9ec6e"
},
{
"name": "TestCEC",
"type": "erc20",
"json": "assets/contracts/EsToken.json",
"address": "0xfa1223747bae6d519580c53Cbb9C11a45b13c6b7"
}
]

36
simple_abi/NFTLock.json Normal file
View File

@ -0,0 +1,36 @@
[
"constructor(uint256,address)",
"event DurationUpdated(uint256 indexed)",
"event Lock(address indexed,address indexed,address indexed,uint256[])",
"event OwnershipTransferred(address indexed,address indexed)",
"event Paused(address)",
"event UnLock(address indexed,address indexed,uint256,(uint256,address,bool)[])",
"event Unpaused(address)",
"event VerifierUpdated(address indexed)",
"function _CACHED_CHAIN_ID() view returns (uint256)",
"function _CACHED_THIS() view returns (address)",
"function addSupportNftList(address[])",
"function addressOriginal(address,uint256) view returns (address)",
"function checkSigner(address,bytes32,bytes) pure",
"function duration() view returns (uint256)",
"function getMessageHash(address,address,(uint256,address,bool)[],address,uint256,uint256,uint256) pure returns (bytes32)",
"function lock(address,address,uint256[])",
"function lockedNft(address,address) view returns (uint256[])",
"function lockedNum(address,address) view returns (uint256)",
"function maxBatch() view returns (uint256)",
"function minDuration() view returns (uint256)",
"function onERC721Received(address,address,uint256,bytes) returns (bytes4)",
"function owner() view returns (address)",
"function passportOriginal(address,uint256) view returns (address)",
"function paused() view returns (bool)",
"function removeSupportNft(address)",
"function renounceOwnership()",
"function supportNftList(address) view returns (bool)",
"function transferOwnership(address)",
"function unlockOrMint(address,(uint256,address,bool)[],uint256,uint256,bytes)",
"function unlockWithSvr(address,uint256[])",
"function updateBatch(uint256)",
"function updateDuation(uint256)",
"function updateVerifier(address)",
"function verifier() view returns (address)"
]

112
test/testCECDistributor.ts Normal file
View File

@ -0,0 +1,112 @@
import { expect } from 'chai'
import hre from "hardhat";
import {
getBytes,
solidityPackedKeccak256,
} from 'ethers'
import {
loadFixture,
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
import { expandDecimals, increaseTime, mineBlock, print } from './shared/utilities';
const ONE_DAY = 3600 * 24
const ONE_MONTH = ONE_DAY * 30
describe('TestCECDistributor', function() {
async function deployOneContract() {
// Contracts are deployed using the first signer/account by default
const [owner, user0, user1, user2] = await hre.ethers.getSigners();
const Cec = await hre.ethers.getContractFactory("MintableBaseToken");
const cec = await Cec.deploy("test cec", "cec");
await cec.setMinter(owner.address, true)
await cec.mint(user0.address, expandDecimals(15000, 18))
const CECDistributor = await hre.ethers.getContractFactory("CECDistributor");
const lockDuration = ONE_DAY; // one day
const distributor = await CECDistributor.deploy("first", cec.target, user0.address, lockDuration, 10, 300000 );
//@ts-ignore
await cec.connect(user0).approve(distributor.target, expandDecimals(10000, 18))
const chainId = hre.network.config.chainId
const start = (Date.now() / 1000 + 3600) | 0 // one hour later
await distributor.setStart(start)
expect(await distributor.name()).to.equal("first")
await distributor.updateBalances([user1.address], [expandDecimals(1000, 18)])
return { distributor, owner, user0, user1, user2, chainId, cec, start };
}
describe("Deployment", function () {
it('should deploy CECDistributor', async function() {
const { distributor, user0, user1, user2, cec } = await loadFixture(deployOneContract);
expect(await distributor.name()).to.equal("first")
});
it('should success claim', async function() {
const { distributor, owner, user0, user1, user2, cec, start } = await loadFixture(deployOneContract);
const wallet = owner
const provider = wallet.provider;
await increaseTime(provider, 3601)
await mineBlock(provider)
const claimAmount1 = await distributor.calcClaimAmount(user1.address)
expect(claimAmount1).to.equal(expandDecimals(300, 18))
// @ts-ignore
await distributor.connect(user1).claim(user1.address)
expect(await cec.balanceOf(user1.address)).to.equal(expandDecimals(300, 18))
const claimAmount2 = await distributor.calcClaimAmount(user1.address)
expect(claimAmount2).to.equal(0)
await increaseTime(provider, ONE_DAY)
await mineBlock(provider)
const claimAmount3 = await distributor.calcClaimAmount(user1.address)
expect(claimAmount3).to.equal(0)
await increaseTime(provider, ONE_MONTH)
await mineBlock(provider)
const claimAmount4 = await distributor.calcClaimAmount(user1.address)
expect(claimAmount4).to.equal(expandDecimals(70, 18))
await increaseTime(provider, ONE_MONTH)
await mineBlock(provider)
const claimAmount5 = await distributor.calcClaimAmount(user1.address)
expect(claimAmount5).to.equal(expandDecimals(140, 18))
// @ts-ignore
await distributor.connect(user1).changeAddress(user1.address, user2.address)
const claimAmount6 = await distributor.calcClaimAmount(user2.address)
expect(claimAmount6).to.equal(expandDecimals(140, 18))
await expect(distributor.calcClaimAmount(user1.address)).to.be.revertedWith("CECDistributor: not in whitelist");
});
it('should revert claim for not start', async function() {
const { distributor, owner, user0, user1, user2, cec, start } = await loadFixture(deployOneContract);
// @ts-ignore
await expect(distributor.connect(user1).claim(user1.address)).to.be.revertedWith("CECDistributor: not in claim time");
});
it('should revert claim for not in whitelist', async function() {
const { distributor, owner, user0, user1, user2, cec, start } = await loadFixture(deployOneContract);
// @ts-ignore
await expect(distributor.connect(user2).claim(user1.address)).to.be.revertedWith("CECDistributor: not in whitelist");
});
it('should revert claim for pause', async function() {
const { distributor, owner, user0, user1, user2, cec, start } = await loadFixture(deployOneContract);
await distributor.pause()
// @ts-ignore
await expect(distributor.connect(user2).claim(user1.address)).to.be.revertedWith("Pausable: paused");
});
it('should change gov success', async function() {
const { distributor, owner, user0, user1, user2, cec, start } = await loadFixture(deployOneContract);
await distributor.setGov(user2.address)
expect(await distributor.gov()).to.equal(user2.address)
// @ts-ignore
await distributor.connect(user2).pause()
expect(await distributor.paused()).to.equal(true)
});
})
})