-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
57a81f5
commit 268b453
Showing
42 changed files
with
2,071 additions
and
5 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
docs/learn/protocols/iota2.0/core-concepts/mana-calculator.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import ManaCalculator from '@site/src/components/ManaCalculator'; | ||
|
||
# Mana Calculator | ||
|
||
Mana is a reward resource generated by holding IOTA tokens. It is utilized for block issuance, access to network throughput, protection against Sybil attacks, and various other services. Our incentives scheme allows you to stake or delegate IOTA tokens to receive Mana rewards, in addition to the Mana generated simply by holding your IOTA tokens, without having to participate in any consensus-related activity. | ||
|
||
Here, we introduce the Mana calculator: a tool to help you make decisions about the type of participation that will suit you best, to help you predict the Mana generation you’ll be granted, and to decide the number of IOTA tokens you should hold to guarantee your desired level of access in the future. | ||
|
||
## How to use the Calculator: Step-by-step instructions | ||
|
||
- **Choose your network:** This calculator works for both Shimmer and the future IOTA 2.0 network. Both networks will have their own type of Mana (IOTA Mana and Shimmer Mana), with their own parameters and, consequently, their own behavior. Be sure you select the correct scenario you want to simulate! | ||
|
||
- **Choose your role:** Will you delegate to a validator, become a validator yourself, or just hold tokens? To be a validator, you must run a node and lock your tokens while performing validation services, which will give you higher rewards. If you want to delegate to a validator, your tokens will not be locked and you won’t need to run a node, but this will give you fewer rewards than validating. Finally, just holding tokens grants you a certain amount of Mana, which will be less than by validating or delegating. | ||
|
||
- **Input the number of tokens you own:** No matter which network and role you choose, your Mana generation will depend on how many available tokens you have. Pay attention to the units! The input should be done in IOTA or Shimmer (not microIota or Glow). | ||
|
||
With the inputs above, you can already estimate how much Mana you’ll be granted per epoch. But what does it mean in TPS? How many blocks per second does your Mana grant you? The answer depends on the size of your block and the network congestion levels. However, we can use a default block size, which will be enough for most applications, and assume certain levels of congestion. This takes us to the last step in this calculator: | ||
|
||
- **Choose a congestion level:** Given a certain Mana generation per epoch, this can be translated into a number of blocks issued per second that depends on the congestion level. Choose one of the given congestion levels (low, stable, or extreme) to estimate how many blocks per second your tokens and participation grant you! This metric is also shown in another way: time until block issuance, which tells you in which periodicity you’ll be able to issue a block. | ||
|
||
<ManaCalculator/> | ||
|
||
|
||
## Advanced settings | ||
|
||
The steps outlined above can give you a rough estimate of your Mana generation and granted TPS. However, playing with the advanced settings will give you more precise results. | ||
|
||
- **Change the state of the system:** The calculator's default settings assume a certain level of participation in the consensus (i.e., locked stake, delegated stake, and performance factor of validators). Those settings can be changed under the “Advanced Settings - State of the System” tab. You can also add or delete validators and change their fixed costs. | ||
|
||
- If you choose to delegate: | ||
- **Change the validator you delegate to:** Under the default settings, you'll delegate to Validator 1 (which is pool that, technically speaking, is under the "equilibrium state", see the WP for more details). However, you can change this setting and know your rewards if you participate in other pools, with different share of delegated to locked stake, and different performance factors. | ||
|
||
- If you choose to validate: | ||
- **Change the amount of stake delegated to you:** The calculator's default settings automatically assign you a certain share of the delegated stake when you start validating. However, you can change this setting to know your rewards should you manage to attract more (or less) delegated stake than the default setting. | ||
- **Change your performance factor:** Your performance factor under the calculator's default settings is 0.95. However, you can change this to simulate larger or smaller shares of online time. | ||
- **Change your fixed costs:** By the default settings of the calculator, your fixed costs are zero. This setting can be changed so you spare part of the pool rowards just for yourself. However, notice that the fixed cost is a public parameter, that you define during registration. This means that the delegator knows how you set this value and might consider it when choosing a validator to delegate. Furthermore, if you set this value too high, you'll be punished and won't get any rewards. | ||
|
||
- **Simulate your Mana accumulation**: the default setting of the calculator assumes a stationary regime and calculates the block creation rate you guarantee depending on your stake and level of participation. However, especially in the early stages of the network, you might want to save your Mana to sell later. This tab simulates how much Mana you'll accumulate in a certain time period. For that (besides the other already defined inputs), you just need to choose an initial and final epoch, and the tool will plot a graph of your accumulation during that period. In the same graph, you can also have an idea of how many blocks can be issued with this accumulated amount of Mana, for the different congestion levels (already defined in the non-advanced settings). | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { EPOCH_DURATION } from '../constants'; | ||
|
||
export function calculateBPS(mana: number, congestion: number): number { | ||
return mana / congestion / EPOCH_DURATION; | ||
} |
145 changes: 145 additions & 0 deletions
145
src/components/ManaCalculator/actions/calculateManaRewards.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { getNetworkSupply } from '../utils'; | ||
import type { ValidatorParameters, ValidatorProps } from '../types'; | ||
import { NetworkType, UserType } from '../enums'; | ||
import { decay } from './decay'; | ||
import { targetReward } from './targetReward'; | ||
|
||
export function calculateManaRewards( | ||
stake: number, | ||
yourPool: number, | ||
validatorParameters: ValidatorParameters, | ||
validators: ValidatorProps[], | ||
initialEpoch: number, | ||
finalEpoch: number, | ||
userType: UserType, | ||
networkType: NetworkType, | ||
generationPerSlot: number, | ||
): number { | ||
const supply = getNetworkSupply(networkType); | ||
let totalTargetReward = 0; | ||
let epochDiff = finalEpoch - initialEpoch; | ||
|
||
if (finalEpoch) { | ||
for (let i = 0; i < epochDiff; i++) { | ||
totalTargetReward += decay( | ||
targetReward(initialEpoch + i, supply, generationPerSlot), | ||
epochDiff - i, | ||
); | ||
} | ||
} else { | ||
epochDiff = 1; | ||
finalEpoch = initialEpoch + 1; | ||
totalTargetReward = targetReward(initialEpoch, supply, generationPerSlot); | ||
} | ||
|
||
const lockedStake: number[] = validators.map( | ||
(validator) => validator.lockedStake, | ||
); | ||
const fixedCosts: number[] = validators.map( | ||
(validator) => validator.fixedCost, | ||
); | ||
const performance: number[] = validators.map( | ||
(validator) => validator.performanceFactor, | ||
); | ||
const delegatedStake: number[] = validators.map( | ||
(validator) => validator.delegatedStake, | ||
); | ||
|
||
if (userType == UserType.VALIDATOR) { | ||
lockedStake.push(stake * validatorParameters.shareOfYourStakeLocked); | ||
fixedCosts.push(validatorParameters.fixedCost); | ||
performance.push(validatorParameters.performanceFactor); | ||
delegatedStake.push( | ||
(1 - validatorParameters.shareOfYourStakeLocked) * stake + | ||
validatorParameters.attractedNewDelegatedStake + | ||
validatorParameters.attractedDelegatedStakeFromOtherPools * | ||
delegatedStake.reduce((a, b) => a + b, 0), | ||
); | ||
for (let i = 0; i < validators.length; i++) { | ||
delegatedStake[i] *= | ||
1 - validatorParameters.attractedDelegatedStakeFromOtherPools; | ||
} | ||
} | ||
|
||
if (userType == UserType.DELEGATOR) { | ||
delegatedStake[yourPool] += stake; | ||
} | ||
|
||
const totalValidatorsStake = lockedStake.reduce((a, b) => a + b, 0); | ||
const totalDelegatedStake = delegatedStake.reduce((a, b) => a + b, 0); | ||
const totalStake = totalDelegatedStake + totalValidatorsStake; | ||
const restOfTokenHoldings = supply - totalStake; | ||
if (restOfTokenHoldings < 0) { | ||
throw new Error('Pools must have (collectively) at most iotaSupply tokens'); | ||
} | ||
|
||
// Calculates profit margin of the epoch (only when there are tokens stake, otherwise, it's set to None) | ||
let profitMargin: number | null; | ||
if (totalStake > 0) { | ||
profitMargin = totalValidatorsStake / (totalValidatorsStake + totalStake); | ||
} else { | ||
profitMargin = null; | ||
} | ||
|
||
// Calculates the total rewards for each pool, already discounting the validator fixed cost | ||
const poolRewards: number[] = new Array(lockedStake.length).fill(0); | ||
if (totalStake > 0) { | ||
if (totalValidatorsStake > 0) { | ||
for (let i = 0; i < lockedStake.length; i++) { | ||
poolRewards[i] = | ||
((lockedStake[i] + delegatedStake[i]) / totalStake + | ||
lockedStake[i] / totalValidatorsStake) * | ||
totalTargetReward * | ||
(performance[i] / 2.0) - | ||
epochDiff * fixedCosts[i]; | ||
} | ||
} else { | ||
for (let i = 0; i < lockedStake.length; i++) { | ||
poolRewards[i] = | ||
((lockedStake[i] + delegatedStake[i]) / totalStake) * | ||
totalTargetReward * | ||
(performance[i] / 2.0) - | ||
epochDiff * fixedCosts[i]; | ||
} | ||
} | ||
} | ||
|
||
// Calculates the rewards for each validator | ||
const validatorRewards: number[] = new Array(lockedStake.length).fill(0); | ||
for (let i = 0; i < lockedStake.length; i++) { | ||
if (poolRewards[i] < 0) { | ||
validatorRewards[i] = 0; | ||
poolRewards[i] = 0; | ||
} else if (poolRewards[i] === 0) { | ||
validatorRewards[i] = epochDiff * fixedCosts[i]; | ||
} else { | ||
validatorRewards[i] = | ||
epochDiff * fixedCosts[i] + | ||
poolRewards[i] * profitMargin + | ||
((1 - profitMargin) * poolRewards[i] * lockedStake[i]) / | ||
(delegatedStake[i] + lockedStake[i]); | ||
} | ||
} | ||
|
||
const delegatorRewards: number[] = new Array(lockedStake.length).fill(0); | ||
for (let i = 0; i < lockedStake.length; i++) { | ||
if (poolRewards[i] > 0) { | ||
delegatorRewards[i] = | ||
(poolRewards[i] * (1 - profitMargin) * delegatedStake[i]) / | ||
(delegatedStake[i] + lockedStake[i]); | ||
} | ||
} | ||
|
||
let rewards = 0; | ||
if (userType == UserType.DELEGATOR) { | ||
if (delegatorRewards[yourPool] > 0) { | ||
rewards = (delegatorRewards[yourPool] * stake) / delegatedStake[yourPool]; | ||
} | ||
} | ||
|
||
if (userType == UserType.VALIDATOR) { | ||
rewards = validatorRewards[lockedStake.length - 1]; | ||
} | ||
|
||
return rewards; | ||
} |
16 changes: 16 additions & 0 deletions
16
src/components/ManaCalculator/actions/calculatePassiveRewards.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { getFirstSlotOfEpoch } from '../utils'; | ||
import { getPotentialMana } from './getPotentialMana'; | ||
|
||
export function calculatePassiveRewards( | ||
tokens: number, | ||
initialEpoch: number, | ||
finalEpoch: number, | ||
generationPerSlot: number, | ||
): number { | ||
return getPotentialMana( | ||
tokens, | ||
getFirstSlotOfEpoch(initialEpoch) - 1, | ||
getFirstSlotOfEpoch(finalEpoch) - 1, | ||
generationPerSlot, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { BETA_PER_YEAR, EPOCH_DURATION_IN_YEARS } from '../constants'; | ||
|
||
// Returns the decayed value of value by the amount of epochs | ||
export function decay(value: number, epochsAmount: number): number { | ||
if (value != 0 && epochsAmount != 0) { | ||
const decay = Math.exp( | ||
-BETA_PER_YEAR * EPOCH_DURATION_IN_YEARS * epochsAmount, | ||
); | ||
value = Math.floor(value * decay); | ||
} | ||
return value; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { | ||
BETA_PER_YEAR, | ||
EPOCH_DURATION_IN_YEARS, | ||
SLOTS_IN_EPOCH, | ||
} from '../constants'; | ||
import { getFirstSlotOfEpoch, slotToEpoch } from '../utils'; | ||
import { decay } from './decay'; | ||
|
||
// Returns the potential mana generated by holding value tokens from creationSlot to consumptionSlot | ||
export function getPotentialMana( | ||
value: number, | ||
creationSlot: number, | ||
consumptionSlot: number, | ||
generationRate: number, | ||
): number { | ||
const creationEpoch = slotToEpoch(creationSlot); | ||
const consumptionEpoch = slotToEpoch(consumptionSlot); | ||
|
||
const epochsLeftToConsumption = consumptionEpoch - creationEpoch; | ||
|
||
const slotAfterCreationEpoch = getFirstSlotOfEpoch(creationEpoch + 1); | ||
const firstSlotConsumptionEpoch = getFirstSlotOfEpoch(consumptionEpoch); | ||
|
||
const d1 = slotAfterCreationEpoch - creationSlot; | ||
const d2 = consumptionSlot - firstSlotConsumptionEpoch; | ||
|
||
let potentialMana = 0; | ||
if (epochsLeftToConsumption == 0) { | ||
potentialMana = value * (consumptionSlot - creationSlot) * generationRate; | ||
} else if (epochsLeftToConsumption == 1) { | ||
potentialMana = | ||
value * d2 * generationRate + decay(value * d1 * generationRate, 1); | ||
} else { | ||
const c = | ||
Math.exp(-BETA_PER_YEAR * EPOCH_DURATION_IN_YEARS) / | ||
(1 - Math.exp(-BETA_PER_YEAR * EPOCH_DURATION_IN_YEARS)); | ||
const aux = value * generationRate * c * SLOTS_IN_EPOCH; | ||
const potentialMana_n = decay( | ||
value * d1 * generationRate, | ||
epochsLeftToConsumption, | ||
); | ||
const potentialMana_n_1 = decay(aux, epochsLeftToConsumption - 1); | ||
const potentialMana_0 = value * d2 * generationRate + aux; | ||
potentialMana = potentialMana_n - potentialMana_n_1 + potentialMana_0; | ||
} | ||
return potentialMana; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from './calculateManaRewards'; | ||
export * from './calculatePassiveRewards'; | ||
export * from './calculateBPS'; | ||
export * from './getPotentialMana'; | ||
export * from './decay'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { | ||
BETA_PER_YEAR, | ||
BOOTSTRAPPING_DURATION, | ||
EPOCH_DURATION_IN_YEARS, | ||
REWARDS_MANA_SHARE_COEFFICIENT, | ||
SLOTS_IN_EPOCH, | ||
} from '../constants'; | ||
import { decay } from './decay'; | ||
|
||
// Returns the target reward for the given epoch | ||
export function targetReward( | ||
epoch: number, | ||
supply: number, | ||
generationPerSlot: number, | ||
): number { | ||
const finalReward = | ||
supply * | ||
REWARDS_MANA_SHARE_COEFFICIENT * | ||
generationPerSlot * | ||
SLOTS_IN_EPOCH; | ||
const decayBalancingConstant = | ||
Math.exp(1) / | ||
(BOOTSTRAPPING_DURATION * | ||
(1 - Math.exp(-EPOCH_DURATION_IN_YEARS * BETA_PER_YEAR))); | ||
const initialReward = finalReward * decayBalancingConstant; | ||
|
||
let reward = 0; | ||
if (epoch <= BOOTSTRAPPING_DURATION) { | ||
reward = decay(initialReward, epoch); | ||
} else { | ||
reward = finalReward; | ||
} | ||
return reward; | ||
} |
54 changes: 54 additions & 0 deletions
54
src/components/ManaCalculator/components/AdvancedSettingsValidator.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Details } from '@docusaurus/theme-common/Details'; | ||
import React from 'react'; | ||
import { useManaState } from '../hooks'; | ||
import { ValidatorCard } from './ValidatorCard'; | ||
|
||
export function AdvancedSettingsValidator() { | ||
const { handleAddValidator, state } = useManaState(); | ||
|
||
function onAddValidator() { | ||
handleAddValidator({ | ||
lockedStake: 100, | ||
delegatedStake: 0, | ||
performanceFactor: 1.0, | ||
fixedCost: 0.0, | ||
}); | ||
} | ||
|
||
return ( | ||
<Details | ||
summary='Advanced Settings - Validators' | ||
className='mana_calculator__card mana_calculator_inner__card table' | ||
> | ||
<div className='table'> | ||
<div className='row small-row-head'> | ||
<div className='col col--2 text--center'>ID</div> | ||
<div className='col col--2 horizontal-spaced text--center'> | ||
Stake ({state.network}) | ||
</div> | ||
<div className='col col--2 horizontal-space text--center'> | ||
Delegated ({state.network}) | ||
</div> | ||
<div className='col col--2 horizontal-spaced text--center'> | ||
Performance factor | ||
</div> | ||
<div className='col col--2 horizontal-spaced text--center'> | ||
Fixed costs | ||
</div> | ||
<div className='col col--1'></div> | ||
</div> | ||
{state.validators.map((validator, i) => ( | ||
<div className='row row--centered' key={i}> | ||
<ValidatorCard validator={validator} id={i} /> | ||
</div> | ||
))} | ||
</div> | ||
<button | ||
className='button button--block mana-calculator__button ' | ||
onClick={onAddValidator} | ||
> | ||
New Validator | ||
</button> | ||
</Details> | ||
); | ||
} |
Oops, something went wrong.