The xx labs team is pushing a fix to vesting schedules January 21, 2022
With the enactment of the BetaNet Staking Rewards program, new vesting schedules were added to most wallets on the xx network. The xx labs team has identified an error that was made in calculating these new vesting schedules and has developed a fix which will restore the correct schedules.
In this post we are going to start with a deep dive into how vesting works in the xx network.
Then, we will explain how the BetaNet Staking Rewards program was implemented, the issues that were found after it was enacted, and how we are fixing those issues, which involve modifying incorrect vesting schedules.
Finally, we are publishing all the revised vesting schedules at https://vesting.xx.network for all coin holders, in JSON data format and an accompanying PNG file containing vesting graphs. We also provide examples of different scenarios that affected coin holders as a result of the aforementioned issues.
We will be pushing these corrections on January 21, 2022.
What are Vesting Schedules
Vesting is a concept traditionally used with stock options, where a grant of a number of stock units is awarded, but the units are only given (they vest) in portions over time, for example, quarterly or yearly.
In the context of xx network, the concept is similar, but the execution is different. The full amount of coins awarded is given directly to the recipient account, but with a vesting schedule that locks the coins for a period of time. Coins that are locked for vesting can be used for any operation in the network (except being used as reserved coins), but cannot be transferred to any other account. Coins under vesting are then unlocked every block.
A vesting schedule in the xx network consists of three fields:
- The amount that is locked by vesting: locked
- The amount that unlocks each block: per_block
- The block when the coins start being unlocked: starting_block
We use vesting schedules to place two different types of restrictions on coins: a lock of the full amount for a specified amount of time, or a linear unlocking, starting at a given point in time and with a specified duration as well.
Vesting schedules on coins given in monthly tranches of 7000 for BetaNet nodes look like this:
This example places a lock of 7000, which starts unlocking at block 1123200, and unlocks 7000 per block. This effectively means that this tranche of 7000 coins is locked until block 1123200, and it fully unlocks at block 1123201. This particular example is for BetaNet rewards for the month of January 2021, since 1123200 blocks is equal to 1123200/(24 hours * 600 blocks per hour) = 78 days, which means the lock ends on 1st of February 2022.
Vesting schedules on coins given for BetaNet staking rewards look like this:
This example places a lock of 23812, which starts unlocking at block 432000, and unlocks 0.009186728 per block. This means that the 23812 coins are locked until block 432000, and then start unlocking at 0.009186728 per block. We can calculate the duration of the vesting by dividing the locked by per_block: 23812/0.009186728 ~= 2592000 blocks, which is 2592000/(24*600) = 180 days. This means that the value is fully unlocked 6 months after it starts vesting, 432000/(24*600) = 30 days after mainnet launch.
Betanet Staking Rewards
In March 2021 the team announced the BetaNet Staking Rewards program, which applies to all coin holders as of mainnet launch. The program has an APY of 50%, with interest calculated daily, from 1st of March 2021 until mainnet launch. Coins received after 1st of March only accrue interest from the day they are awarded. The reward amount was calculated for every coin holder and posted in the genesis block spreadsheet before mainnet launch. In the genesis block, the reward amount was configured for each coin holder in the claims module, or directly in the BetaNet Staking Rewards module.
The program was designed with the following reward options:
- No vesting and 2% of reward is given
- 1 month vesting on 100% of principal plus reward, 12% of the reward is given
- 3 month vesting on 90% of principal plus reward, 45% of the reward is given
- 6 month vesting on 80% of principal plus reward, the total reward is given. This is the default option if no selection is made
- 9 month vesting on 100% of principal plus reward, 120% of the reward is given
The BetaNet Staking Rewards module was designed in order to support the required features of the program. This module allowed users that claimed their coins into an xx network address to select their desired option. Furthermore, the module was configured with an enactment block that was set to 30 days after mainnet launch. This was necessary to allow time for the acceptance of the program to be voted on by the community. The program was approved via a referendum, where coin holders voted during one week.
At the enactment block, the program was executed, with rewards being given from the mainnet staking rewards pool to all coin holders, and the vesting schedules applied, according to the selected option. The default option of 6 month vesting was automatically applied to all leftover claims, with the reward amount being added to the claim value, and vesting schedules added.
The team identified 3 issues that affected the enactment of the BetaNet Staking Rewards program:
- Leftover claims – the amount to be locked was computed after the reward was added to the principal, i.e. lock = 0.8 * (principal + reward) + reward, instead of lock = 0.8 * principal + reward. This results in a higher locked amount than expected in many of the leftover claims
- Vesting computation – the amount to be locked was computed by taking the required lock and subtracting the current amount already locked at the enactment block. This was a best effort solution which locks the needed amount from rewards while maintaining the existing locks, resulting in vesting locks that are always higher than before. However, in many scenarios this solution can lead to locked values lower than what is required by the program, which can create unfair situations amongst coin holders that selected different options. Moreover, in some cases, the value locked starts off higher than needed, and then drops to lower than allowed
- Incorrect reward payment – consider a user that has two separate claims, one without vesting schedules (for example from the original sale) and another with. If both are claimed into the same address, only the reward value from the second one is kept. This means that the reward value from the first claim was not paid out at the enactment block. This particular issue only affects 3 coin holders.
Fixing the Issues
As previously announced in a forum post, the team proposed a blockchain runtime update that added extra functionality. This included two functions that grant the ability to the technical committee, by unanimous vote, to modify vesting schedules in the Claims and Vesting modules. This upgrade was approved in a referendum, and was enacted last Thursday, 13th of January.
These privileged functions are going to be used by the team to fix the incorrect vesting schedules, and then will be removed as soon as possible in a further runtime upgrade.
New Vesting Schedules
Vesting and BetaNet Staking Rewards program information for all coin holders can be found at https://vesting.xx.network.
For each coin holder there is a JSON file containing the following information:
- principal: the amount of coins present at genesis
- reward: the reward given, adjusted according to chosen option
- option: the BetaNet Staking Rewards program selected option
- vesting: all the information regarding vesting
- original: vesting schedules present at genesis
- betanet_rewards: vesting schedule required by the betanet staking rewards
- on_chain: vesting schedules currently on chain
- computed: fixed vesting schedules
- incorrect_on_chain: flag to specify in the on-chain vesting is incorrect
For each coin holder there is a PNG file containing three graphs of the amount of coins locked as a function of the block number. These provide an easier way to compare the amount locked by different vesting schedules over time:
- Original vesting (left): this graph shows the original vesting schedules (blue) with the one required by the BetaNet Staking Rewards (orange). After the enactment of the program, the amount locked must always be the most restrictive one, i.e. max(original, betanet_rewards)
- On-Chain vesting (middle): this graph shows the same information as the previous one, but with the on-chain vesting schedules overlaid (green). Here, coin holders can see right away if the current vesting is invalid, and if it’s locking more, less (or both) coins
- New vesting (right): this graph shows the same information as the first one, but with the new (fixed) vesting schedules overlaid (green). Here, coin holders can see the vesting schedules that will be set by the team, in order to fix the issues found, which, as can be seen, always follows the max between original and betanet rewards vesting. If this graph is the same as the middle one, that means the on-chain vesting is already correct.
The filename for each JSON and PNG file is the xx network address of the coin holder, or the ETH address if the coins were not claimed as of block 870000. We encourage all community members to find their respective information and contact the team if they find any issues.
All the necessary information was compiled directly from the genesis block and on-chain state at various blocks. When generating all the files, multiple checks were in place in order to detect any issues, and correct them. The computation of the fixed vesting schedules was performed recursively by adjusting the original schedules and adding portions of the BetaNet rewards schedules as needed, so that the resulting total matches the necessary lock at all blocks, which is exactly the maximum between both types of schedules.
Examples of different situations
Leftover claim without original locks, showing issue 1
Leftover claim with original locks, showing both issues 1 and 2
Leftover claim with original locks, but correct vesting
Holder with original locks, showing issue 2 causing higher locks than expected
Holder with original locks, showing issue 2 causing initial higher locks, and then lower than expected
Holder with original locks, but correct vesting
Holder without original locks, but correct vesting
Holder with original locks, showing bug 1, due to claiming after enactment