Skip to content

Commit

Permalink
reintroduce registries contract
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick95550 committed Jun 13, 2024
1 parent efb8acd commit f80643d
Show file tree
Hide file tree
Showing 10 changed files with 559 additions and 22 deletions.
10 changes: 10 additions & 0 deletions common/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const DeployedContractNames = {
seekerStatsOracle: 'SeekerStatsOracle',
seekerStakingManager: 'SeekerStakingManager',
protocolTimeManager: 'ProtocolTimeManager',
registries: 'Registries',
};

export const ContractNames = {
Expand All @@ -24,6 +25,7 @@ export type SyloContracts = {
seekerStakingManager: factories.contracts.staking.seekers.SeekerStakingManager;
seekers: factories.contracts.mocks.TestSeekers;
protocolTimeManager: factories.contracts.ProtocolTimeManager;
registries: factories.contracts.Registries;
};

export type ContractAddresses = {
Expand All @@ -33,6 +35,8 @@ export type ContractAddresses = {
seekerStakingManager: string;
seekers: string;
protocolTimeManager: string;
seekerPowerOracle: string;
registries: string;
};

export function connectContracts(
Expand Down Expand Up @@ -69,12 +73,18 @@ export function connectContracts(
provider,
);

const registries = factories.Registries__factory.connect(
contracts.registries,
provider,
);

return {
syloToken,
syloStakingManager,
seekerStatsOracle,
seekerStakingManager,
seekers,
protocolTimeManager,
registries,
};
}
32 changes: 32 additions & 0 deletions contracts/IRegistries.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;

interface IRegistries {
struct Registry {
// Percentage of a tickets value that will be rewarded to
// delegated stakers expressed as a fraction of 100000.
// This value is currently locked to the default payout percentage
// until epochs are implemented.
uint32 payoutPercentage;
// Public http/s endpoint to retrieve additional metadata
// about the node.
// The current metadata schema is as follows:
// { name: string, multiaddrs: string[] }
string publicEndpoint;
}

function register(string calldata publicEndpoint) external;

function setDefaultPayoutPercentage(uint32 _defaultPayoutPercentage) external;

function getRegistry(address account) external view returns (Registry memory);

function getNodes() external view returns (address[] memory);

function getRegistries(
uint256 start,
uint256 end
) external view returns (address[] memory, Registry[] memory);

function getTotalNodes() external view returns (uint256);
}
4 changes: 3 additions & 1 deletion contracts/ProtocolTimeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ contract ProtocolTimeManager is
* `interfaceId` from ERC165.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IProtocolTimeManager).interfaceId;
return
interfaceId == type(IProtocolTimeManager).interfaceId ||
super.supportsInterface(interfaceId);
}

/**
Expand Down
177 changes: 177 additions & 0 deletions contracts/Registries.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

import "./IRegistries.sol";

/**
* @notice This contract manages Registries for Nodes. A Registry is a
* set of parameters configured by the Node itself. A Node is required
* to have a valid Registry to be able to participate in the network.
*/
contract Registries is IRegistries, Initializable, Ownable2StepUpgradeable, ERC165 {
using ECDSA for bytes32;

/**
* @notice Tracks each Node's registry.
*/
mapping(address => IRegistries.Registry) public registries;

/**
* @notice Tracks the address of every registered node.
*/
address[] public nodes;

/**
* @notice Payout percentage refers to the portion of a tickets reward
* that will be allocated to the Node's stakers. This is global, and is
* currently set for all Nodes.
*/
uint32 public defaultPayoutPercentage;

event DefaultPayoutPercentageUpdated(uint32 defaultPayoutPercentage);

error EndMustBeGreaterThanStart();
error PercentageCannotExceed100000();
error PublicEndpointCannotBeEmpty();
error EndCannotExceedNumberOfNodes(uint256 nodeLength);

function initialize(uint32 _defaultPayoutPercentage) external initializer {
if (_defaultPayoutPercentage > 100000) {
revert PercentageCannotExceed100000();
}

Ownable2StepUpgradeable.__Ownable2Step_init();

defaultPayoutPercentage = _defaultPayoutPercentage;
}

/**
* @notice Returns true if the contract implements the interface defined by
* `interfaceId` from ERC165.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return
interfaceId == type(IRegistries).interfaceId || super.supportsInterface(interfaceId);
}

/**
* @notice Set the global default payout percentage value. Only callable
* by the owner.
* @param _defaultPayoutPercentage The payout percentage as a value where the
* denominator is 100000.
*/
function setDefaultPayoutPercentage(uint32 _defaultPayoutPercentage) external onlyOwner {
if (_defaultPayoutPercentage > 100000) {
revert PercentageCannotExceed100000();
}

defaultPayoutPercentage = _defaultPayoutPercentage;
emit DefaultPayoutPercentageUpdated(_defaultPayoutPercentage);
}

/**
* @notice Call this as a Node to set or update your Registry entry.
* @param publicEndpoint The public endpoint of your Node. Essential for
* clients to be able to retrieve additional information, such as
* an address to establish a p2p connection.
*/
function register(string calldata publicEndpoint) external {
if (bytes(publicEndpoint).length == 0) {
revert PublicEndpointCannotBeEmpty();
}

// This is the nodes first registration
if (bytes(registries[msg.sender].publicEndpoint).length == 0) {
nodes.push(msg.sender);
}

registries[msg.sender].publicEndpoint = publicEndpoint;
}

/**
* @notice Retrieve the registry associated with a Node.
* @param account The address of the Node.
* @return The Node's Registry.
*/
function getRegistry(address account) external view returns (Registry memory) {
return registries[account];
}

/**
* @notice Retrieve all registered nodes.
* @return An array of node addresses.
*/
function getNodes() external view returns (address[] memory) {
return nodes;
}

/**
* @notice Retrieves a list of registries. Takes in a
* a start and end indices to allow pagination.
* @param start The start index which is inclusive.
* @param end The end index which is exclusive.
* @return An array of Registries.
*/
function getRegistries(
uint256 start,
uint256 end
) external view returns (address[] memory, Registry[] memory) {
uint256 nodesLength = nodes.length;

if (end <= start) {
revert EndMustBeGreaterThanStart();
}
if (end > nodesLength) {
revert EndCannotExceedNumberOfNodes(nodesLength);
}

address[] memory _nodes = new address[](end - start);
Registry[] memory _registries = new Registry[](_nodes.length);

for (uint256 i = start; i < end; ++i) {
_nodes[i - start] = nodes[i];
_registries[i - start] = registries[nodes[i]];
}

return (_nodes, _registries);
}

/**
* @notice Returns the total number of registered nodes.
* @return The number of registered nodes.
*/
function getTotalNodes() external view returns (uint256) {
return nodes.length;
}

/**
* @notice Helper function for deriving the proof message used to
* validate seeker ownership.
* @param seekerId The tokenId of the seeker used for operation.
* @param node The address of the node which that will be operated
* by the specified seeker.
* @param nonce The nonce used for this message.
*/
function getProofMessage(
uint256 seekerId,
address node,
bytes32 nonce
) public pure returns (bytes memory) {
return
abi.encodePacked(
unicode"🤖 Hi frend! 🤖\n\n📜 Signing this message proves that you're the owner of this Seeker NFT and allows your Seeker to be used to operate your Seeker's Node. It's a simple but important step to ensure smooth operation.\n\nThis request will not trigger a blockchain transaction or cost any gas fees.\n\n🔥 Your node's address: ",
Strings.toHexString(uint256(uint160(node)), 20),
unicode"\n\n🆔 Your seeker id: ",
Strings.toString(seekerId),
unicode"\n\n📦 A unique random value which secures this message: ",
Strings.toHexString(uint256(nonce), 32)
);
}
}
32 changes: 16 additions & 16 deletions contracts/staking/sylo/SyloStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ contract SyloStakingManager is
* `interfaceId` from ERC165.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(ISyloStakingManager).interfaceId;
return
interfaceId == type(ISyloStakingManager).interfaceId ||
super.supportsInterface(interfaceId);
}

function setUnlockDuration(uint256 _unlockDuration) external onlyOwner {
Expand Down Expand Up @@ -116,8 +118,6 @@ contract SyloStakingManager is

// update total managed stake for this node
stakes[node].totalManagedStake += amount;


}

/**
Expand Down Expand Up @@ -239,24 +239,24 @@ contract SyloStakingManager is
* @param amount The amount of stake to transfer in SOLO.
*/
function transferStake(address from, address to, uint256 amount) external {
if (from == address(0)) {
revert NodeAddressCannotBeNil();
}
if (from == address(0)) {
revert NodeAddressCannotBeNil();
}

if (to == address(0)) {
revert NodeAddressCannotBeNil();
}
if (to == address(0)) {
revert NodeAddressCannotBeNil();
}

StakeEntry storage stakeEntry = stakes[from].entries[msg.sender];
StakeEntry storage stakeEntry = stakes[from].entries[msg.sender];

if (amount > stakeEntry.amount) {
revert CannotTransferMoreThanStaked(stakeEntry.amount, amount);
}
if (amount > stakeEntry.amount) {
revert CannotTransferMoreThanStaked(stakeEntry.amount, amount);
}

stakeEntry.amount -= amount;
stakeEntry.updatedAt = block.timestamp;
stakeEntry.amount -= amount;
stakeEntry.updatedAt = block.timestamp;

_addStake(to, amount);
_addStake(to, amount);
}

function getManagedStake(
Expand Down
18 changes: 17 additions & 1 deletion test/protocolTimeManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ describe('Protocol time manager', () => {
assert.equal(Number(currentPeriodFour), 185);
});

it('supports only protocol time manager interface', async () => {
it('protocol time manager supports correct interfaces', async () => {
const abi = [
'function setProtocolStart(uint256 _start) external',
'function setCycleDuration(uint256 duration) external',
Expand All @@ -284,6 +284,22 @@ describe('Protocol time manager', () => {
'Expected protocol time manager to support correct interface',
);

const abiERC165 = [
'function supportsInterface(bytes4 interfaceId) external view returns (bool)',
];

const interfaceIdERC165 = getInterfaceId(abiERC165);

const supportsERC165 = await protocolTimeManager.supportsInterface(
interfaceIdERC165,
);

assert.equal(
supportsERC165,
true,
'Expected protocol time manager to support ERC165',
);

const invalidAbi = ['function foo(uint256 duration) external'];

const invalidAbiInterfaceId = getInterfaceId(invalidAbi);
Expand Down
Loading

0 comments on commit f80643d

Please sign in to comment.