arda-org / xSuite

🛠️ Init, Build, Test, Deploy MultiversX smart contracts in seconds. The full suite for efficiently developing high-quality contracts.
https://xsuite.dev
MIT License
13 stars 3 forks source link

Get easy access to currentBlockInfo with light simulnet #201

Closed nvergez closed 3 weeks ago

nvergez commented 1 month ago

If we want to increment the block epoch or nonce, we need to keep track of the info in a variable:

const world = await LSWorld.start();

let currentEpoch = 1;
let currentNonce = 3;
await world.setCurrentBlockInfo({
  nonce: currentNonce,
  epoch: currentEpoch,
});

// do some actions

currentNonce += 10;
currentEpoch += 3;
await world.setCurrentBlockInfo({
  nonce: currentNonce,
  epoch: currentEpoch,
});
for (let i = 0; i < 10; i++) {
  // conditionnally do actions based on the epoch for example

  currentNonce += 5;
  currentEpoch += 1;
  await world.setCurrentBlockInfo({
    nonce: currentNonce,
    epoch: currentEpoch,
  });
}

To make it easier to use for developers, we have 2 options:

Here is an example of the second option:

const world = await LSWorld.start();

await world.setCurrentBlockInfo({
  nonce: 3,
  epoch: 1,
});

// do some actions

await world.setCurrentBlockInfo((blockInfo) => ({
  ...blockInfo,
  nonce: blockInfo.nonce + 10,
  epoch: blockInfo.epoch + 3,
}));
for (let i = 0; i < 10; i++) {
  // conditionnally do actions based on the epoch for example

  await world.setCurrentBlockInfo((blockInfo) => ({
    ...blockInfo,
    nonce: blockInfo.nonce + 5,
    epoch: blockInfo.epoch + 1,
  }));
}
lcswillems commented 1 month ago

Do you have concrete use cases where you need to increase the blockNonce and the epoch?

If you have to choose between advanceEpoch / generateBlocks and setCurrentBlockInfo, what do you prefer?

Note: advanceToEpoch is similar to setting the epoch, not increasing.

nvergez commented 1 month ago

When I have to deal with a staking that gives rewards based on blockNonce but also handle a rewardBonus based on epoch.

I'd go with the setCurrentBlockInfo bc we do the minimal changes possible to the API, and the developper can fully customize the increment of the epoch, nonce, timestamp, and round.

Example:

const advanceOneDay = async () => {
  await world.setCurrentBlockInfo((blockInfo) => ({
  timestamp: blockInfo.timestamp + 86_400,
  nonce: blockInfo.nonce + 14_400,
  round: blockInfo.round + 14_400,
  epoch: blockInfo.epoch + 1,
}));
lcswillems commented 1 month ago

When I have to deal with a staking that gives rewards based on blockNonce but also handle a rewardBonus based on epoch.

This is unclear to me. Could you provide a sample code?

we do the minimal changes possible to the API

This is not an issue to do modifications, and these modifications will just be additions, no modification.

the developper can fully customize the increment of the epoch, nonce, timestamp, and round.

Yes but is there a need for this? That's why I'm asking for concrete and precise use cases. It is not because something is doable that we necessarily need to do it.

nvergez commented 1 month ago

A simple usecase that currently, forces me to use an extra variable to keep track of the timestamp:

test("Stake multiple NFTs and go through the unbonding process", async () => {
  let currentTimestamp = 10;
  world.setCurrentBlockInfo({
    timestamp: currentTimestamp,
  });

  // stake some NFTs
  // Asserts

  currentTimestamp += 5;
  world.setCurrentBlockInfo({
    timestamp: currentTimestamp,
  });

  // unstake an NFT
  // Asserts

  currentTimestamp += unbondingPeriod;
  world.setCurrentBlockInfo({
    timestamp: currentTimestamp,
  });

  // withdraw the NFT after the unbonding period
  // Asserts
});

And could be:

test("Stake multiple NFTs and go through the unbonding process", async () => {
  world.setCurrentBlockInfo({
    timestamp: 10,
  });

  // stake some NFTs
  // Asserts

  world.setCurrentBlockInfo((blockInfo) => {
    ...blockInfo,
    timestamp: blockInfo.timestamp + 5,
  });

  // unstake an NFT
  // Asserts

  world.setCurrentBlockInfo((blockInfo) => {
    ...blockInfo,
    timestamp: blockInfo.timestamp + unbondingPeriod,
  });

  // withdraw the NFT after the unbonding period
  // Asserts
});
lcswillems commented 1 month ago

@nvergez Thanks for sharing this! In your case, wouldn't it be simpler to have an advanceTimestamp method? Like:

world.advanceTimestamp(unbondingPeriod)

?

Or is there a case where you need to advance all of them at the same time?

nvergez commented 1 month ago

If my unbonding period is based on epochs, then I would need a world.advanceEpoch(unbondingPeriod). But we could easily create the helpers for each "attribute", so thats not a problem.

I have a usecase where I need to advance 2 or 3 of them, but I don't know if its needed to have my design instead of the one you gave.

I could easily do:

world.advanceTimestamp(x);
world.advanceNonce(y);
world.advanceEpoch(z);

So I think the question is: Do we have a usecase where we need to do something different than just an addition? And I don't have one

nvergez commented 1 month ago

Design chosen:

We add the following methods to the LSWorld class:

advanceTimestamp(amount: number);
advanceNonce(amount: number);
advanceEpoch(amount: number);
advanceRound(amount: number);