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

feat: upload the files #29

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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 Jun 27, 2023
e93be66
fix: fix an issue
siftal Jun 28, 2023
12810e6
fix: remove an extra check
siftal Jun 28, 2023
4887e77
feat: refactor lockToBondedToken to restrict usage to NFT stakers
siftal Jun 28, 2023
26d3e41
feat: refactor mergeBondedTokens to restrict usage to NFT stakers
siftal Jun 28, 2023
4d3403a
feat: remove tier mapping and add tier field to the Node struct
siftal Jun 30, 2023
350da1c
feat: change tier type from uint64 to uint8
siftal Jul 4, 2023
0a0db3f
feat: remove unnecessary getTier function
siftal Jul 4, 2023
7c4920a
fix: fix an issue
siftal Jul 4, 2023
d14feb9
feat: remove unnecessary check
siftal Jul 4, 2023
518dd50
fix: fix an issue
siftal Jul 4, 2023
eacd683
feat: change minStakeAmountPerNode to minStakeAmount
siftal Jul 5, 2023
ad192c9
feat: add setMuonNodeTire function
siftal Jul 5, 2023
01ddb15
feat: remove unnecessary check
siftal Jul 5, 2023
7e64a40
feat: set balance to zero on node exit
siftal Jul 8, 2023
2abbcee
feat: calculate not paid rewards and redistribute them in the next di…
siftal Jul 8, 2023
585f9af
style: format the code
siftal Jul 8, 2023
c62cd70
feat: add getInfo function
siftal Jul 8, 2023
5c60340
test: update tests
siftal Jul 12, 2023
ee75a25
feat: add a new getEditedNodes function
siftal Jul 17, 2023
c5c1506
feat: let DAO_ROLE to deactivate a node
siftal Jul 17, 2023
edcfd0b
feat: check the balance of the node before withdrawing the stake
siftal Jul 17, 2023
46e3368
fix: add nodes' roles
siftal Jul 18, 2023
4e7b630
feat: get response length as a parameter
siftal Jul 18, 2023
c46dd01
fix: fix an issue
siftal Jul 18, 2023
e04c7da
feat: to improve performance break the loop instead of checking the c…
siftal Jul 18, 2023
27e6e6b
feat: make the nodesList size dynamic
siftal Jul 18, 2023
012bd68
test: update tests
siftal Jul 18, 2023
2cf81c3
feat: ables DAO to pause/unpause specific functions
siftal Jul 22, 2023
3420da0
test: update tests
siftal Jul 22, 2023
c1267aa
feat: use IERC20Upgradeable instead of IERC20
siftal Jul 22, 2023
245d8a0
fix: remove an extra import
siftal Jul 22, 2023
c1bbf8c
feat: add constant for reward period
siftal Jul 22, 2023
6797572
feat: check the NFT is received
siftal Jul 22, 2023
3b3dbdd
feat: write state variables before the call
siftal Jul 22, 2023
ac0b9db
feat: add onERC721Received function
siftal Jul 23, 2023
7e3bcc7
feat: check the node exists before setting the tier
siftal Jul 23, 2023
11531bf
feat: check the node exists before setting the tier
siftal Jul 23, 2023
b1361a8
feat: update messages
siftal Jul 23, 2023
ea5925a
style: clean the code
siftal Jul 23, 2023
74de8f4
test: update tests
siftal Jul 23, 2023
5e59c8a
feat: combine lockStake and unlockStake
siftal Jul 23, 2023
12c7c18
feat: use safeTransferFrom instead of transferFrom
siftal Jul 23, 2023
544b829
feat: combine pauseFunction and unpauseFunction
siftal Jul 25, 2023
fce9c5e
fix: fix a typo
siftal Aug 14, 2023
64e6044
fix: fix a typo
siftal Aug 14, 2023
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
354 changes: 354 additions & 0 deletions contracts/MuonNodeManager.sol
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)
Copy link
Member Author

@bakhshandeh bakhshandeh Jun 30, 2023

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

Copy link
Member Author

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

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) {
Copy link
Member Author

Choose a reason for hiding this comment

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

We do not use byte32 role anywhere.
Is it safe to remove it?

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);
}
Loading