Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 32 additions & 0 deletions contracts/interfaces/INexusViewer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.18;

import {IAssessmentViewer} from "./IAssessmentViewer.sol";
import {IStakingViewer} from "./IStakingViewer.sol";

interface INexusViewer {

struct ClaimableNXM {
uint governanceRewards; // Governance rewards in NXM
uint assessmentRewards; // Claimable assessment reward in NXM
uint assessmentStake; // Claimable assessment stake in NXM
uint stakingPoolTotalRewards; // Total staking pool rewards in NXM
uint stakingPoolTotalExpiredStake; // Total staking pool expired stake in NXM
uint managerTotalRewards; // Pool manager total staking rewards in NXM
uint legacyPooledStakeRewards; // Legacy pooled staking rewards in NXM
uint legacyPooledStakeDeposits; // Legacy pooled staking deposits in NXM
uint legacyCoverNoteDeposits; // Legacy cover note deposits in NXM
uint legacyClaimAssessmentTokens; // Legacy claim assessment tokens in NXM
}

struct StakedNXM {
uint stakingPoolTotalActiveStake; // Total amount of active stake in staking pools in NXM
uint assessmentStake; // Locked assessment stake in NXM
uint assessmentRewards; // Locked assessment rewards in NXM
}

function getClaimableNXM(address member, uint[] calldata tokenIds) external view returns (ClaimableNXM memory);

function getStakedNXM(address member, uint[] calldata tokenIds) external view returns (StakedNXM memory);
}
5 changes: 4 additions & 1 deletion contracts/interfaces/IStakingViewer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ interface IStakingViewer {
struct ManagerPoolsAndRewards {
Pool[] pools;
Token[] rewards;
uint totalRewards;
}

/* ========== VIEWS ========== */
Expand Down Expand Up @@ -91,7 +92,9 @@ interface IStakingViewer {

function getManagedStakingPools(address manager) external view returns (Pool[] memory);

function getManagerTokenRewards(address manager) external view returns (Token[] memory tokens);
function getManagerTokenRewardsByAddr(address manager) external view returns (Token[] memory tokens);

function getManagerTotalRewards(address manager) external view returns (uint managerTotalRewards);

function getManagerPoolsAndRewards(address manager) external view returns (ManagerPoolsAndRewards memory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity >=0.5.0;
import {IAssessment} from "../../../interfaces/IAssessment.sol";

contract AVMockAssessment is IAssessment {

Configuration public override config;
mapping(address => Stake) public override stakeOf;
mapping(address => Vote[]) public override votesOf;
Expand Down
48 changes: 48 additions & 0 deletions contracts/mocks/modules/NexusViewer/NVMockAssessmentViewer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.18;

import {IAssessment} from "../../../interfaces/IAssessment.sol";
import {IAssessmentViewer} from "../../../interfaces/IAssessmentViewer.sol";
import {INXMMaster} from "../../../interfaces/INXMMaster.sol";
import {INXMToken} from "../../../interfaces/INXMToken.sol";

contract NVMockAssessmentViewer is IAssessmentViewer {

bool stakeLocked;
AssessmentRewards assessmentRewards;

/* ========== SETTERS ========== */

function setStakeLocked(bool _stakeLocked) external {
stakeLocked = _stakeLocked;
}

function setRewards(
uint _totalPendingAmountInNXM,
uint _withdrawableAmountInNXM,
uint _withdrawableUntilIndex
) external {
assessmentRewards = AssessmentRewards({
totalPendingAmountInNXM: _totalPendingAmountInNXM,
withdrawableAmountInNXM: _withdrawableAmountInNXM,
withdrawableUntilIndex: _withdrawableUntilIndex
});
}

/* ========== VIEWS ========== */

function isStakeLocked(address) external view returns (bool) {
return stakeLocked;
}

function getRewards(address) external view returns (AssessmentRewards memory) {
return assessmentRewards;
}

/* ========== NOT YET IMPLEMENTED ========== */

function assessment() public pure returns (IAssessment) {
revert("assessment not yet implemented");
}
}
126 changes: 126 additions & 0 deletions contracts/mocks/modules/NexusViewer/NVMockStakingViewer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.18;

import {ICover} from "../../../interfaces/ICover.sol";
import {INXMMaster} from "../../../interfaces/INXMMaster.sol";
import {IStakingNFT} from "../../../interfaces/IStakingNFT.sol";
import {IStakingPool} from "../../../interfaces/IStakingPool.sol";
import {IStakingProducts} from "../../../interfaces/IStakingProducts.sol";
import {IStakingPoolFactory} from "../../../interfaces/IStakingPoolFactory.sol";
import {IStakingViewer} from "../../../interfaces/IStakingViewer.sol";
import {StakingPoolLibrary} from "../../../libraries/StakingPoolLibrary.sol";

contract NVMockStakingViewer is IStakingViewer {

uint public constant TRANCHE_DURATION = 91 days;
uint public constant MAX_ACTIVE_TRANCHES = 8;
uint public constant ONE_NXM = 1 ether;
uint public constant TRANCHE_ID_AT_DEPLOY = 213;
uint public constant MAX_UINT = type(uint).max;
AggregatedTokens aggregatedTokens;

/* ========== SETTERS ========== */

function setAggregatedTokens(uint _totalActiveStake, uint _totalExpiredStake, uint _totalRewards) public {
aggregatedTokens = AggregatedTokens({
totalActiveStake: _totalActiveStake,
totalExpiredStake: _totalExpiredStake,
totalRewards: _totalRewards
});
}

/* ========== VIEWS ========== */

function getAggregatedTokens(uint[] calldata) public view returns (AggregatedTokens memory) {
return aggregatedTokens;
}

/* ========== NOT YET IMPLEMENTED ========== */

function _cover() internal pure returns (ICover) {
revert("_cover not yet implemented");
}

function _stakingProducts() internal pure returns (IStakingProducts) {
revert("_stakingProducts not yet implemented");
}

function stakingPool(uint) public pure returns (IStakingPool) {
revert("stakingPool not yet implemented");
}

/* ========== STAKING POOL ========== */

function getPool(uint) public pure returns (Pool memory) {
revert("getPool not yet implemented");
}

function getPools(uint[] memory) public pure returns (Pool[] memory) {
revert("getPools not yet implemented");
}

function getAllPools() public pure returns (Pool[] memory) {
revert("getAllPools not yet implemented");
}

function getProductPools(uint) public pure returns (Pool[] memory) {
revert("getProductPools not yet implemented");
}

/* ========== PRODUCTS ========== */

function getPoolProducts(uint) public pure returns (StakingProduct[] memory) {
revert("getPoolProducts not yet implemented");
}

/* ========== TOKENS AND DEPOSITS ========== */

function getStakingPoolsOf(uint[] memory) public pure returns (TokenPoolMap[] memory) {
revert("getStakingPoolsOf not yet implemented");
}

function _getToken(uint, uint) internal pure returns (Token memory) {
revert("_getToken not yet implemented");
}

function getToken(uint) public pure returns (Token memory) {
revert("getToken not yet implemented");
}

function getTokens(uint[] memory) public pure returns (Token[] memory) {
revert("getTokens not yet implemented");
}

function getManagedStakingPools(address) public pure returns (Pool[] memory) {
revert("getManagedStakingPools not yet implemented");
}

function getManagerTokenRewardsByAddr(address) public pure returns (Token[] memory) {
revert("getManagerTokenRewardsByAddr not yet implemented");
}

function getManagerTotalRewards(address) public pure returns (uint) {
revert("getManagerTotalRewards not yet implemented");
}

function getManagerPoolsAndRewards(address) external pure returns (ManagerPoolsAndRewards memory) {
revert("getManagerPoolsAndRewards not yet implemented");
}

function getManagerRewards(uint[] memory) external pure returns (Token[] memory) {
revert("getManagerRewards not yet implemented");
}

function processExpirationsFor(uint[] memory) external pure {
revert("processExpirationsFor not yet implemented");
}

function processExpirations(uint[] memory) public pure {
revert("processExpirations not yet implemented");
}

function _getMatchingPools(address) internal pure returns (Pool[] memory, uint) {
revert("_getMatchingPools not yet implemented");
}
}
16 changes: 13 additions & 3 deletions contracts/modules/staking/StakingViewer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ contract StakingViewer is IStakingViewer, Multicall {
return pools;
}

function getManagerTokenRewards(address manager) public view returns (Token[] memory tokens) {
function getManagerTokenRewardsByAddr(address manager) public view returns (Token[] memory tokens) {

(Pool[] memory managedPools, uint256 managedPoolCount) = _getMatchingPools(manager);
tokens = new Token[](managedPoolCount);
Expand All @@ -344,12 +344,22 @@ contract StakingViewer is IStakingViewer, Multicall {
return tokens;
}

function getManagerTotalRewards(address manager) public view returns (uint managerTotalRewards) {

Token[] memory tokenRewards = getManagerTokenRewardsByAddr(manager);

for (uint i = 0; i < tokenRewards.length; i++) {
managerTotalRewards += tokenRewards[i].rewards;
}
}

function getManagerPoolsAndRewards(address manager) external view returns (ManagerPoolsAndRewards memory) {

Pool[] memory pools = getManagedStakingPools(manager);
Token[] memory tokens = getManagerTokenRewards(manager);
Token[] memory tokens = getManagerTokenRewardsByAddr(manager);
uint totalRewards = getManagerTotalRewards(manager);

return ManagerPoolsAndRewards({ pools: pools, rewards: tokens });
return ManagerPoolsAndRewards({pools: pools, rewards: tokens, totalRewards: totalRewards});
}

function getManagerRewards(uint[] memory poolIds) external view returns (Token[] memory tokens) {
Expand Down
108 changes: 108 additions & 0 deletions contracts/modules/viewer/NexusViewer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.18;

import {Multicall} from "../../abstract/Multicall.sol";
import {IAssessment} from "../../interfaces/IAssessment.sol";
import {IAssessmentViewer} from "../../interfaces/IAssessmentViewer.sol";
import {IGovernance} from "../../interfaces/IGovernance.sol";
import {INexusViewer} from "../../interfaces/INexusViewer.sol";
import {INXMMaster} from "../../interfaces/INXMMaster.sol";
import {IPooledStaking} from "../../interfaces/IPooledStaking.sol";
import {IStakingViewer} from "../../interfaces/IStakingViewer.sol";
import {ITokenController} from "../../interfaces/ITokenController.sol";

/// @title NexusViewer Contract
/// @notice This contract provides a unified view of system-wide data from various contracts within the Nexus Mutual protocol.
contract NexusViewer is INexusViewer, Multicall {

INXMMaster public immutable master;
IStakingViewer public immutable stakingViewer;
IAssessmentViewer public immutable assessmentViewer;

constructor(INXMMaster _master, IStakingViewer _stakingViewer, IAssessmentViewer _assessmentViewer) {
master = _master;
stakingViewer = _stakingViewer;
assessmentViewer = _assessmentViewer;
}

/// @notice Retrieves the claimable NXM tokens across the protocol for a given member.
/// @dev Ensure the tokenIds passed belongs to the member address.
/// @param member The address of the member to query.
/// @param tokenIds An array of staking NFT token IDs associated with the member.
/// @return A ClaimableNxm struct containing details of the claimable NXM tokens.
function getClaimableNXM(address member, uint[] calldata tokenIds) external view returns (ClaimableNXM memory) {

// Governance
uint governanceRewards = _governance().getPendingReward(member);

// Assessment
IAssessmentViewer.AssessmentRewards memory assessmentRewards = assessmentViewer.getRewards(member);
(uint assessmentStake, bool isStakeLocked) = _getAssessmentStake(member);

// Staking Pool
IStakingViewer.AggregatedTokens memory aggregatedTokens = stakingViewer.getAggregatedTokens(tokenIds);
uint managerTotalRewards = stakingViewer.getManagerTotalRewards(member);

// V1
uint legacyPooledStakeDeposits = _legacyPooledStaking().stakerDeposit(member);
uint legacyPooledStakeRewards = _legacyPooledStaking().stakerReward(member);
uint legacyClaimAssessmentTokens = _tokenController().tokensLocked(member, "CLA");
(, , uint legacyCoverNoteDeposits) = _tokenController().getWithdrawableCoverNotes(member);

return ClaimableNXM({
governanceRewards: governanceRewards,
assessmentRewards: assessmentRewards.withdrawableAmountInNXM,
assessmentStake: isStakeLocked ? 0 : assessmentStake,
stakingPoolTotalRewards: aggregatedTokens.totalRewards,
stakingPoolTotalExpiredStake: aggregatedTokens.totalExpiredStake,
managerTotalRewards: managerTotalRewards,
legacyPooledStakeRewards: legacyPooledStakeRewards,
legacyPooledStakeDeposits: legacyPooledStakeDeposits,
legacyCoverNoteDeposits: legacyCoverNoteDeposits,
legacyClaimAssessmentTokens: legacyClaimAssessmentTokens
});
}

/// @notice Retrieves the locked NXM tokens across the protocol for a given member.
/// @dev Ensure the tokenIds passed belongs to the member address.
/// @param member The address of the member to query.
/// @param tokenIds An array of staking NFT token IDs associated with the member.
/// @return A StakedNXM struct containing details of the locked NXM tokens.
function getStakedNXM(address member, uint[] calldata tokenIds) external view returns (StakedNXM memory) {

IStakingViewer.AggregatedTokens memory aggregatedTokens = stakingViewer.getAggregatedTokens(tokenIds);

IAssessmentViewer.AssessmentRewards memory assessmentRewards = assessmentViewer.getRewards(member);
(uint assessmentStake, bool isStakeLocked) = _getAssessmentStake(member);

return StakedNXM({
stakingPoolTotalActiveStake: aggregatedTokens.totalActiveStake,
assessmentStake: isStakeLocked ? assessmentStake : 0,
assessmentRewards: assessmentRewards.totalPendingAmountInNXM - assessmentRewards.withdrawableAmountInNXM
});
}

function _getAssessmentStake(address member) internal view returns (uint assessmentStake, bool isStakeLocked) {
(assessmentStake, , ) = _assessment().stakeOf(member);
isStakeLocked = assessmentViewer.isStakeLocked(member);
}

/* ========== DEPENDENCIES ========== */

function _assessment() internal view returns (IAssessment) {
return IAssessment(master.getLatestAddress("AS"));
}

function _legacyPooledStaking() internal view returns (IPooledStaking) {
return IPooledStaking(master.getLatestAddress("PS"));
}

function _governance() internal view returns (IGovernance) {
return IGovernance(master.getLatestAddress("GV"));
}

function _tokenController() internal view returns (ITokenController) {
return ITokenController(master.contractAddresses("TC"));
}
}
6 changes: 1 addition & 5 deletions test/fork/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { artifacts, ethers, network } = require('hardhat');
const assert = require('assert');

const { setEtherBalance } = require('../utils/evm');
const { calculateCurrentTrancheId } = require('../utils/stakingPool');
const { ProposalCategory: PROPOSAL_CATEGORIES } = require('../../lib/constants');
const { parseEther, defaultAbiCoder, keccak256 } = ethers.utils;
const { BigNumber } = ethers;
Expand Down Expand Up @@ -157,11 +158,6 @@ async function submitMemberVoteGovernanceProposal(categoryId, actionData, signer
assert.equal(proposal[2].toNumber(), 3, 'Proposal Status != ACCEPTED');
}

async function calculateCurrentTrancheId() {
const lastBlock = await ethers.provider.getBlock('latest');
return Math.floor(lastBlock.timestamp / (91 * 24 * 3600));
}

const getSigner = async address => {
const provider =
network.name !== 'hardhat' // ethers errors out when using non-local accounts
Expand Down
Loading