As now all epochEnd() are multiples of EPOCH_SIZE, we can drop the startTime from the struct Epoch, and avoid storing an information that can be cheaply calculated on the fly.
- Refactored the StakeManager contract to optimize the finalization of epochs and execution of accounts and epochs.
- Replaced the Epoch.startTime with a new global variable startTime to keep track of the start time of the contract.
- Modified the finalizeEpoch function to use a temporary variable for the current epoch and calculate the epoch reward only for the current epoch.
- Updated the migrationInitialize function to set the start time using the new startTime variable.
- Modified the epochEnd function to calculate the end time of the current epoch using the startTime variable.
- Modified the newEpoch function to calculate the number of the next epoch based on the startTime variable.
refactor(StakeManager): set startTime as immutable, load startTime from previousManager at constructor, verify startTime is correct on initializeMigration
This commit refactors the StakeManager contract to optimize the finalization and execution of epochs. It updates the function finalizeEpoch() to new function finalizeEpoch(uint256 _limitEpoch) that releases rewards for the current epoch and increases the epoch up to the specified limit. This allows for more efficient processing of epochs.
The changes also include updates to the executeEpoch() and executeAccount() functions to utilize the new finalizeEpoch() function. Additionally, a new function newEpoch() is introduced to calculate the last epoch that can be processed based on the current time. Now the executeAccount(account,limit) will also process epochs up to the specified limit.
Added a overload of the executeAccount to process the account up to the newEpoch()
Added a overload of the executeEpoch to allow the process of epochs up to a certain limit.
All methods now that finalizeEpoch will process the epochs up to the newEpoch().
These changes improve the overall performance, user expirience and reliability of the StakeManager contract.
chore(StakeManagerStartMigration.spec): add new function executeEpoch(uint256) to blockedWhenMigrating
fix(StakeManager.sol): Replace the check for pending migration in migrationInitialize with noPendingMigration modifier to avoid code duplication
This commit introduces the internal accounting logic for accrueing
multiplier points, that will later be used to determine how many
experience points an account is eligible to.
The majority of the work here was done by @3esmit.
After discussing this offline, we've decided that the naming of these
properties was misleading. This commit performs the following changes:
- `account.initialMP` becomes `account.bonusMP`
- `account.currentMP` becomes `account.totalMP`
Rationale:
`initialMP` indicates that this is an immutable field which is not the
case as in scenarios where accounts increase the `lock()` time, they'll
also increase their bonus multiplier (`initialMP`).
`currentMP` was misleading in combination with `initialMP`. Really what
it reflects is the total multiplier points of an account **including**
its bonus multiplier points.
A previous manager can only migrate once, because the migration address
is locked in. A **new** manager is always aware of its previous manager.
This means, when a migration happens and is initialized, we know for
sure it's always the first time this is happening.
We probably don't want a migration to take place if the new manager has
already processed epochs, so we're adding a check that its
`currentEpoch` must be `0`.
This also ensures one of its invariants isn't violated:
`epochsOnlyIncrease` and `highEpochsAreNull`.
This is actually a bug that the certora prover found.
The rule `epochStaysSameOnMigration` failed because a previous
`StakeManager` could call `migrationInitialize` and change
`currentEpoch` on a next `StakeManager`, even though the next `StakeManager`
might be in migration itself (which means the `currentEpoch` is now
allowed to change).
This commit fixes this by ensure `migrationInitialize()` will revert if
the `StakeManager` already has a `migration` on going.
This adds a test that ensures multiplier points are minted with a 1:1
ratio to the stake token amount.
This scenario covers the case where no lock up time is set during
staking.
Unstaking didn't actually work because it was using `transferFrom()` on the
`StakeVault` with the `from` address being the vault itself.
This would result in an approval error because the vault isn't creating
any approvals to spend its own funds.
The solution is to use `transfer` instead and ensuring the return value
is checked.
This commit introduces `MIN_LOCKUP_PERIOD` and `MAX_LOCKUP_PERIOD` and
makes use of them within `StakeManager.stake()` and
`StakeManager.lock()` accordingly.
When users deposit tokens into their vault via `stake()`, they can
provide an optional lockup time. If the value is `0` it implies users do
not want to lock their stake.
If the value is `> 0` it has to be within the range of
`MIN_LOCKUP_PERIOD` and `MAX_LOCKUP_PERIOD`.
Properly addresses #15
This commit introduces a first version of a `VaultFactory` that later
will be extended to be capable of instantiating reward vaults and
possible keep track of vault instances per owner.
As a first step, this implementation comes with a `createVault()`
function which takes care of creating vaults.
Because `VaultFactory` also knows about `StakeManager` it can derive the
manager's address and stake token from it when creating vaults, allowing
the API to be without arguments.
Partially addresses #37
Because the `stakedToken` property is `immutable`, solhint recommends to
make it in all caps. This commit changes the property to adhere to that
rule and also makes the property private.
To access the `stakedToken` there's now a `stakedToken()` function on
the contract.
This commit migrates the repo to our foundry template, which ensures we
have consistent tooling across smart contract repositories that are
maintained by Vac.
This removes all hardhat related files and workflows and replaces them
with more perfomant foundry workflows.
It also sets up tests, CI and linting.
- Now, MPs are minted on the newly staked amount only (previously was entire stake).
- The bonus MPs are calculated correctly such that there is a 1x bonus per year of lock.
- Still TBD: MPs are not minted for existing stake if current lock extended (or newly introduced), there is no check for max boost MP ceiling