Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing tokenomics changes to reflect on new top-ups approach #179

Open
wants to merge 90 commits into
base: aip1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
abe2a98
chore: update tokenomics implementation address
kupermind Jul 4, 2024
573d6e9
Merge pull request #161 from valory-xyz/addressing_issue_26
kupermind Jul 4, 2024
aa49aaf
Merge pull request #162 from valory-xyz/update_tokenomics
DavidMinarsch Jul 4, 2024
18f8245
doc: internal audit after fixing C4A
Jul 5, 2024
dba07d1
refactor: minimal changes according to internal audit findings
kupermind Jul 5, 2024
45089c5
fix: correcting removed nominees value adjustments after not issuing …
kupermind Jul 6, 2024
7eedd6c
doc: adding important comment for nominee removal
kupermind Jul 6, 2024
8805e41
refactor: addressing issue L-08
kupermind Jul 12, 2024
fa2c187
doc: adding deployment procedure for Tokenomics 1.2.0
kupermind Jul 12, 2024
c0064d1
Merge pull request #169 from valory-xyz/bridge_testing
kupermind Jul 12, 2024
af692db
Merge pull request #168 from valory-xyz/v1.2.2-internal-audit
kupermind Jul 12, 2024
9f29a7b
Merge pull request #166 from valory-xyz/addressing_issues_56_27
kupermind Jul 12, 2024
3108ab6
Merge pull request #165 from valory-xyz/addressing_issue_37
kupermind Jul 12, 2024
28f904b
Merge pull request #164 from valory-xyz/addressing_issue_61
kupermind Jul 12, 2024
8eb3685
Merge pull request #163 from valory-xyz/addressing_issue_67
kupermind Jul 12, 2024
f3fad06
Merge pull request #160 from valory-xyz/addressing_issues_32_5
kupermind Jul 12, 2024
61fb1a7
Merge pull request #159 from valory-xyz/addressing_issues_45_4
kupermind Jul 12, 2024
f1ae5a9
Merge pull request #158 from valory-xyz/addressing_issue_29
kupermind Jul 12, 2024
c41d402
Merge pull request #157 from valory-xyz/address_issue_20
kupermind Jul 12, 2024
949e7a7
Merge branch 'main' into addressing_issue_22
kupermind Jul 12, 2024
a423fb7
chore: updating Dispenser ABI
kupermind Jul 12, 2024
660e67c
test: splitting two time-dependent tests
kupermind Jul 12, 2024
10bdd73
Merge pull request #156 from valory-xyz/addressing_issue_22
DavidMinarsch Jul 12, 2024
4d2f266
doc: catch up on changes
Jul 15, 2024
8b49e18
chore: Tokenomics V1.2 deployment procedure update
kupermind Jul 15, 2024
ea67131
chore: diepenser deployment script
kupermind Jul 15, 2024
3c5c064
chore: deployment scripts
kupermind Jul 15, 2024
5291640
chore: gitleaksignore
kupermind Jul 15, 2024
6a6dad9
chore: gitleaksignore
kupermind Jul 15, 2024
abeb2f6
Merge pull request #171 from valory-xyz/catchup_changes
DavidMinarsch Jul 15, 2024
58273b3
Merge pull request #172 from valory-xyz/deployment_v1.2
DavidMinarsch Jul 15, 2024
7cf3cd1
chore: adding base scripts
kupermind Jul 17, 2024
78ba6ea
chore: gitleaksignore
kupermind Jul 17, 2024
cfe4b80
chore: adding celo deployment scripts
kupermind Jul 17, 2024
3781d38
chore: gitleaksignore
kupermind Jul 17, 2024
b439787
chore: gitleaksignore
kupermind Jul 17, 2024
80c9b27
Merge pull request #173 from valory-xyz/base_scripts
kupermind Jul 18, 2024
a65ea20
chore: adding staking claiming events info
kupermind Jul 18, 2024
cdbe1f1
Merge pull request #174 from valory-xyz/claiming_events
DavidMinarsch Jul 19, 2024
184fb9d
chore: adding unit types and ids into dev incentives event
kupermind Jul 19, 2024
ff13c51
Merge pull request #175 from valory-xyz/event_dev_incentives
kupermind Jul 19, 2024
5ee1475
chore: updating ABI and scripts
kupermind Jul 19, 2024
e09cdb5
Merge pull request #176 from valory-xyz/dispenser_deployment
DavidMinarsch Jul 19, 2024
e9945cd
chore: staking deployment scripts
kupermind Jul 19, 2024
583c448
chore: gitleaksignore
kupermind Jul 19, 2024
d3c5ea3
chore: updating globals
kupermind Jul 19, 2024
d79d621
chore: deploying staking contracts
kupermind Jul 19, 2024
b92c814
chore: deploy target dispensers
DavidMinarsch Jul 19, 2024
a1fb94f
chore: prepp staking/deploy_09
DavidMinarsch Jul 19, 2024
b8f1fe4
chore: continue with deployment scripts
kupermind Jul 20, 2024
edaf3aa
Merge branch 'staking_deployment' of github.com:valory-xyz/autonolas-…
kupermind Jul 20, 2024
fbab175
chore: linter
kupermind Jul 20, 2024
5da0726
chore: gitleaksignore
kupermind Jul 20, 2024
28a4c20
chore: linter
kupermind Jul 20, 2024
b616524
chore: adding scripts to update owners on L2
kupermind Jul 22, 2024
05c1937
chore: gitleaksignore
kupermind Jul 22, 2024
de29c4c
chore: gitleaksignore
kupermind Jul 22, 2024
2ac921b
chore: linter
kupermind Jul 22, 2024
be06fac
chore: adding contract addresses into configuration files
kupermind Jul 22, 2024
b74f36b
chore: updating static audit
kupermind Jul 22, 2024
5e43d54
chore: static audit
kupermind Jul 22, 2024
b946818
chore: gitleaksignore
kupermind Jul 22, 2024
7208470
chore: space
kupermind Jul 22, 2024
f3c9b3f
Merge pull request #178 from valory-xyz/staking_deployment2
DavidMinarsch Jul 22, 2024
7199f38
chore: updating static audit script
kupermind Jul 23, 2024
9cc1126
Merge pull request #177 from valory-xyz/staking_deployment
DavidMinarsch Jul 23, 2024
097d0d9
refactor: modifying tokenomics according to AIP1
kupermind Jul 24, 2024
da06ab9
fix: donator power recording
kupermind Jul 25, 2024
f8d7081
refactor: aip1 tokenomics logic update
kupermind Jul 26, 2024
12101b4
chore: upping tokenomics version
kupermind Jul 26, 2024
a5f5c34
doc: internal audit ref AIP-1/Bonding
Jul 26, 2024
44d9345
doc: updating changelog
kupermind Jul 29, 2024
a10e6b8
refactor: addressing interim internal audit findings
kupermind Jul 29, 2024
6f9f16f
refactor: tokenomics dev incentives debugging
kupermind Jul 31, 2024
f363668
Merge pull request #181 from valory-xyz/chg
kupermind Aug 1, 2024
7a72025
test: fixing tokenomics tests
kupermind Aug 1, 2024
4f03ded
refactor: making code more readable
kupermind Aug 1, 2024
1d079de
test: forge test
kupermind Aug 1, 2024
fbadbbb
chore: comments
kupermind Aug 1, 2024
ecb362b
chore: changing internal audit number
kupermind Aug 2, 2024
c76a04a
chore: merging main
kupermind Aug 2, 2024
23e803b
chore: making the map public
kupermind Aug 2, 2024
88f30e8
doc: re-audit
Aug 2, 2024
1417b2d
test: adding part of test
kupermind Aug 2, 2024
38ebd10
Merge pull request #183 from valory-xyz/v1.3.0-internal-audit2
DavidMinarsch Aug 3, 2024
927a3b8
Merge pull request #182 from valory-xyz/aip1_3
DavidMinarsch Aug 3, 2024
cc25bdf
Merge pull request #180 from valory-xyz/v1.3.0-internal-audit
DavidMinarsch Aug 3, 2024
1d4d12a
doc: comments
kupermind Aug 5, 2024
69eaccf
doc: check-erc-721 in audit
Aug 5, 2024
405d918
Merge pull request #184 from valory-xyz/audit_erc721
DavidMinarsch Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 73 additions & 28 deletions contracts/Tokenomics.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ error WrongUnitId(uint256 unitId, uint256 unitType);
struct UnitPoint {
// Summation of all the relative OLAS top-ups accumulated by each component / agent in a service
// After 10 years, the OLAS inflation rate is 2% per year. It would take 220+ years to reach 2^96 - 1
// Note: This is a legacy parameter now and not used throughout the current tokenomics logic
uint96 sumUnitTopUpsOLAS;
// Number of new units
// This number cannot be practically bigger than the total number of supported units
Expand All @@ -181,7 +182,7 @@ struct EpochPoint {
// After 10 years, the OLAS inflation rate is 2% per year. It would take 220+ years to reach 2^96 - 1
uint96 totalTopUpsOLAS;
// Inverse of the discount factor
// NOTE: This is a legacy parameter now and not used throughout the tokenomics logic
// NOTE: This is a legacy parameter now and not used throughout the current tokenomics logic
// IDF is bound by a factor of 18, since (2^64 - 1) / 10^18 > 18
// IDF uses a multiplier of 10^18 by default, since it is a rational number and must be accounted for divisions
// The IDF depends on the epsilonRate value, idf = 1 + epsilonRate, and epsilonRate is bound by 17 with 18 decimals
Expand Down Expand Up @@ -246,6 +247,13 @@ struct StakingPoint {
uint8 stakingFraction;
}

// Struct for service staking epoch info
struct DonationPoint {
uint96 totalUnitTopUps;
uint96 totalDonationPower;
bool moreDonationPower;
}
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved

/// @title Tokenomics - Smart contract for tokenomics logic with incentives for unit owners, discount factor
/// regulations for bonds, and staking incentives.
/// @author Aleksandr Kuperman - <[email protected]>
Expand Down Expand Up @@ -317,7 +325,7 @@ contract Tokenomics is TokenomicsConstants {
// Component Registry
address public componentRegistry;
// Default epsilon rate that contributes to the interest rate: 10% or 0.1
// NOTE: This is a legacy parameter now and not used throughout the tokenomics logic
// NOTE: This is a legacy parameter now and not used throughout the current tokenomics logic
// We assume that for the IDF calculation epsilonRate must be lower than 17 (with 18 decimals)
// (2^64 - 1) / 10^18 > 18, however IDF = 1 + epsilonRate, thus we limit epsilonRate by 17 with 18 decimals at most
uint64 public epsilonRate;
Expand Down Expand Up @@ -372,6 +380,10 @@ contract Tokenomics is TokenomicsConstants {

// Mapping of epoch => service staking point
mapping(uint256 => StakingPoint) public mapEpochStakingPoints;
// Mapping of hash(epoch + donator address) => true the voting power has been utilized in the on-going epoch
mapping(bytes32 => bool) public mapEpochDonatorPowerHashes;
// Mapping of epoch => donation point
mapping(uint256 => DonationPoint) mapDonationPoints;

/// @dev Tokenomics constructor.
constructor()
Expand Down Expand Up @@ -853,11 +865,18 @@ contract Tokenomics is TokenomicsConstants {
// The topUpUnitFraction was checked before and if it were zero, pendingRelativeTopUp would be zero as well
if (totalIncentives > 0) {
// Summation of all the unit top-ups and total amount of top-ups per epoch
// TODO: update the formula
// topUp = (pendingRelativeTopUp * totalTopUpsOLAS * topUpUnitFraction) / (100 * sumUnitTopUpsOLAS)
totalIncentives *= mapEpochTokenomics[epochNum].epochPoint.totalTopUpsOLAS;
totalIncentives *= mapEpochTokenomics[epochNum].unitPoints[unitType].topUpUnitFraction;
uint256 sumUnitIncentives = uint256(mapEpochTokenomics[epochNum].unitPoints[unitType].sumUnitTopUpsOLAS) * 100;
totalIncentives = mapUnitIncentives[unitType][unitId].topUp + totalIncentives / sumUnitIncentives;
if (mapDonationPoints[epochNum].moreDonationPower) {
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
// TODO: calculate top-ups for components and agents before this function call, before the loop if possible
uint256 totalTopUpsOLAS = mapEpochTokenomics[epochNum].epochPoint.totalTopUpsOLAS;
// TODO: pass this as a function parameter in order to optimize on gas
uint256 totalDonationPower = mapDonationPoints[epochNum].totalDonationPower;
totalIncentives *= totalTopUpsOLAS * mapEpochTokenomics[epochNum].unitPoints[unitType].topUpUnitFraction;
totalIncentives = mapUnitIncentives[unitType][unitId].topUp + (totalIncentives / (totalDonationPower * 100));
} else {
totalIncentives = mapUnitIncentives[unitType][unitId].topUp + totalIncentives;
}
mapUnitIncentives[unitType][unitId].topUp = uint96(totalIncentives);
// Setting pending top-up to zero
mapUnitIncentives[unitType][unitId].pendingRelativeTopUp = 0;
Expand Down Expand Up @@ -888,18 +907,31 @@ contract Tokenomics is TokenomicsConstants {

// Get the number of services
uint256 numServices = serviceIds.length;
// Loop over service Ids to calculate their partial contributions
for (uint256 i = 0; i < numServices; ++i) {
// Check if the service owner or donator stakes enough OLAS for its components / agents to get a top-up
// If both component and agent owner top-up fractions are zero, there is no need to call external contract
// functions to check each service owner veOLAS balance
bool topUpEligible;
if (incentiveFlags[2] || incentiveFlags[3]) {
address serviceOwner = IToken(serviceRegistry).ownerOf(serviceIds[i]);
topUpEligible = (IVotingEscrow(ve).getVotes(serviceOwner) >= veOLASThreshold ||
IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false;

// Check if the donator stakes enough OLAS for its components / agents to get a top-up
// If both component and agent owner top-up fractions are zero, there is no need to call external contract
// functions to check each service owner veOLAS balance
uint256 vPower;
if (incentiveFlags[2] || incentiveFlags[3]) {
vPower = IVotingEscrow(ve).getVotes(donator);
bytes32 donatorPowerHash = keccak256(abi.encode(epochCounter, donator));
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
// Check the donator voting power
if (vPower < veOLASThreshold || mapEpochDonatorPowerHashes[donatorPowerHash]) {
// If voting power is below the threshold or has been already utilized during the on-going epoch,
// top-ups are not eligible
vPower = 0;
} else {
// Increase the total top-up voting power in this epoch
mapDonationPoints[epochCounter].totalDonationPower += uint96(vPower);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

veOLAS is added to the totalDonationPower (per epoch) once for each donator who has not donated yet.

// Otherwise, split them to the corresponding number of services
vPower /= numServices;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

veOLAS is equally distributed across the number of services being donated to.

// Add donator voting power to the map
mapEpochDonatorPowerHashes[donatorPowerHash] = true;
}
}

// Loop over service Ids to calculate their partial contributions
for (uint256 i = 0; i < numServices; ++i) {
// Loop over component and agent Ids
for (uint256 unitType = 0; unitType < 2; ++unitType) {
// Get the number and set of units in the service
Expand All @@ -912,8 +944,12 @@ contract Tokenomics is TokenomicsConstants {
}
// Record amounts data only if at least one incentive unit fraction is not zero
if (incentiveFlags[unitType] || incentiveFlags[unitType + 2]) {
// The amount has to be adjusted for the number of units in the service
// Amount and voting power has to be adjusted for the number of units in the service
uint96 amount = uint96(amounts[i] / numServiceUnits);
uint96 vPowerUnit;
if (vPower > 0) {
vPowerUnit = uint96(vPower / numServiceUnits);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

veOLAS power is then further distributed between service units. Note that the fraction for component / agent normalizer will be applied later in the finalization of top-ups.

}
// Accumulate amounts for each unit Id
for (uint256 j = 0; j < numServiceUnits; ++j) {
// Get the last epoch number the incentives were accumulated for
Expand All @@ -934,12 +970,11 @@ contract Tokenomics is TokenomicsConstants {
if (incentiveFlags[unitType]) {
mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeReward += amount;
}
// If eligible, add relative top-up weights in the form of donation amounts.
// If eligible, add relative top-up weights in the form of voting power of a donator.
// These weights will represent the fraction of top-ups for each component / agent relative
// to the overall amount of top-ups that must be allocated
if (topUpEligible && incentiveFlags[unitType + 2]) {
mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeTopUp += amount;
mapEpochTokenomics[curEpoch].unitPoints[unitType].sumUnitTopUpsOLAS += amount;
// to the overall voting power for top-ups calculation correction
if (vPowerUnit > 0 && incentiveFlags[unitType + 2]) {
mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeTopUp += vPowerUnit;
}
}
}
Expand All @@ -965,8 +1000,6 @@ contract Tokenomics is TokenomicsConstants {
/// @param donationETH Overall service donation amount in ETH.
/// #if_succeeds {:msg "totalDonationsETH can only increase"} old(mapEpochTokenomics[epochCounter].epochPoint.totalDonationsETH) + donationETH <= type(uint96).max
/// ==> mapEpochTokenomics[epochCounter].epochPoint.totalDonationsETH == old(mapEpochTokenomics[epochCounter].epochPoint.totalDonationsETH) + donationETH;
/// #if_succeeds {:msg "sumUnitTopUpsOLAS for components can only increase"} mapEpochTokenomics[epochCounter].unitPoints[0].sumUnitTopUpsOLAS >= old(mapEpochTokenomics[epochCounter].unitPoints[0].sumUnitTopUpsOLAS);
/// #if_succeeds {:msg "sumUnitTopUpsOLAS for agents can only increase"} mapEpochTokenomics[epochCounter].unitPoints[1].sumUnitTopUpsOLAS >= old(mapEpochTokenomics[epochCounter].unitPoints[1].sumUnitTopUpsOLAS);
function trackServiceDonations(
address donator,
uint256[] memory serviceIds,
Expand Down Expand Up @@ -1173,6 +1206,11 @@ contract Tokenomics is TokenomicsConstants {
// we still record the amount of OLAS allocated for component / agent owner top-ups from the inflation schedule.
// This amount will appear in the EpochSettled event, and thus can be tracked historically
uint256 accountTopUps = incentives[5] + incentives[6];
// Record donation power state in a settled epoch
if (mapDonationPoints[eCounter].totalDonationPower > accountTopUps) {
mapDonationPoints[eCounter].moreDonationPower = true;
}
mapDonationPoints[eCounter].totalDonationPower = uint96(accountTopUps);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bug, here it must be the totalUnitTopUps value, which is finalized at the checkpoint.


// Service staking funding
// Refunded amount during the epoch
Expand Down Expand Up @@ -1376,12 +1414,19 @@ contract Tokenomics is TokenomicsConstants {
totalIncentives = mapUnitIncentives[unitTypes[i]][unitIds[i]].pendingRelativeTopUp;
if (totalIncentives > 0) {
// Summation of all the unit top-ups and total amount of top-ups per epoch
// TODO: update the formula
// topUp = (pendingRelativeTopUp * totalTopUpsOLAS * topUpUnitFraction) / (100 * sumUnitTopUpsOLAS)
totalIncentives *= mapEpochTokenomics[lastEpoch].epochPoint.totalTopUpsOLAS;
totalIncentives *= mapEpochTokenomics[lastEpoch].unitPoints[unitTypes[i]].topUpUnitFraction;
uint256 sumUnitIncentives = uint256(mapEpochTokenomics[lastEpoch].unitPoints[unitTypes[i]].sumUnitTopUpsOLAS) * 100;
// Accumulate to the final top-up for the last epoch
topUp += totalIncentives / sumUnitIncentives;
if (mapDonationPoints[lastEpoch].moreDonationPower) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This corresponds to the same top-ups calculations as in the write function above.

// TODO: calculate top-ups for components and agents before this function call, before the loop if possible
uint256 totalTopUpsOLAS = mapEpochTokenomics[lastEpoch].epochPoint.totalTopUpsOLAS;
// TODO: pass this as a function parameter in order to optimize on gas
uint256 totalDonationPower = mapDonationPoints[lastEpoch].totalDonationPower;
totalIncentives *= totalTopUpsOLAS * mapEpochTokenomics[lastEpoch].unitPoints[unitTypes[i]].topUpUnitFraction;
totalIncentives = mapUnitIncentives[unitTypes[i]][unitIds[i]].topUp + (totalIncentives / (totalDonationPower * 100));
} else {
topUp += totalIncentives;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/TokenomicsConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pragma solidity ^0.8.25;
/// @author Mariapia Moscatiello - <[email protected]>
abstract contract TokenomicsConstants {
// Tokenomics version number
string public constant VERSION = "1.2.0";
string public constant VERSION = "1.3.0";
// Tokenomics proxy address slot
// keccak256("PROXY_TOKENOMICS") = "0xbd5523e7c3b6a94aa0e3b24d1120addc2f95c7029e097b466b2bedc8d4b4362f"
bytes32 public constant PROXY_TOKENOMICS = 0xbd5523e7c3b6a94aa0e3b24d1120addc2f95c7029e097b466b2bedc8d4b4362f;
Expand Down
Loading