-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: upload the files #29
Open
bakhshandeh
wants to merge
46
commits into
main
Choose a base branch
from
staking
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
6063546
feat: upload the files
siftal e93be66
fix: fix an issue
siftal 12810e6
fix: remove an extra check
siftal 4887e77
feat: refactor lockToBondedToken to restrict usage to NFT stakers
siftal 26d3e41
feat: refactor mergeBondedTokens to restrict usage to NFT stakers
siftal 4d3403a
feat: remove tier mapping and add tier field to the Node struct
siftal 350da1c
feat: change tier type from uint64 to uint8
siftal 0a0db3f
feat: remove unnecessary getTier function
siftal 7c4920a
fix: fix an issue
siftal d14feb9
feat: remove unnecessary check
siftal 518dd50
fix: fix an issue
siftal eacd683
feat: change minStakeAmountPerNode to minStakeAmount
siftal ad192c9
feat: add setMuonNodeTire function
siftal 01ddb15
feat: remove unnecessary check
siftal 7e64a40
feat: set balance to zero on node exit
siftal 2abbcee
feat: calculate not paid rewards and redistribute them in the next di…
siftal 585f9af
style: format the code
siftal c62cd70
feat: add getInfo function
siftal 5c60340
test: update tests
siftal ee75a25
feat: add a new getEditedNodes function
siftal c5c1506
feat: let DAO_ROLE to deactivate a node
siftal edcfd0b
feat: check the balance of the node before withdrawing the stake
siftal 46e3368
fix: add nodes' roles
siftal 4e7b630
feat: get response length as a parameter
siftal c46dd01
fix: fix an issue
siftal e04c7da
feat: to improve performance break the loop instead of checking the c…
siftal 27e6e6b
feat: make the nodesList size dynamic
siftal 012bd68
test: update tests
siftal 2cf81c3
feat: ables DAO to pause/unpause specific functions
siftal 3420da0
test: update tests
siftal c1267aa
feat: use IERC20Upgradeable instead of IERC20
siftal 245d8a0
fix: remove an extra import
siftal c1bbf8c
feat: add constant for reward period
siftal 6797572
feat: check the NFT is received
siftal 3b3dbdd
feat: write state variables before the call
siftal ac0b9db
feat: add onERC721Received function
siftal 7e3bcc7
feat: check the node exists before setting the tier
siftal 11531bf
feat: check the node exists before setting the tier
siftal b1361a8
feat: update messages
siftal ea5925a
style: clean the code
siftal 74de8f4
test: update tests
siftal 5e59c8a
feat: combine lockStake and unlockStake
siftal 12c7c18
feat: use safeTransferFrom instead of transferFrom
siftal 544b829
feat: combine pauseFunction and unpauseFunction
siftal fce9c5e
fix: fix a typo
siftal 64e6044
fix: fix a typo
siftal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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,354 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.19; | ||
|
||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; | ||
import "./interfaces/IMuonNodeManager.sol"; | ||
|
||
contract MuonNodeManager is | ||
Initializable, | ||
AccessControlUpgradeable, | ||
IMuonNodeManager | ||
{ | ||
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); | ||
bytes32 public constant DAO_ROLE = keccak256("DAO_ROLE"); | ||
|
||
// nodeId => Node | ||
mapping(uint64 => Node) public nodes; | ||
|
||
// nodeAddress => nodeId | ||
mapping(address => uint64) public nodeAddressIds; | ||
|
||
// stakerAddress => nodeId | ||
mapping(address => uint64) public stakerAddressIds; | ||
|
||
uint64 public lastNodeId; | ||
|
||
// muon nodes check lastUpdateTime to sync their memory | ||
uint256 public lastUpdateTime; | ||
|
||
// commit_id => git commit id | ||
mapping(string => string) public configs; | ||
|
||
uint64 public lastRoleId; | ||
|
||
// hash(role) => role id | ||
mapping(bytes32 => uint64) public roleIds; | ||
|
||
// role id => node id => index + 1 | ||
mapping(uint64 => mapping(uint64 => uint16)) public nodesRoles; | ||
|
||
/** | ||
* @dev Modifier to update the lastUpdateTime state variable. | ||
*/ | ||
modifier updateState() { | ||
lastUpdateTime = block.timestamp; | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Modifier to update the lastEditTime of a specific node. | ||
* @param nodeId The id of the node. | ||
*/ | ||
modifier updateNodeState(uint64 nodeId) { | ||
nodes[nodeId].lastEditTime = block.timestamp; | ||
_; | ||
} | ||
|
||
function __MuonNodeManagerUpgradeable_init() internal initializer { | ||
__AccessControl_init(); | ||
|
||
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender); | ||
_setupRole(ADMIN_ROLE, msg.sender); | ||
_setupRole(DAO_ROLE, msg.sender); | ||
|
||
lastNodeId = 0; | ||
lastUpdateTime = block.timestamp; | ||
lastRoleId = 0; | ||
} | ||
|
||
/** | ||
* @dev Initializes the contract. | ||
*/ | ||
function initialize() external initializer { | ||
__MuonNodeManagerUpgradeable_init(); | ||
} | ||
|
||
function __MuonNodeManagerUpgradeable_init_unchained() | ||
internal | ||
initializer | ||
{} | ||
|
||
/** | ||
* @dev Adds a new node. | ||
* Only callable by the ADMIN_ROLE. | ||
* @param _nodeAddress The address of the node. | ||
* @param _stakerAddress The address of the staker associated with the node. | ||
* @param _peerId The peer ID of the node. | ||
* @param _active Indicates whether the node is active or not. | ||
*/ | ||
function addNode( | ||
address _nodeAddress, | ||
address _stakerAddress, | ||
string calldata _peerId, | ||
bool _active | ||
) | ||
public | ||
override | ||
onlyRole(ADMIN_ROLE) | ||
updateState | ||
{ | ||
require(nodeAddressIds[_nodeAddress] == 0, "Node address is already registered."); | ||
|
||
require( | ||
stakerAddressIds[_stakerAddress] == 0, | ||
"Staker address is already registered." | ||
); | ||
|
||
lastNodeId++; | ||
nodes[lastNodeId] = Node({ | ||
id: lastNodeId, | ||
nodeAddress: _nodeAddress, | ||
stakerAddress: _stakerAddress, | ||
peerId: _peerId, | ||
tier: 0, | ||
active: _active, | ||
roles: new uint64[](0), | ||
startTime: block.timestamp, | ||
lastEditTime: block.timestamp, | ||
endTime: 0 | ||
}); | ||
|
||
nodeAddressIds[_nodeAddress] = lastNodeId; | ||
stakerAddressIds[_stakerAddress] = lastNodeId; | ||
|
||
emit NodeAdded(lastNodeId, nodes[lastNodeId]); | ||
} | ||
|
||
/** | ||
* @dev Allows the admins to deactivate the nodes. | ||
* Only callable by the ADMIN_ROLE. | ||
* @param nodeId The ID of the node to be deactivated. | ||
*/ | ||
function deactiveNode(uint64 nodeId) | ||
public | ||
override | ||
onlyRole(ADMIN_ROLE) | ||
updateState | ||
updateNodeState(nodeId) | ||
{ | ||
require(nodes[nodeId].id == nodeId, "Node ID not found."); | ||
|
||
require(nodes[nodeId].active, "Node is already deactivated."); | ||
|
||
nodes[nodeId].endTime = block.timestamp; | ||
nodes[nodeId].active = false; | ||
|
||
emit NodeDeactivated(nodeId); | ||
} | ||
|
||
/** | ||
* @dev Adds a role to a given node. | ||
* Only callable by the DAO_ROLE. | ||
* @param nodeId The ID of the node. | ||
* @param roleId The ID of the role. | ||
*/ | ||
function setNodeRole(uint64 nodeId, uint64 roleId) | ||
public | ||
onlyRole(DAO_ROLE) | ||
updateState | ||
updateNodeState(nodeId) | ||
{ | ||
require(nodes[nodeId].active, "Node is not active."); | ||
bakhshandeh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
require(roleId > 0 && roleId <= lastRoleId, "Invalid role ID."); | ||
|
||
require( | ||
nodesRoles[roleId][nodeId] == 0, | ||
"Role is already assigned to this node." | ||
); | ||
|
||
nodes[nodeId].roles.push(roleId); | ||
nodesRoles[roleId][nodeId] = uint16(nodes[nodeId].roles.length); | ||
emit NodeRoleSet(nodeId, roleId); | ||
} | ||
|
||
/** | ||
* @dev Removes a role from a given node. | ||
* Only callable by the DAO_ROLE. | ||
* @param nodeId The ID of the node. | ||
* @param roleId The ID of the role. | ||
*/ | ||
function unsetNodeRole(uint64 nodeId, uint64 roleId) | ||
public | ||
onlyRole(DAO_ROLE) | ||
updateState | ||
updateNodeState(nodeId) | ||
{ | ||
require(roleId > 0 && roleId <= lastRoleId, "Invalid role ID."); | ||
|
||
require( | ||
nodesRoles[roleId][nodeId] > 0, | ||
"Node does not have this role." | ||
); | ||
|
||
uint16 index = nodesRoles[roleId][nodeId] - 1; | ||
uint64 lRoleId = nodes[nodeId].roles[nodes[nodeId].roles.length - 1]; | ||
nodes[nodeId].roles[index] = lRoleId; | ||
nodesRoles[lRoleId][nodeId] = index + 1; | ||
nodes[nodeId].roles.pop(); | ||
nodesRoles[roleId][nodeId] = 0; | ||
emit NodeRoleUnset(nodeId, roleId); | ||
} | ||
|
||
/** | ||
* @dev Returns whether a given node has a given role. | ||
* @param nodeId The ID of the node. | ||
* @param role The role to check. | ||
* @return A boolean indicating whether the node has the role. | ||
*/ | ||
function nodeHasRole(uint64 nodeId, bytes32 role) | ||
public | ||
view | ||
returns (bool) | ||
{ | ||
return nodesRoles[roleIds[role]][nodeId] > 0; | ||
} | ||
|
||
/** | ||
* @dev Returns a list of roles associated with a node. | ||
* @param nodeId The ID of the node. | ||
* @return An array of role IDs. | ||
*/ | ||
function getNodeRoles(uint64 nodeId) public view returns (uint64[] memory) { | ||
return nodes[nodeId].roles; | ||
} | ||
|
||
/** | ||
* @dev Returns the information of a node. | ||
* @param nodeId The ID of the node. | ||
* @return The node information. | ||
*/ | ||
function getNode(uint64 nodeId) public view returns (Node memory) { | ||
Node memory node = nodes[nodeId]; | ||
node.roles = getNodeRoles(nodeId); | ||
return node; | ||
} | ||
|
||
/** | ||
* @dev Returns a list of nodes that have been edited. | ||
* @param _lastEditTime The time of the last edit. | ||
* @param _from The starting node ID. | ||
* @param _to The ending node ID. | ||
* @return nodesList An array of edited nodes. | ||
*/ | ||
function getEditedNodes( | ||
uint256 _lastEditTime, | ||
uint64 _from, | ||
uint64 _to | ||
) public view returns (Node[] memory nodesList) { | ||
_from = _from > 0 ? _from : 1; | ||
_to = _to <= lastNodeId ? _to : lastNodeId; | ||
require(_from <= _to, "Invalid range of node IDs."); | ||
|
||
nodesList = new Node[](100); | ||
bakhshandeh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint64 n = 0; | ||
for (uint64 i = _from; i <= _to && n < 100; i++) { | ||
Node memory node = nodes[i]; | ||
|
||
if (node.lastEditTime > _lastEditTime) { | ||
nodesList[n] = node; | ||
nodesList[n].roles = getNodeRoles(i); | ||
n++; | ||
} | ||
} | ||
|
||
// Resize the array to remove any unused elements | ||
assembly { | ||
mstore(nodesList, n) | ||
} | ||
} | ||
|
||
/** | ||
* @dev Returns the information of a node associated with the provided node address. | ||
* @param _addr The node address. | ||
* @return node The node information. | ||
*/ | ||
function nodeAddressInfo(address _addr) | ||
public | ||
view | ||
returns (Node memory node) | ||
{ | ||
node = nodes[nodeAddressIds[_addr]]; | ||
} | ||
|
||
/** | ||
* @dev Returns the information of a node associated with the provided staker address. | ||
* @param _addr The staker address. | ||
* @return node The node information. | ||
*/ | ||
function stakerAddressInfo(address _addr) | ||
public | ||
view | ||
override | ||
returns (Node memory node) | ||
{ | ||
node = nodes[stakerAddressIds[_addr]]; | ||
} | ||
|
||
/** | ||
* @dev Returns the tier of a node. | ||
* @param nodeId The ID of the node. | ||
* @return The tier of the node. | ||
*/ | ||
function getTier(uint64 nodeId) external view override returns (uint64) { | ||
return nodes[nodeId].tier; | ||
} | ||
|
||
/** | ||
* @dev Sets the tier of a node. | ||
* Only callable by the DAO_ROLE. | ||
* @param nodeId The ID of the node. | ||
* @param tier The tier to set. | ||
*/ | ||
function setTier(uint64 nodeId, uint64 tier) public onlyRole(DAO_ROLE) { | ||
bakhshandeh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
nodes[nodeId].tier = tier; | ||
bakhshandeh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
emit TierSet(nodeId, tier); | ||
} | ||
|
||
/** | ||
* @dev Sets a configuration value. | ||
* Only callable by the DAO_ROLE. | ||
* @param key The key of the configuration value. | ||
* @param val The value to be set. | ||
*/ | ||
function setConfig(string memory key, string memory val) | ||
public | ||
onlyRole(DAO_ROLE) | ||
{ | ||
configs[key] = val; | ||
emit ConfigSet(key, val); | ||
} | ||
|
||
/** | ||
* @dev Adds a new role. | ||
* Only callable by the DAO_ROLE. | ||
* @param role The role to be added. | ||
*/ | ||
function addNodeRole(bytes32 role) public onlyRole(DAO_ROLE) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not use Muon node can define the roles and just save the ids in the contract. |
||
require(roleIds[role] == 0, "This role has already been added."); | ||
|
||
lastRoleId++; | ||
roleIds[role] = lastRoleId; | ||
emit NodeRoleAdded(role, lastRoleId); | ||
} | ||
|
||
bakhshandeh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// ======== Events ======== | ||
event NodeAdded(uint64 indexed nodeId, Node node); | ||
event NodeDeactivated(uint64 indexed nodeId); | ||
event ConfigSet(string indexed key, string value); | ||
event NodeRoleAdded(bytes32 indexed role, uint64 roleId); | ||
event NodeRoleSet(uint64 indexed nodeId, uint64 indexed roleId); | ||
event NodeRoleUnset(uint64 indexed nodeId, uint64 indexed roleId); | ||
event TierSet(uint64 indexed nodeId, uint64 indexed tier); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a function to let the ADMIN activate a node. Otherwise, a node could be deactivated and its staked amount will be locked
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's allow the admin to decative a node via staking contract