Skip to content

Commit

Permalink
test: staking dispenser
Browse files Browse the repository at this point in the history
  • Loading branch information
kupermind committed May 15, 2024
1 parent 77bbd4e commit c3891aa
Show file tree
Hide file tree
Showing 6 changed files with 409 additions and 73 deletions.
142 changes: 78 additions & 64 deletions contracts/Dispenser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -284,20 +284,22 @@ contract Dispenser {
mapping(uint256 => uint256) public mapChainIdWithheldAmounts;

/// @dev Dispenser constructor.
/// @param _olas OLAS token address.
/// @param _tokenomics Tokenomics address.
/// @param _treasury Treasury address.
/// @param _voteWeighting Vote Weighting address.
constructor(address _tokenomics, address _treasury, address _voteWeighting) {
constructor(address _olas, address _tokenomics, address _treasury, address _voteWeighting) {
owner = msg.sender;
_locked = 1;
// TODO Define final behavior before deployment
paused = Pause.StakingIncentivesPaused;

// Check for at least one zero contract address
if (_tokenomics == address(0) || _treasury == address(0) || _voteWeighting == address(0)) {
if (_tokenomics == address(0) || _treasury == address(0) || _olas == address(0) || _voteWeighting == address(0)) {
revert ZeroAddress();
}

olas = _olas;
tokenomics = _tokenomics;
treasury = _treasury;
voteWeighting = _voteWeighting;
Expand All @@ -317,7 +319,12 @@ contract Dispenser {
uint256 numClaimedEpochs
) internal returns (uint256 firstClaimedEpoch, uint256 lastClaimedEpoch) {
// Checkpoint the vote weighting for the retainer on L1
IVoteWeighting(voteWeighting).checkpointNominee(target, chainId);
if (msg.sender == address(this) || msg.sender == owner) {
IVoteWeighting(voteWeighting).checkpointNominee(target, chainId);
} else {
voteWeighting.staticcall(abi.encodeWithSelector(IVoteWeighting(voteWeighting).checkpointNominee.selector,
target, chainId));
}

// Get the current epoch number
uint256 eCounter = ITokenomics(tokenomics).epochCounter();
Expand Down Expand Up @@ -365,7 +372,9 @@ contract Dispenser {
}

// Write last claimed epoch counter to start claiming / retaining from the next time
mapLastClaimedStakingEpochs[nomineeHash] = lastClaimedEpoch;
if (msg.sender == address(this) || msg.sender == owner) {
mapLastClaimedStakingEpochs[nomineeHash] = lastClaimedEpoch;
}
}

/// @dev Distributes staking incentives to a corresponding staking target.
Expand Down Expand Up @@ -641,6 +650,36 @@ contract Dispenser {
}
}

/// @dev Sets deposit processor contracts addresses and L2 chain Ids.
/// @notice It is the contract owner responsibility to set correct L1 deposit processor contracts
/// and corresponding supported L2 chain Ids.
/// @param depositProcessors Set of deposit processor contract addresses on L1.
/// @param chainIds Set of corresponding L2 chain Ids.
function setDepositProcessorChainIds(address[] memory depositProcessors, uint256[] memory chainIds) external {
// Check for the ownership
if (msg.sender != owner) {
revert OwnerOnly(msg.sender, owner);
}

// Check for array length correctness
if (depositProcessors.length == 0 || depositProcessors.length != chainIds.length) {
revert WrongArrayLength(depositProcessors.length, chainIds.length);
}

// Link L1 and L2 bridge mediators, set L2 chain Ids
for (uint256 i = 0; i < chainIds.length; ++i) {
// Check supported chain Ids on L2
if (chainIds[i] == 0) {
revert ZeroValue();
}

// Note: depositProcessors[i] might be zero if there is a need to stop processing a specific L2 chain Id
mapChainIdDepositProcessors[chainIds[i]] = depositProcessors[i];
}

emit SetDepositProcessorChainIds(depositProcessors, chainIds);
}

/// @dev Records nominee starting epoch number.
/// @param nomineeHash Nominee hash.
function addNominee(bytes32 nomineeHash) external {
Expand All @@ -649,6 +688,12 @@ contract Dispenser {
revert ManagerOnly(msg.sender, voteWeighting);
}

// Check for the paused state
Pause currentPause = paused;
if (currentPause == Pause.StakingIncentivesPaused || currentPause == Pause.AllPaused) {
revert Paused();
}

mapLastClaimedStakingEpochs[nomineeHash] = ITokenomics(tokenomics).epochCounter();
}

Expand All @@ -663,39 +708,6 @@ contract Dispenser {
mapRemovedNomineeEpochs[nomineeHash] = ITokenomics(tokenomics).epochCounter();
}

/// @dev Retains staking incentives according to the retainer address to return it back to the staking inflation.
function retain() external {
// Go over epochs and retain funds to return back to the tokenomics
bytes32 localRetainer = retainer;

// Check for zero retainer address
if (localRetainer == 0) {
revert ZeroAddress();
}

(uint256 firstClaimedEpoch, uint256 lastClaimedEpoch) =
_checkpointNomineeAndGetClaimedEpochCounters(localRetainer, block.chainid, maxNumClaimingEpochs);

uint256 totalReturnAmount;

// Traverse all the claimed epochs
for (uint256 j = firstClaimedEpoch; j < lastClaimedEpoch; ++j) {
// Get service staking info
ITokenomics.StakingPoint memory stakingPoint =
ITokenomics(tokenomics).mapEpochStakingPoints(j);

// Get epoch end time
uint256 endTime = ITokenomics(tokenomics).getEpochEndTime(j);

// Get the staking weight for each epoch
(uint256 stakingWeight, ) =
IVoteWeighting(voteWeighting).nomineeRelativeWeight(localRetainer, block.chainid, endTime);

totalReturnAmount += stakingPoint.stakingAmount * stakingWeight;
}
totalReturnAmount /= 1e18;
}

/// @dev Claims incentives for the owner of components / agents.
/// @notice `msg.sender` must be the owner of components / agents they are passing, otherwise the function will revert.
/// @notice If not all `unitIds` belonging to `msg.sender` were provided, they will be untouched and keep accumulating.
Expand All @@ -713,6 +725,7 @@ contract Dispenser {
}
_locked = 2;

// Check for the paused state
Pause currentPause = paused;
if (currentPause == Pause.DevIncentivesPaused || currentPause == Pause.AllPaused) {
revert Paused();
Expand Down Expand Up @@ -848,6 +861,7 @@ contract Dispenser {
revert Overflow(numClaimedEpochs, maxNumClaimingEpochs);
}

// Check for the paused state
Pause currentPause = paused;
if (currentPause == Pause.StakingIncentivesPaused || currentPause == Pause.AllPaused) {
revert Paused();
Expand Down Expand Up @@ -1017,37 +1031,37 @@ contract Dispenser {
_locked = 1;
}

/// @dev Sets deposit processor contracts addresses and L2 chain Ids.
/// @notice It is the contract owner responsibility to set correct L1 deposit processor contracts
/// and corresponding supported L2 chain Ids.
/// @param depositProcessors Set of deposit processor contract addresses on L1.
/// @param chainIds Set of corresponding L2 chain Ids.
function setDepositProcessorChainIds(
address[] memory depositProcessors,
uint256[] memory chainIds
) external {
// Check for the ownership
if (msg.sender != owner) {
revert OwnerOnly(msg.sender, owner);
}
/// @dev Retains staking incentives according to the retainer address to return it back to the staking inflation.
function retain() external {
// Go over epochs and retain funds to return back to the tokenomics
bytes32 localRetainer = retainer;

// Check for array correctness
if (depositProcessors.length != chainIds.length) {
revert WrongArrayLength(depositProcessors.length, chainIds.length);
// Check for zero retainer address
if (localRetainer == 0) {
revert ZeroAddress();
}

// Link L1 and L2 bridge mediators, set L2 chain Ids
for (uint256 i = 0; i < chainIds.length; ++i) {
// Check supported chain Ids on L2
if (chainIds[i] == 0) {
revert ZeroValue();
}
(uint256 firstClaimedEpoch, uint256 lastClaimedEpoch) =
_checkpointNomineeAndGetClaimedEpochCounters(localRetainer, block.chainid, maxNumClaimingEpochs);

// Note: depositProcessors[i] might be zero if there is a need to stop processing a specific L2 chain Id
mapChainIdDepositProcessors[chainIds[i]] = depositProcessors[i];
}
uint256 totalReturnAmount;

emit SetDepositProcessorChainIds(depositProcessors, chainIds);
// Traverse all the claimed epochs
for (uint256 j = firstClaimedEpoch; j < lastClaimedEpoch; ++j) {
// Get service staking info
ITokenomics.StakingPoint memory stakingPoint =
ITokenomics(tokenomics).mapEpochStakingPoints(j);

// Get epoch end time
uint256 endTime = ITokenomics(tokenomics).getEpochEndTime(j);

// Get the staking weight for each epoch
(uint256 stakingWeight, ) =
IVoteWeighting(voteWeighting).nomineeRelativeWeight(localRetainer, block.chainid, endTime);

totalReturnAmount += stakingPoint.stakingAmount * stakingWeight;
}
totalReturnAmount /= 1e18;
}

/// @dev Syncs the withheld amount according to the data received from L2.
Expand Down Expand Up @@ -1104,7 +1118,7 @@ contract Dispenser {

/// @dev Sets the pause state.
/// @param pauseState Pause state.
function setPause(Pause pauseState) external {
function setPauseState(Pause pauseState) external {
// Check the contract ownership
if (msg.sender != owner) {
revert OwnerOnly(msg.sender, owner);
Expand Down
82 changes: 82 additions & 0 deletions contracts/test/MockVoteWeighting.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IDispenser {
function addNominee(bytes32 nomineeHash) external;
function removeNominee(bytes32 nomineeHash) external;
}

// Nominee struct
struct Nominee {
bytes32 account;
uint256 chainId;
}

/// @dev Mocking contract of vote weighting.
contract MockVoteWeighting {
address public immutable dispenser;
uint256 public totalWeight;

// Set of Nominee structs
Nominee[] public setNominees;
// Mapping of hash(Nominee struct) => nominee Id
mapping(bytes32 => uint256) public mapNomineeIds;
// Mapping of hash(Nominee struct) => nominee weight
mapping(bytes32 => uint256) public mapNomineeRelativeWeights;

constructor(address _dispenser) {
dispenser = _dispenser;
setNominees.push(Nominee(0, 0));
}

/// @dev Checkpoint to fill data for both a specific nominee and common for all nominees.
/// @param account Address of the nominee.
/// @param chainId Chain Id.
function checkpointNominee(bytes32 account, uint256 chainId) external {

}

/// @dev Set staking weight.
function setNomineeRelativeWeight(address account, uint256 chainId, uint256 weight) external {
Nominee memory nominee = Nominee(bytes32(uint256(uint160(account))), chainId);
bytes32 nomineeHash = keccak256(abi.encode(nominee));

mapNomineeRelativeWeights[nomineeHash] = weight * 10**18;

totalWeight += weight;
}

/// @dev Get Nominee relative weight (not more than 1.0) normalized to 1e18 and the sum of weights.
/// (e.g. 1.0 == 1e18). Inflation which will be received by it is
/// inflation_rate * relativeWeight / 1e18.
/// @param account Address of the nominee in bytes32 form.
/// @param chainId Chain Id.
/// @param time Relative weight at the specified timestamp in the past or present.
/// @return Value of relative weight normalized to 1e18.
/// @return Sum of nominee weights.
function nomineeRelativeWeight(bytes32 account, uint256 chainId, uint256 time) external view returns (uint256, uint256) {
Nominee memory nominee = Nominee(account, chainId);
bytes32 nomineeHash = keccak256(abi.encode(nominee));
return (mapNomineeRelativeWeights[nomineeHash], totalWeight);
}

/// @dev Records nominee starting epoch number.
function addNominee(address account, uint256 chainId) external {
Nominee memory nominee = Nominee(bytes32(uint256(uint160(account))), chainId);
bytes32 nomineeHash = keccak256(abi.encode(nominee));

uint256 id = setNominees.length;
mapNomineeIds[nomineeHash] = id;
// Push the nominee into the list
setNominees.push(nominee);

IDispenser(dispenser).addNominee(nomineeHash);
}

/// @dev Records nominee removal epoch number.
function removeNominee(address account, uint256 chainId) external {
Nominee memory nominee = Nominee(bytes32(uint256(uint160(account))), chainId);
bytes32 nomineeHash = keccak256(abi.encode(nominee));
IDispenser(dispenser).removeNominee(nomineeHash);
}
}
2 changes: 1 addition & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,6 @@ module.exports = {
]
},
gasReporter: {
enabled: true
enabled: false
}
};
14 changes: 8 additions & 6 deletions test/DispenserDevIncentives.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { ethers } = require("hardhat");
const { expect } = require("chai");
const helpers = require("@nomicfoundation/hardhat-network-helpers");

describe("DispenserStakingIncentives", async () => {
describe("DispenserDevIncentives", async () => {
const initialMint = "1" + "0".repeat(26);
const AddressZero = "0x" + "0".repeat(40);
const oneMonth = 86400 * 30;
Expand All @@ -15,7 +15,6 @@ describe("DispenserStakingIncentives", async () => {
let treasury;
let dispenser;
let ve;
let voteWeighting;
let serviceRegistry;
let componentRegistry;
let agentRegistry;
Expand Down Expand Up @@ -49,7 +48,7 @@ describe("DispenserStakingIncentives", async () => {
await ve.deployed();

const Dispenser = await ethers.getContractFactory("Dispenser");
dispenser = await Dispenser.deploy(deployer.address, deployer.address, deployer.address);
dispenser = await Dispenser.deploy(olas.address, deployer.address, deployer.address, deployer.address);
await dispenser.deployed();

const Treasury = await ethers.getContractFactory("Treasury");
Expand Down Expand Up @@ -135,13 +134,16 @@ describe("DispenserStakingIncentives", async () => {
it("Should fail if deploying a dispenser with a zero address", async function () {
const Dispenser = await ethers.getContractFactory("Dispenser");
await expect(
Dispenser.deploy(AddressZero, AddressZero, AddressZero)
Dispenser.deploy(AddressZero, AddressZero, AddressZero, AddressZero)
).to.be.revertedWithCustomError(dispenser, "ZeroAddress");
await expect(
Dispenser.deploy(AddressZero, deployer.address, AddressZero)
Dispenser.deploy(deployer.address, AddressZero, AddressZero, AddressZero)
).to.be.revertedWithCustomError(dispenser, "ZeroAddress");
await expect(
Dispenser.deploy(deployer.address, AddressZero, AddressZero)
Dispenser.deploy(deployer.address, deployer.address, AddressZero, AddressZero)
).to.be.revertedWithCustomError(dispenser, "ZeroAddress");
await expect(
Dispenser.deploy(deployer.address, deployer.address, deployer.address, AddressZero)
).to.be.revertedWithCustomError(dispenser, "ZeroAddress");
});
});
Expand Down
Loading

0 comments on commit c3891aa

Please sign in to comment.