Skip to content

Commit

Permalink
optimise code
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul-D-Ant committed Nov 3, 2023
1 parent 72b3535 commit 7fc89e9
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 26 deletions.
4 changes: 2 additions & 2 deletions contracts/OLEV1Lock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import "./libraries/TransferHelper.sol";
contract OLEV1Lock {
using TransferHelper for IERC20;

IERC20 public ole;
uint64 public expireTime;
IERC20 public immutable ole;
uint64 public immutable expireTime;

event Locked (address account, uint amount);

Expand Down
6 changes: 3 additions & 3 deletions contracts/OLEV2Swap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import "./libraries/TransferHelper.sol";
contract OLEV2Swap is Adminable, ReentrancyGuard {
using TransferHelper for IERC20;

IERC20 public oleV1;
IERC20 public oleV2;
uint64 public expireTime;
IERC20 public immutable oleV1;
IERC20 public immutable oleV2;
uint64 public immutable expireTime;

event Swapped (address account, uint amount);

Expand Down
36 changes: 21 additions & 15 deletions contracts/RewardDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ contract RewardDistributor is Adminable, ReentrancyGuard {

error InvalidAmount();
error InvalidTime();
error InvalidPenalty();
error InsufficientTransfersIn();
error NotStarted();
error Expired();
error NotExpired();
Expand Down Expand Up @@ -69,22 +71,23 @@ contract RewardDistributor is Adminable, ReentrancyGuard {
}

event VestStarted(uint256 epochId, address account, uint256 balance, uint256 vestTime);
event Withdrawn(uint256 epochId, address account, uint amount, uint penalty);
event ConvertedToXOLE(uint256 epochId, address account, uint amount);
event Withdrawn(uint256 epochId, address account, uint256 amount, uint256 penalty);
event ConvertedToXOLE(uint256 epochId, address account, uint256 amount);

event EpochAdded(uint256 epochId, bytes32 merkleRoot, uint256 total, uint256 startTime, uint256 expireTime, uint256 vestDuration, uint16 penaltyBase, uint16 penaltyAdd);
event Recycled(uint256 epochId, uint256 recycledAmount);
event PenaltyWithdrawn(uint256 amount);

function vest(uint256 _epochId, uint256 _balance, bytes32[] calldata _merkleProof) external {
if (_balance == 0) revert InvalidAmount();
if (block.timestamp < epochs[_epochId].startTime) revert NotStarted();
if (block.timestamp > epochs[_epochId].expireTime) revert Expired();
Epoch storage epoch = epochs[_epochId];
if (block.timestamp < epoch.startTime) revert NotStarted();
if (block.timestamp > epoch.expireTime) revert Expired();
if (_balance == 0 || _balance + epoch.vested > epoch.total) revert InvalidAmount();

Reward memory reward = rewards[_epochId][msg.sender];
if (reward.amount > 0) revert AlreadyVested();
if (!_verifyVest(msg.sender, epochs[_epochId].merkleRoot, _balance, _merkleProof)) revert IncorrectMerkleProof();
epochs[_epochId].vested += _balance;
if (!_verifyVest(msg.sender, epoch.merkleRoot, _balance, _merkleProof)) revert IncorrectMerkleProof();
epoch.vested += _balance;
rewards[_epochId][msg.sender] = Reward(_balance, 0, block.timestamp);
emit VestStarted(_epochId, msg.sender, _balance, block.timestamp);
}
Expand Down Expand Up @@ -140,7 +143,7 @@ contract RewardDistributor is Adminable, ReentrancyGuard {
}

function getWithdrawable(address account, uint256[] calldata _epochIds) external view returns (uint256[] memory results){
uint len = _epochIds.length;
uint256 len = _epochIds.length;
results = new uint256[](len);
for (uint256 i = 0; i < len; i++) {
Reward memory reward = rewards[_epochIds[i]][account];
Expand Down Expand Up @@ -172,10 +175,12 @@ contract RewardDistributor is Adminable, ReentrancyGuard {
uint256 vestDuration,
uint16 penaltyBase,
uint16 penaltyAdd)
external onlyAdmin verifyDuration(vestDuration) {
external onlyAdminOrDeveloper verifyDuration(vestDuration) {
if (expireTime <= startTime || expireTime <= block.timestamp) revert InvalidTime();
if (total == 0 || penaltyBase > PERCENT_DIVISOR || penaltyAdd > PERCENT_DIVISOR) revert InvalidAmount();
uint epochId = ++epochIdx;
if (total == 0 || penaltyBase + penaltyAdd >= PERCENT_DIVISOR) revert InvalidAmount();
uint256 received = oleToken.safeTransferFrom(msg.sender, address(this), total);
if(received != total) revert InsufficientTransfersIn();
uint256 epochId = ++epochIdx;
epochs[epochId] = Epoch(merkleRoot, total, 0, startTime, expireTime, vestDuration, penaltyBase, penaltyAdd, false);
emit EpochAdded(epochId, merkleRoot, total, startTime, expireTime, vestDuration, penaltyBase, penaltyAdd);
}
Expand Down Expand Up @@ -242,36 +247,37 @@ contract RewardDistributor is Adminable, ReentrancyGuard {
uint256 penaltyFactor = (endTime - block.timestamp) * epoch.penaltyAdd / epoch.vestDuration + epoch.penaltyBase;
uint256 locked = reward.amount - releaseable;
penalty = locked * penaltyFactor / PERCENT_DIVISOR;
if (penalty >= locked) revert InvalidPenalty();
withdrawable += locked - penalty;
return (withdrawable, penalty);
}

function _convertOLE(uint256 epochId, address account) internal returns (uint256) {
Reward storage reward = rewards[epochId][account];
uint convertible = reward.amount - reward.withdrawn;
uint256 convertible = reward.amount - reward.withdrawn;
if (reward.amount == 0 || convertible == 0) revert InvalidAmount();
reward.withdrawn = reward.amount;
emit ConvertedToXOLE(epochId, account, convertible);
return convertible;
}

function _convertToNewXole(address account, uint oleAmount, uint256 token1MaxAmount, uint256 unlockTime) internal {
function _convertToNewXole(address account, uint256 oleAmount, uint256 token1MaxAmount, uint256 unlockTime) internal {
unlockTime = unlockTime / WEEK * WEEK;
verifyUnlockTime(unlockTime);
uint256 liquidity = formLp(oleAmount, token1MaxAmount);
pair.safeApprove(xole, liquidity);
IXOLE(xole).create_lock_for(account, liquidity, unlockTime);
}

function _convertAndIncreaseXoleAmount(address account, uint oleAmount, uint256 token1MaxAmount) internal {
function _convertAndIncreaseXoleAmount(address account, uint256 oleAmount, uint256 token1MaxAmount) internal {
(,uint256 lockTime) = IXOLE(xole).locked(account);
verifyUnlockTime(lockTime);
uint256 liquidity = formLp(oleAmount, token1MaxAmount);
pair.safeApprove(xole, liquidity);
IXOLE(xole).increase_amount_for(account, liquidity);
}

function formLp(uint oleAmount, uint256 token1MaxAmount) internal returns (uint256 liquidity){
function formLp(uint256 oleAmount, uint256 token1MaxAmount) internal returns (uint256 liquidity){
(uint256 reserveA, uint256 reserveB) = getReserves(address(oleToken), token1);
uint256 amountBOptimal = oleAmount * reserveB / reserveA;
if (amountBOptimal > token1MaxAmount) revert ExceedMax(amountBOptimal);
Expand Down
12 changes: 9 additions & 3 deletions contracts/RewardVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ contract RewardVault is Adminable, ReentrancyGuard {
/// @param startTime The time of distribution rewards begins.
/// @param endTime The time of distribution rewards ends.
/// @param ruleFlag Flag corresponds to the rules of reward distribution.
function newTranche(uint256 total, IERC20 token, uint64 startTime, uint64 endTime, uint128 ruleFlag) external payable {
function newTranche(uint256 total, IERC20 token, uint64 startTime, uint64 endTime, uint128 ruleFlag) external payable nonReentrant {
require(startTime > block.timestamp && endTime > startTime && total > 0 && ruleFlag > 0, "Incorrect inputs");
uint256 _transferIn = transferIn(msg.sender, token, total);
uint256 _trancheId = ++ trancheIdx;
Expand All @@ -75,7 +75,7 @@ contract RewardVault is Adminable, ReentrancyGuard {
/// @param startTime The time of distribution rewards begins.
/// @param endTime The time of distribution rewards ends.
/// @param add Added token amount.
function updateTranche(uint256 _trancheId, uint64 startTime, uint64 endTime, uint256 add) external payable {
function updateTranche(uint256 _trancheId, uint64 startTime, uint64 endTime, uint256 add) external payable nonReentrant {
Tranche storage tranche = tranches[_trancheId];
require(tranche.provider == msg.sender, "No permission");
require(block.timestamp < tranche.startTime, 'Already started');
Expand Down Expand Up @@ -187,7 +187,7 @@ contract RewardVault is Adminable, ReentrancyGuard {
require(tranche.provider == msg.sender, "No permission");
require(tranche.merkleRoot != _INIT_MERKLE_ROOT, "Not start");
uint recycling = tranche.unDistribute;
uint distributed = tranche.total - tranche.unDistribute - tranche.tax;
uint distributed = getDistribute(tranche);
// can recycle expire
if (block.timestamp >= tranche.expireTime && distributed > tranche.claimed){
recycling = recycling + distributed - tranche.claimed;
Expand All @@ -205,6 +205,8 @@ contract RewardVault is Adminable, ReentrancyGuard {
require(tranche.merkleRoot != _NO_MERKLE_ROOT, "No Reward");
require(tranche.expireTime > block.timestamp, "Expired");
require(!claimed[_trancheId][msg.sender], "Already claimed");
uint distributed = getDistribute(tranche);
require(_share > 0 && _share + tranche.claimed <= distributed, "Invalid amount");
require(_verifyClaim(msg.sender, tranche.merkleRoot, _share, _merkleProof), "Incorrect merkle proof");
claimed[_trancheId][msg.sender] = true;
tranche.claimed = tranche.claimed + _share;
Expand Down Expand Up @@ -249,6 +251,10 @@ contract RewardVault is Adminable, ReentrancyGuard {
}
}

function getDistribute(Tranche memory tranche) internal pure returns (uint256){
return tranche.total - tranche.unDistribute - tranche.tax;
}

function amountToShare(uint _amount, uint _reserve, uint _totalShare) private pure returns (uint share){
share = _amount > 0 && _totalShare > 0 && _reserve > 0 ? _totalShare * _amount / _reserve : _amount;
}
Expand Down
30 changes: 27 additions & 3 deletions test/RewardDistributorTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ contract("OLE reward distributor", async accounts => {
pair = await MockUniV2ClassPair.new(ole.address, usd.address, toWei(10000).toString(), toWei(10000).toString());
xole = await MockXOLE.new(pair.address);
contract = await RewardDistributor.new(ole.address, pair.address, usd.address, xole.address, 30 * day);
await ole.mint(contract.address, total);
await ole.mint(admin, total);
await ole.approve(contract.address, total);
await usd.mint(user1, defaultReward);
blockTime = await lastBlockTime();
});
Expand All @@ -63,6 +64,8 @@ contract("OLE reward distributor", async accounts => {
assert.equal(epoch.penaltyAdd, defaultExitPenaltyAdd);
m.log("add epoch success");

await ole.mint(admin, total);
await ole.approve(contract.address, total);
let tx = await contract.newEpoch(merkleRoot, total, blockTime, blockTime + 2 * day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd);
let epoch2 = await contract.epochs(2);
assert.equal(epoch2.expireTime, blockTime + 2 * day);
Expand All @@ -89,6 +92,18 @@ contract("OLE reward distributor", async accounts => {
await assertThrows(contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd), 'InvalidTime()');
})

it("Add epoch fail when penaltyBase + penaltyAdd >= PERCENT_DIVISOR", async () => {
await assertThrows(contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + 2 * day, defaultVestDuration, 4000, 6000), 'InvalidAmount()');
})

it("Add epoch fail when total is zero", async () => {
await assertThrows(contract.newEpoch(merkleRoot, 0, blockTime - 1, blockTime + 2 * day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd), 'InvalidAmount()');
})

it("Add epoch fail when msg sender ole amount not enough", async () => {
await assertThrows(contract.newEpoch(merkleRoot, toWei(100), blockTime - 1, blockTime + 2 * day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd), 'TFF');
})

// ------ user vest test ------
it("User vest success", async () => {
await contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd);
Expand Down Expand Up @@ -137,6 +152,13 @@ contract("OLE reward distributor", async accounts => {
await assertThrows(contract.vest(1, toWei(11), merkleTree.getHexProof(leaves[0]), {from : user1}), 'IncorrectMerkleProof()');
})

it("User vest fail when vest amount add epoch vested amount more than total", async () => {
await contract.newEpoch(merkleRoot, toWei(10), blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd);
m.log("epoch total reward is", toWei(10));
m.log("user1 vest amount is", toWei(11));
await assertThrows(contract.vest(1, toWei(11), merkleTree.getHexProof(leaves[0]), {from : user1}), 'InvalidAmount()');
})

// ------ user withdraw and early exit test ------
it("User withdraw released reward success", async () => {
await contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd);
Expand Down Expand Up @@ -299,6 +321,8 @@ contract("OLE reward distributor", async accounts => {

it("User withdraw multiple epoch reward at once success", async () => {
await contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd);
await ole.mint(admin, total);
await ole.approve(contract.address, total);
await contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd);
await contract.vest(1, defaultReward, merkleTree.getHexProof(leaves[0]), {from : user1});
await contract.vest(2, defaultReward, merkleTree.getHexProof(leaves[0]), {from : user1});
Expand Down Expand Up @@ -585,8 +609,8 @@ contract("OLE reward distributor", async accounts => {
await assertThrows(contract.setMinXOLELockDuration(60 * day, {from : user1}), 'caller must be admin');
})

it("Add epoch fail when the operator is not admin", async () => {
await assertThrows(contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd, {from : user1}), 'caller must be admin');
it("Add epoch fail when the operator is not admin or dev", async () => {
await assertThrows(contract.newEpoch(merkleRoot, total, blockTime - 1, blockTime + day, defaultVestDuration, defaultExitPenaltyBase, defaultExitPenaltyAdd, {from : user1}), 'Only admin or dev');
})

})
14 changes: 14 additions & 0 deletions test/RewardVaultTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,20 @@ contract("RewardVault", async accounts => {
await assertThrows(rewardVault.claim(1, toWei(20), merkleTree.getHexProof(leaves[user1Index]), {from : user1}), 'Incorrect merkle proof');
});

it("claim fail when claim amount more than distributed", async () => {
await newTranche(token.address, toWei(5), 0);
m.log("add new tranche finished, supply total amount is", toWei(5));
await advanceMultipleBlocksAndAssignTime( 1, oneDaySeconds * 2 + 1);
const merkleTree = new MerkleTree(leaves, keccak256, {sort: true});
const root = merkleTree.getHexRoot();
await rewardVault.setTrancheTree(1, toWei(2), toWei(2), toWei(1), root, {from : distributor});
m.log("set tree finished, set distribute amount is", toWei(2));
let user1Index = 0;
let user1 = users[user1Index].address;
m.log("claim amount is", toWei(10));
await assertThrows(rewardVault.claim(1, toWei(10), merkleTree.getHexProof(leaves[user1Index]), {from : user1}), 'Invalid amount');
});

it("claim fail when admin not set merkle root", async () => {
await newTranche(token.address, initRewardAmount, 0);
await advanceMultipleBlocksAndAssignTime( 1, oneDaySeconds * 2 + 1);
Expand Down

0 comments on commit 7fc89e9

Please sign in to comment.