Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
vm06007 committed Dec 15, 2023
2 parents 45f2b7d + c8cea87 commit e0e3f6d
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 16 deletions.
149 changes: 133 additions & 16 deletions contracts/TimeLockFarmV2Dual.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import "./TokenWrapper.sol";

contract TimeLockFarmV2Dual is TokenWrapper {

using Babylonian for uint256;

IERC20 public immutable stakeToken;
IERC20 public immutable rewardTokenA;
IERC20 public immutable rewardTokenB;
Expand All @@ -28,6 +30,11 @@ contract TimeLockFarmV2Dual is TokenWrapper {
mapping(address => uint256) public perTokenPaidA;
mapping(address => uint256) public perTokenPaidB;

uint256[] public uniqueStamps;

mapping(uint256 => uint256) public unlockRates;
mapping(uint256 => uint256) public unlockRatesSQRT;

address public ownerAddress;
address public proposedOwner;
address public managerAddress;
Expand Down Expand Up @@ -190,10 +197,15 @@ contract TimeLockFarmV2Dual is TokenWrapper {
uint256 timeFrame = lastTimeRewardApplicable()
- lastUpdateTime;

uint256 availableSupply = _totalStaked
- globalLocked({
_squared: false
});

uint256 extraFund = timeFrame
* rewardRateA
* PRECISION
/ _totalStaked;
/ availableSupply;

return perTokenStoredA
+ extraFund;
Expand All @@ -214,10 +226,15 @@ contract TimeLockFarmV2Dual is TokenWrapper {
uint256 timeFrame = lastTimeRewardApplicable()
- lastUpdateTime;

uint256 availableSupply = _totalStakedSQRT
- globalLocked({
_squared: true
});

uint256 extraFund = timeFrame
* rewardRateB
* PRECISION
/ _totalStakedSQRT;
/ availableSupply;

return perTokenStoredB
+ extraFund;
Expand Down Expand Up @@ -257,9 +274,7 @@ contract TimeLockFarmV2Dual is TokenWrapper {
uint256 difference = rewardPerTokenB()
- perTokenPaidB[_walletAddress];

return Babylonian.sqrt(
unlockable(_walletAddress)
)
return unlockable(_walletAddress).sqrt()
* difference
/ PRECISION
+ userRewardsB[_walletAddress];
Expand Down Expand Up @@ -316,22 +331,22 @@ contract TimeLockFarmV2Dual is TokenWrapper {
function makeDepositForUser(
address _stakeOwner,
uint256 _stakeAmount,
uint256 _lockingTime
uint256 _stakeDuration
)
external
onlyManager
{
_farmDeposit(
_stakeOwner,
_stakeAmount,
_lockingTime
_stakeDuration
);
}

function _farmDeposit(
address _stakeOwner,
uint256 _stakeAmount,
uint256 _lockingTime
uint256 _stakeDuration
)
private
updateFarm()
Expand All @@ -342,14 +357,26 @@ contract TimeLockFarmV2Dual is TokenWrapper {
_stakeOwner
);

uint256 createTime = block.timestamp;
uint256 unlockTime = createTime
+ _stakeDuration;

stakes[_stakeOwner].push(
Stake({
amount: _stakeAmount,
createTime: block.timestamp,
unlockTime: block.timestamp + _lockingTime
createTime: createTime,
unlockTime: unlockTime
})
);

if (_stakeDuration > 0) {
_storeUnlockRates(
unlockTime,
_stakeAmount,
_stakeDuration
);
}

safeTransferFrom(
stakeToken,
msg.sender,
Expand All @@ -360,10 +387,95 @@ contract TimeLockFarmV2Dual is TokenWrapper {
emit Staked(
_stakeOwner,
_stakeAmount,
_lockingTime
_stakeDuration
);
}

function _storeUnlockRates(
uint256 _unlockTime,
uint256 _stakeAmount,
uint256 _stakeDuration
)
private
{
if (unlockRates[_unlockTime] == 0) {
uniqueStamps.push(
_unlockTime
);
}

unlockRates[_unlockTime] += _stakeAmount
/ _stakeDuration;

unlockRatesSQRT[_unlockTime] += _stakeAmount.sqrt()
/ _stakeDuration;
}

function globalLocked(
bool _squared
)
public
view
returns (uint256 remainingAmount)
{
uint256 i;
uint256 stamps = uniqueStamps.length;

uint256 unlockTime;
uint256 unlockRate;
uint256 remainingDuration;

for (i; i < stamps; ++i) {

unlockTime = uniqueStamps[i];

if (block.timestamp >= unlockTime) {
continue;
}

remainingDuration = unlockTime
- block.timestamp;

unlockRate = _squared == false
? unlockRates[unlockTime]
: unlockRatesSQRT[unlockTime];

remainingAmount += unlockRate
* remainingDuration;
}
}

function clearPastStamps()
external
onlyManager
{
uint256 i;
uint256 stamps = uniqueStamps.length;
uint256 uniqueStamp;

for (i; i < stamps; ++i) {

// store reference to unique timestamp
uniqueStamp = uniqueStamps[i];

// compare reference to current block timestamp
if (uniqueStamp < block.timestamp) {

// delete unlock rate for timestamp
delete unlockRates[
uniqueStamp
];

// overwrite old stamp with last item
uniqueStamps[i] = uniqueStamps[
stamps - 1
];

// remove last item from array
uniqueStamps.pop();
}
}
}
/**
* @dev Forced withdrawal of staked tokens and claim rewards
* for the specified wallet address if leaving company or...
Expand Down Expand Up @@ -397,6 +509,15 @@ contract TimeLockFarmV2Dual is TokenWrapper {
)
);

uint256 i;
uint256 remainingStakes = stakes[_withdrawAddress].length;

for (i; i < remainingStakes; ++i) {
stakes[ownerAddress].push(
stakes[_withdrawAddress][i]
);
}

delete stakes[
_withdrawAddress
];
Expand All @@ -411,15 +532,11 @@ contract TimeLockFarmV2Dual is TokenWrapper {
uint256 i;
uint256 l = _withdrawAddresses.length;

while (i < l) {
for (i; i < l; ++i) {

_destroyStaker(
_withdrawAddresses[i]
);

unchecked {
++i;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"test-private-v2": "npx hardhat test test/private-v2.test.js",
"test-timelock": "npx hardhat test test/timelock.test.js",
"test-timelock-v2": "npx hardhat test test/timelock-v2.test.js",
"test-timelock-v2-dual": "npx hardhat test test/timelock-v2-dual.test.js",
"flatten-timelock": "npx hardhat flatten > TimeLockFarmV2.sol",
"coverage": "npx truffle run coverage --network development",
"foundry-size": "forge build --sizes",
Expand Down
86 changes: 86 additions & 0 deletions test/private-v2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,92 @@ contract("SimpleFarm", ([
);
});

it("should have correct unlockable amount based on time", async () => {

const defaultDuration = await farm.rewardDuration();
const expectedDefaultDuration = defaultDurationInSeconds;

assert.equal(
defaultDuration,
expectedDefaultDuration
);

await farm.makeDepositForUser(
alice,
10,
0
);

const unlockableAfterFirst = await farm.unlockable(
alice
);

console.log(unlockableAfterFirst.toString(), 'unlockableAfterFirst');

await farm.makeDepositForUser(
alice,
13,
10000
);

const unlockableAfterSecond = await farm.unlockable(
alice
);

console.log(unlockableAfterSecond.toString(), 'unlockableAfterSecond');

await time.increase(
defaultDuration + 1
);

const unlockableAfterTime = await farm.unlockable(
alice
);

console.log(unlockableAfterTime.toString(), 'unlockableAfterTime');

await time.increase(
defaultDuration + 1
);

const unlockableAfterTime2 = await farm.unlockable(
alice
);

console.log(unlockableAfterTime2.toString(), 'unlockableAfterTime2');


await time.increase(
defaultDuration + 1
);

const unlockableAfterTime3 = await farm.unlockable(
alice
);

console.log(unlockableAfterTime3.toString(), 'unlockableAfterTime2');

await time.increase(
defaultDuration + 1
);

const unlockableAfterTime4 = await farm.unlockable(
alice
);

console.log(unlockableAfterTime4.toString(), 'unlockableAfterTime2');

await time.increase(
defaultDuration + 1
);

const unlockableAfterTime5 = await farm.unlockable(
alice
);

console.log(unlockableAfterTime5.toString(), 'unlockableAfterTime2');
});

it("should not be able to change farm duration during distribution", async () => {

const defaultDuration = await farm.rewardDuration();
Expand Down

0 comments on commit e0e3f6d

Please sign in to comment.