From f3343bac304ceefbf83aa32c71fa2d5a7e54385e Mon Sep 17 00:00:00 2001 From: mixplore <9848598+mixplore@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:57:03 +0200 Subject: [PATCH 1/4] feat: add pool manager NXM locked for MV timestamp to NexusViewer claimable NXM method return --- contracts/interfaces/INexusViewer.sol | 1 + contracts/modules/viewer/NexusViewer.sol | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/contracts/interfaces/INexusViewer.sol b/contracts/interfaces/INexusViewer.sol index 1523bacaed..3045006706 100644 --- a/contracts/interfaces/INexusViewer.sol +++ b/contracts/interfaces/INexusViewer.sol @@ -13,6 +13,7 @@ interface INexusViewer { 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 stakingPoolManagerIsNXMLockedForMV; // Staking pool manager NXM locked for MV 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 diff --git a/contracts/modules/viewer/NexusViewer.sol b/contracts/modules/viewer/NexusViewer.sol index 18243fa041..1c874134d0 100644 --- a/contracts/modules/viewer/NexusViewer.sol +++ b/contracts/modules/viewer/NexusViewer.sol @@ -8,6 +8,7 @@ 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 {INXMToken} from "../../interfaces/INXMToken.sol"; import {IPooledStaking} from "../../interfaces/IPooledStaking.sol"; import {IStakingViewer} from "../../interfaces/IStakingViewer.sol"; import {ITokenController} from "../../interfaces/ITokenController.sol"; @@ -17,6 +18,7 @@ import {ITokenController} from "../../interfaces/ITokenController.sol"; contract NexusViewer is INexusViewer, Multicall { INXMMaster public immutable master; + INXMToken public nxm; IStakingViewer public immutable stakingViewer; IAssessmentViewer public immutable assessmentViewer; @@ -43,7 +45,20 @@ contract NexusViewer is INexusViewer, Multicall { // Staking Pool IStakingViewer.AggregatedTokens memory aggregatedTokens = stakingViewer.getAggregatedTokens(tokenIds); uint managerTotalRewards = stakingViewer.getManagerTotalRewards(member); - + + IStakingViewer.TokenPoolMap[] memory tokenPools = stakingViewer.getStakingPoolsOf(tokenIds); + uint poolManagerNXMLockedForMV = 0; + // for each token, get the pool and manager + for (uint i = 0; i < tokenPools.length; i++) { + IStakingViewer.Pool memory pool = stakingViewer.getPool(tokenPools[i].poolId); + // check if pool manager is locked for MV + uint lockedForMV = nxm.isLockedForMV(pool.manager); + // get the latest date locked for MV + if (lockedForMV > 0 && lockedForMV > poolManagerNXMLockedForMV) { + poolManagerNXMLockedForMV = lockedForMV; + } + } + // V1 uint legacyPooledStakeRewards = _legacyPooledStaking().stakerReward(member); uint legacyPooledStakeDeposits = _legacyPooledStaking().stakerDeposit(member); @@ -56,6 +71,7 @@ contract NexusViewer is INexusViewer, Multicall { assessmentStake: stakeLockedState.isStakeLocked ? 0 : assessmentStake, stakingPoolTotalRewards: aggregatedTokens.totalRewards, stakingPoolTotalExpiredStake: aggregatedTokens.totalExpiredStake, + stakingPoolManagerIsNXMLockedForMV: poolManagerNXMLockedForMV, managerTotalRewards: managerTotalRewards, legacyPooledStakeRewards: legacyPooledStakeRewards, legacyPooledStakeDeposits: legacyPooledStakeDeposits, From 1ea19ff870c243c4da24b08ea6c4567225330856 Mon Sep 17 00:00:00 2001 From: mixplore <9848598+mixplore@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:32:04 +0200 Subject: [PATCH 2/4] chore: fix stack too deep compile error --- contracts/modules/viewer/NexusViewer.sol | 30 ++++++++++++------- .../assessment-and-nexus-viewer-deploy.js | 1 + 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/contracts/modules/viewer/NexusViewer.sol b/contracts/modules/viewer/NexusViewer.sol index 1c874134d0..536d941812 100644 --- a/contracts/modules/viewer/NexusViewer.sol +++ b/contracts/modules/viewer/NexusViewer.sol @@ -40,22 +40,30 @@ contract NexusViewer is INexusViewer, Multicall { // Assessment IAssessmentViewer.AssessmentRewards memory assessmentRewards = assessmentViewer.getRewards(member); - (uint assessmentStake, IAssessmentViewer.AssessmentStakeLockedState memory stakeLockedState) = _getAssessmentStake(member); + uint assessmentStakeValue = 0; + // Workaround for stack too deep error + { + (uint assessmentStake, IAssessmentViewer.AssessmentStakeLockedState memory stakeLockedState) = _getAssessmentStake(member); + assessmentStakeValue = stakeLockedState.isStakeLocked ? 0 : assessmentStake; + } // Staking Pool IStakingViewer.AggregatedTokens memory aggregatedTokens = stakingViewer.getAggregatedTokens(tokenIds); uint managerTotalRewards = stakingViewer.getManagerTotalRewards(member); - IStakingViewer.TokenPoolMap[] memory tokenPools = stakingViewer.getStakingPoolsOf(tokenIds); uint poolManagerNXMLockedForMV = 0; - // for each token, get the pool and manager - for (uint i = 0; i < tokenPools.length; i++) { - IStakingViewer.Pool memory pool = stakingViewer.getPool(tokenPools[i].poolId); - // check if pool manager is locked for MV - uint lockedForMV = nxm.isLockedForMV(pool.manager); - // get the latest date locked for MV - if (lockedForMV > 0 && lockedForMV > poolManagerNXMLockedForMV) { - poolManagerNXMLockedForMV = lockedForMV; + // Workaround for stack too deep error + { + IStakingViewer.TokenPoolMap[] memory tokenPools = stakingViewer.getStakingPoolsOf(tokenIds); + // for each token, get the pool and manager + for (uint i = 0; i < tokenPools.length; i++) { + IStakingViewer.Pool memory pool = stakingViewer.getPool(tokenPools[i].poolId); + // check if pool manager is locked for MV + uint lockedForMV = nxm.isLockedForMV(pool.manager); + // get the latest date locked for MV + if (lockedForMV > 0 && lockedForMV > poolManagerNXMLockedForMV) { + poolManagerNXMLockedForMV = lockedForMV; + } } } @@ -68,7 +76,7 @@ contract NexusViewer is INexusViewer, Multicall { return ClaimableNXM({ governanceRewards: governanceRewards, assessmentRewards: assessmentRewards.withdrawableAmountInNXM, - assessmentStake: stakeLockedState.isStakeLocked ? 0 : assessmentStake, + assessmentStake: assessmentStakeValue, stakingPoolTotalRewards: aggregatedTokens.totalRewards, stakingPoolTotalExpiredStake: aggregatedTokens.totalExpiredStake, stakingPoolManagerIsNXMLockedForMV: poolManagerNXMLockedForMV, diff --git a/scripts/deploy/assessment-and-nexus-viewer-deploy.js b/scripts/deploy/assessment-and-nexus-viewer-deploy.js index 28dc2916ee..a43cd471a5 100644 --- a/scripts/deploy/assessment-and-nexus-viewer-deploy.js +++ b/scripts/deploy/assessment-and-nexus-viewer-deploy.js @@ -19,6 +19,7 @@ const main = async () => { console.log('AssessmentViewer implementation address:', assessmentViewerImplementation.address); console.log('NexusViewer implementation address:', nexusViewerImplementation.address); + console.log('NexusViewer ABI', nexusViewerImplementation.interface.format(ethers.utils.FormatTypes.json)); }; main() .then(() => process.exit(0)) From 05526e35bdb6552a81b6a750aa4d43569e51674a Mon Sep 17 00:00:00 2001 From: mixplore <9848598+mixplore@users.noreply.github.com> Date: Tue, 12 Nov 2024 19:48:17 +0200 Subject: [PATCH 3/4] fix: nxm token contract instantiation in nexus viewer --- contracts/modules/viewer/NexusViewer.sol | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/modules/viewer/NexusViewer.sol b/contracts/modules/viewer/NexusViewer.sol index 536d941812..e27dc47095 100644 --- a/contracts/modules/viewer/NexusViewer.sol +++ b/contracts/modules/viewer/NexusViewer.sol @@ -12,20 +12,22 @@ import {INXMToken} from "../../interfaces/INXMToken.sol"; import {IPooledStaking} from "../../interfaces/IPooledStaking.sol"; import {IStakingViewer} from "../../interfaces/IStakingViewer.sol"; import {ITokenController} from "../../interfaces/ITokenController.sol"; +import {IStakingPool} from "../../interfaces/IStakingPool.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; - INXMToken public nxm; IStakingViewer public immutable stakingViewer; IAssessmentViewer public immutable assessmentViewer; + INXMToken public immutable nxm; - constructor(INXMMaster _master, IStakingViewer _stakingViewer, IAssessmentViewer _assessmentViewer) { + constructor(INXMMaster _master, IStakingViewer _stakingViewer, IAssessmentViewer _assessmentViewer, INXMToken _nxm) { master = _master; stakingViewer = _stakingViewer; assessmentViewer = _assessmentViewer; + nxm = _nxm; } /// @notice Retrieves the claimable NXM tokens across the protocol for a given member. @@ -57,9 +59,10 @@ contract NexusViewer is INexusViewer, Multicall { IStakingViewer.TokenPoolMap[] memory tokenPools = stakingViewer.getStakingPoolsOf(tokenIds); // for each token, get the pool and manager for (uint i = 0; i < tokenPools.length; i++) { - IStakingViewer.Pool memory pool = stakingViewer.getPool(tokenPools[i].poolId); + IStakingPool _stakingPool = stakingViewer.stakingPool(tokenPools[i].poolId); + address poolManager = _stakingPool.manager(); // check if pool manager is locked for MV - uint lockedForMV = nxm.isLockedForMV(pool.manager); + uint lockedForMV = nxm.isLockedForMV(poolManager); // get the latest date locked for MV if (lockedForMV > 0 && lockedForMV > poolManagerNXMLockedForMV) { poolManagerNXMLockedForMV = lockedForMV; From eb3d23e497126d53cca4a78498b0109b70253326 Mon Sep 17 00:00:00 2001 From: mixplore <9848598+mixplore@users.noreply.github.com> Date: Tue, 12 Nov 2024 19:48:51 +0200 Subject: [PATCH 4/4] chore: update viewer contracts deploy script and add ui data fetching script for claimable NXM --- .../assessment-and-nexus-viewer-deploy.js | 5 +- scripts/ui-data-fetching/get-claimable-nxm.js | 137 ++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 scripts/ui-data-fetching/get-claimable-nxm.js diff --git a/scripts/deploy/assessment-and-nexus-viewer-deploy.js b/scripts/deploy/assessment-and-nexus-viewer-deploy.js index a43cd471a5..445ea10ba8 100644 --- a/scripts/deploy/assessment-and-nexus-viewer-deploy.js +++ b/scripts/deploy/assessment-and-nexus-viewer-deploy.js @@ -2,6 +2,7 @@ const { ethers, network } = require('hardhat'); const STV = '0xcafea5E8a7a54dd14Bb225b66C7a016dfd7F236b'; // StakingViewer const MS = '0x01BFd82675DBCc7762C84019cA518e701C0cD07e'; // NXMaster +const NXM = '0xd7c49CEE7E9188cCa6AD8FF264C1DA2e69D4Cf3B'; // NXMToken const main = async () => { console.log(`Starting deploy script on ${network.name} network`); @@ -13,13 +14,15 @@ const main = async () => { const assessmentViewerImplementation = await ethers.deployContract('AssessmentViewer', [MS], signer); const nexusViewerImplementation = await ethers.deployContract( 'NexusViewer', - [MS, STV, assessmentViewerImplementation.address], + [MS, STV, assessmentViewerImplementation.address, NXM], signer, ); console.log('AssessmentViewer implementation address:', assessmentViewerImplementation.address); console.log('NexusViewer implementation address:', nexusViewerImplementation.address); console.log('NexusViewer ABI', nexusViewerImplementation.interface.format(ethers.utils.FormatTypes.json)); + + console.log('Done'); }; main() .then(() => process.exit(0)) diff --git a/scripts/ui-data-fetching/get-claimable-nxm.js b/scripts/ui-data-fetching/get-claimable-nxm.js new file mode 100644 index 0000000000..412edd3584 --- /dev/null +++ b/scripts/ui-data-fetching/get-claimable-nxm.js @@ -0,0 +1,137 @@ +require('dotenv').config(); +const { ethers } = require('hardhat'); + +const NexusViewerABI = [ + { + type: 'constructor', + payable: false, + inputs: [ + { type: 'address', name: '_master' }, + { type: 'address', name: '_stakingViewer' }, + { type: 'address', name: '_assessmentViewer' }, + { type: 'address', name: '_nxm' }, + ], + }, + { type: 'error', name: 'RevertedWithoutReason', inputs: [{ type: 'uint256', name: 'index' }] }, + { + type: 'function', + name: 'assessmentViewer', + constant: true, + stateMutability: 'view', + payable: false, + gas: 11000000, + inputs: [], + outputs: [{ type: 'address' }], + }, + { + type: 'function', + name: 'getClaimableNXM', + constant: true, + stateMutability: 'view', + payable: false, + gas: 11000000, + inputs: [ + { type: 'address', name: 'member' }, + { type: 'uint256[]', name: 'tokenIds' }, + ], + outputs: [ + { + type: 'tuple', + components: [ + { type: 'uint256', name: 'governanceRewards' }, + { type: 'uint256', name: 'assessmentRewards' }, + { type: 'uint256', name: 'assessmentStake' }, + { type: 'uint256', name: 'stakingPoolTotalRewards' }, + { type: 'uint256', name: 'stakingPoolTotalExpiredStake' }, + { type: 'uint256', name: 'stakingPoolManagerIsNXMLockedForMV' }, + { type: 'uint256', name: 'managerTotalRewards' }, + { type: 'uint256', name: 'legacyPooledStakeRewards' }, + { type: 'uint256', name: 'legacyPooledStakeDeposits' }, + { type: 'uint256', name: 'legacyClaimAssessmentTokens' }, + { type: 'uint256', name: 'legacyCoverNoteDeposits' }, + ], + }, + ], + }, + { + type: 'function', + name: 'getStakedNXM', + constant: true, + stateMutability: 'view', + payable: false, + gas: 11000000, + inputs: [ + { type: 'address', name: 'member' }, + { type: 'uint256[]', name: 'tokenIds' }, + ], + outputs: [ + { + type: 'tuple', + components: [ + { type: 'uint256', name: 'stakingPoolTotalActiveStake' }, + { type: 'uint256', name: 'assessmentStake' }, + { type: 'uint256', name: 'assessmentStakeLockupExpiry' }, + { type: 'uint256', name: 'assessmentRewards' }, + ], + }, + ], + }, + { + type: 'function', + name: 'master', + constant: true, + stateMutability: 'view', + payable: false, + gas: 11000000, + inputs: [], + outputs: [{ type: 'address' }], + }, + { + type: 'function', + name: 'multicall', + constant: false, + payable: false, + gas: 11000000, + inputs: [{ type: 'bytes[]', name: 'data' }], + outputs: [{ type: 'bytes[]', name: 'results' }], + }, + { + type: 'function', + name: 'nxm', + constant: true, + stateMutability: 'view', + payable: false, + gas: 11000000, + inputs: [], + outputs: [{ type: 'address' }], + }, + { + type: 'function', + name: 'stakingViewer', + constant: true, + stateMutability: 'view', + payable: false, + gas: 11000000, + inputs: [], + outputs: [{ type: 'address' }], + }, +]; +const NexusViewerAddress = '0xF62eEc897fa5ef36a957702AA4a45B58fE8Fe312'; + +const member = '0xd6CE9335f5A68e885271CdbE460b7A4FED5FeDA9'; +const tokenIds = [34, 35, 36, 103, 136]; + +async function main() { + const viewer = await ethers.getContractAt(NexusViewerABI, NexusViewerAddress); + + const claimableNXM = await viewer.getClaimableNXM(member, tokenIds); + + console.log('Claimable NXM:', claimableNXM); +} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + });