Vesting gradually in seconds in Hardhat (Solidity) - testing

I have been studying vesting schedules in blockchain lately. I wanted to test if the implementation of vesting works correctly. First i made a test that has duration of 24 months and cliff duration of 6 months. After testing everything worked correctly, so I decided to create another test but in seconds. Here is the implementation:
it("Should be able to vest gradually (in seconds)", async () => {
// deploy vesting contract
let tokenVesting: TokenVesting = await new TokenVesting__factory(owner).deploy();
await tokenVesting.deployed();
await tokenVesting.initialize();
// send tokens to vesting contract
await expect(saleToken.transfer(tokenVesting.address, 1000))
.to.emit(saleToken, "Transfer")
.withArgs(owner.address, tokenVesting.address, 1000)
const currentBlock = await hh.ethers.provider.getBlock(await hh.ethers.provider.getBlockNumber());
let now: number = currentBlock.timestamp;
let vestingId = "vesting id";
let initiallyReleasablePercentage = 0;
let startTime: number = now + 1; // current time
let duration: number = 10; // 10 seconds
let cliffDuration: number = 4; // 4 seconds
let slicePeriodSeconds = 1; // 1 second
await tokenVesting.createVesting(
vestingId,
saleToken.address,
startTime,
duration,
cliffDuration,
initiallyReleasablePercentage,
slicePeriodSeconds
);
await saleToken.connect(owner).approve(tokenVesting.address, hh.ethers.utils.parseUnits("10", 18));
await tokenVesting.vest(vestingId, owner.address, hh.ethers.utils.parseUnits("10", 18));
/**
* vested amount should be 0 for the next 4 seconds, because cliff duration is 4 seconds
*/
expect(await tokenVesting.getReleasableAmount(tokenVesting.address, vestingId)).to.equal(0);
/**
* go ahead by 5 seconds -> current time is 5 seconds,
* should be able to vest 5 tokens because it reached cliff start time
*/
await hh.ethers.provider.send("evm_increaseTime", [5]);
await hh.ethers.provider.send("evm_mine", []);
expect(await tokenVesting.getReleasableAmount(owner.address, vestingId)).to.be.equal(5);
}
In this code first 10 lines are for deploying vesting contract and sending tokens to it. Then get current time using block.timestamp. Then I create fields that will be used to createVesting. duration is the total duration of a vesting, cliffDuration is cliff time, so time after which we can vest tokens and slicePeriodSeconds is the time that we have to wait after the previous release. Then under the tokenVesting.createVesting I send tokens to the user that will be tested and the amount is 10 and then testing starts.
Here is the problem
First test works fine because the releasable amount is 0. But then when I jump to the future by 5 seconds and test it, the test cracks. It shows that the releasable amount is 7 tokens but it should be 5 because we jumped to the middle of the token vesting period and total amount of tokens is 10.
My thoughts
It got me thinking that in these tests, time does not stop. It is going further. So when I jump by 5 second it logs time before and after creating vesting but when i m trying to getReleasableAmount the time passed and in this function gives me wrong amount of tokens. I don not really know if this is correct it is just my assumption.
Is it possible to somehow stop the time in hardhat testing after evm_increaseTime? When I tested the code but evm_increase_time was in months everything worked and releasable amount was as expected. But trying to test it in seconds when total vesting time is 10 seconds and cliff duration is 4 seconds it is not working as expected.
Has anybody had similar problem or know the solution to that (e.g. stopping the time in hardhat or something else)?

Related

Postman running the same request multiple times

I want to run the same request multi times with different pre-request scripts? Any idea how can I do it without using the Data Driven (CSV) test?
Eg., I have to run the below GET url multiple times (every 2 minutes) but whenever I run this, I need to have a different pre request tests!
{{url}}/legacy/COL
enter image description here
Onetime operation:
If you want to send request 10 times (including first request 11) , then create two environment variables that contains the count. you can create the variables by simply copy pasting the below two lines in pre-request or test script ( remove all other code).
pm.environment.set("repeat",10);
pm.environment.set("repeat",10);
Once the variables are added remove the above lines from script.
Now in test script:
we can sendrequest multiple time by using pm.sendrequest or pm.setNextrequest. Here the example shows calling same request 10 more times using pm.setNextRequest.
The delay of 2mins or 3 mins can be set using the setTimeout javascript function which waits the mentioned time (here 3 seconds ) before executing the code inside that. so the setNextrequest will executed only after 3 sec in this case you can change it to 2 mins.
let repeatTemp = pm.environment.get("repeatTemp");
if (repeatTemp === 0) {
pm.environment.set("repeatTemp", pm.environment.get("repeat"));
} else {
let repeatTemp = pm.environment.get("repeatTemp")
let increment = pm.environment.get("increment")===0?15:pm.environment.get("increment")+5
pm.environment.set("increment",increment)
pm.environment.set("repeatTemp", repeatTemp-1);
setTimeout(function () { postman.setNextRequest("something") }, 3000);
}
so if your request name is "yourrequestname" then it will send this request 1+10 times
Pre-request script:
in your format you mentioned yyyy-mm which is wrong mm stands for minutes not month for year-month you have to give capital YYYY-MM
let repeatTemp = pm.environment.get("repeatTemp");
let repeat = pm.environment.get("repeat");
if (repeatTemp===repeat) {
pm.environment.set("increment", 0)
}
let moment = require('moment')
pm.environment.set('estimatedTimeArrival', moment().add(30 + pm.environment.get("increment"), 'minutes').format("YYYY-MM-DDThh:mm:ss"));
pm.environment.set('estimatedTimeDeparture', moment().add(2, 'hours').format("YYYY-MM-DDThh:mm:ss"));
pm.environment.set('scheduledTimeArrival', moment().add(10, 'minutes').format("YYYY-MM-DDThh:mm:ss"));
console.log(pm.environment.get('increment'))
console.log(pm.environment.get('estimatedTimeArrival'))
output:

Moment Formatting in minutes for over 60 minutes

I am working on an app that tracks a users activity over time. When the user completes the activity I'd like to show the total time in minutes. Currently when the user passes 60 mins it resets the minute counter.
If a user is active for 1hr:2min I'd like it to show as 62 minutes. Is this possible? Currently it rolls over to 2 mins. This is what I have and it works great for under 60 minutes. I appreciate the feedback.
export const formatWalkTimeInMinutes = (walkSeconds) => {
return moment().hour(0).minute(0).second(walkSeconds).format('m');
};
'''
You can use
return moment.duration(walkSeconds, "seconds").asMinutes()

How to trigger a new state in a timed state machine (solidity)

I am creating a perpetual trivia dapp (for learning purposes) that has 3 stages. Each stage should last approximately 30 secs. Example:
enum Stages {
AcceptingEntryFees,
RevealQuestion,
Complete
}
modifier transitionToReveal(uint _playerCount) {
_;
if (stage == Stages.AcceptingEntryFees && now >= creationTime + 30 seconds && _playerCount > 0) {
nextStage();
}
}
modifier transitionToComplete() {
_;
if (stage == Stages.RevealQuestion && now >= creationTime + 60 seconds) {
nextStage();
}
}
modifier transitionToAcceptingFees() {
_;
if (stage == Stages.Complete && now >= creationTime + 90 seconds) {
nextStage();
}
}
function nextStage() internal {
stage = Stages(uint(stage) + 1);
}
Im struggling with a solution on how to make the stage increment once the time requirement has been met. I don't need exactly 30 seconds by any means.
Take the first transition (accepting fees).
function payEntryFee() external payable transitionToReveal(getPlayerCount()) atStage(Stages.AcceptingEntryFees) {
....
}
I currently have it set up where people can pay to play up until the 30 seconds is up. However for the stage to increment a tx has to take place. So for this setup the first person to join after the 30 seconds is up will incur the gas price and trigger the next stage. This is not ideal because what if another player doesn't show for a while.
From my research there is no way to trigger a method internally by time and trying to trigger it from the front end would require gas and then who pays it?
Can anyone think of an elegant solution to this? I would like the stage to increment every ~ 30 seconds without interruption to the game.
You would either need an external entity, like a game master web application which changes the state using its own timer and sending transactions but that would cost you gas for every single transaction.
Or you could keep track of the state on the front end (kind of like syncing) and then whenever the player interacts with the ethereum dapp to make a function call, you could do a fetchState() to get the new state and then route the player to the correct game state.
For example, after the web app frontend gets confirmation that the user has paid, it personally keeps track of the state and presents the user with the UI options related to predicted state of the dapp, then when the user sends something like "submitTriviaAnswer" the dapp would update its state and verify that the user can submit a trivia answer.
function SubmitTriviaAnswer(int responseID) public {
fetchState()
...
}

How to easily test redux-observable epic that emits an action after 5 minutes?

I have an epic that emits SOME_OTHER_ACTION when 5 minutes have passed after SOME_ACTION (using delay operator).
I want to use jest or sinon useFakeTimers method to be able to do the following: dispatch an action, wait 5 minutes and test if another action was dispatched.
I really don't want to use marble diagrams, or inject TestSchedulers.
I also don't want to wait 5 minutes of real time.
const timeBefore = new Date().getTime() / 1000;
store.dispatch(SOME_ACTION);
// wait 5 minutes
jest.runTimersToTime(5 * 60 * 1000);
expect(store.getActions()).toEqual([ SOME_ACTION, SOME_OTHER_ACTION ]);
expect(store.getActions()[1].time).toEqual(timeBefore + 5 * 60 * 1000);
So what I ended up doing is: replaced RxJS with xstream, and used adapter for redux-observable, modified my epics accordingly.
It turns out that Jest doesn't handle Date correctly when using fakeTimers, so I used lolex for fake timers.
See my gist for full example.
(I didn't find easy way to test or replace internal RxJS delay, so I had to use different reactive library).
clock.setTimeout(() => {
expect(store.getActions()).toEqual([trainIncomingAction, reminderAction]);
done();
}, 60 * 60 * 1001);
clock.runToLast();

API throttle RateLimit-Remaining never updates every minutes

I have an API created with Laravel 5.2. I am using throttle for rate limiting. In my route file, I have set the following..
Route::group(['middleware' => ['throttle:60,1'], 'prefix' => 'api/v1'], function() {
//
}
As I understood the Laravel throttling, the above script will set the request limit to 60 per minute. I have an application querying the route which repeat every 10 seconds. So, per minute there are 6 request which is much more satisfying the above throttle.
Problem is, my query works until I execute 60 request regardless of time and after 60 request, it responds me 429 Too Many Requests with header Retry-After: 60. As per my understanding, the X-RateLimit-Remaining should update every 1 minute. But it seems never updated until it goes to 0. After become zero, then waits for 60 seconds then updated.
Am I doing anything wrong here.