Skip to content

Commit

Permalink
feat(contracts): add registry manager
Browse files Browse the repository at this point in the history
- [x] Add RegistryManager contract
- [x] Add EASRegistryManager contract
- [x] Add Common contract with common errors
- [x] Minor refactoring
- [x] Add tests
  • Loading branch information
0xmad committed Aug 22, 2024
1 parent e72cfbb commit 1377d82
Show file tree
Hide file tree
Showing 15 changed files with 822 additions and 13 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/slither.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Slither Analysis

on:
push:
branches: [main]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
slither:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9

- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- name: Install
run: |
pnpm install --frozen-lockfile --prefer-offline
- name: Build
run: |
pnpm run build
working-directory: packages/contracts

- name: Run Slither
uses: crytic/[email protected]
continue-on-error: true
id: slither
with:
sarif: results.sarif
fail-on: none
ignore-compile: true
node-version: 20
target: "packages/contracts/"

- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.slither.outputs.sarif }}
12 changes: 11 additions & 1 deletion packages/contracts/.solcover.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { buildPoseidonT3, buildPoseidonT4, buildPoseidonT5, buildPoseidonT6 } = require("maci-contracts");
const { poseidonContract } = require("circomlibjs");
const hre = require("hardhat");
const fs = require("fs");
const path = require("path");

Expand All @@ -8,6 +9,15 @@ const PATHS = [
path.resolve(__dirname, "..", "typechain-types"),
];

const buildPoseidon = async (numInputs) => {
await hre.overwriteArtifact(`PoseidonT${numInputs + 1}`, poseidonContract.createCode(numInputs));
};

const buildPoseidonT3 = () => buildPoseidon(2);
const buildPoseidonT4 = () => buildPoseidon(3);
const buildPoseidonT5 = () => buildPoseidon(4);
const buildPoseidonT6 = () => buildPoseidon(5);

module.exports = {
onPreCompile: async () => {
await Promise.all(
Expand Down
12 changes: 12 additions & 0 deletions packages/contracts/contracts/common/Common.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title Common
/// @notice Contract that contains common things for all the contracts
contract Common {
/// @notice custom errors
error InvalidAddress();
error InvalidInput();
error InvalidIndex();
error ValidationError();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ interface IRecipientRegistry {

/// @notice Custom errors
error MaxRecipientsReached();
error InvalidIndex();
error InvalidInput();

/// @notice Get a registry metadata url
/// @return The metadata url in bytes32 format
Expand Down
84 changes: 84 additions & 0 deletions packages/contracts/contracts/interfaces/IRegistryManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IRecipientRegistry } from "./IRecipientRegistry.sol";

/// @title IRegistryManager
/// @notice An interface for a registry manager. Allows to manage requests for Registry.
interface IRegistryManager {
/// @notice Enum representing request type
enum RequestType {
Add,
Change
}

/// @notice Enum representing request status
enum Status {
Pending,
Approved,
Rejected
}

/// @notice Request data
struct Request {
/// @notice index (optional)
uint256 index;
/// @notice registry address
address registry;
/// @notice request type
RequestType requestType;
/// @notice recipient data
IRecipientRegistry.Recipient recipient;
}

/// @notice Events
event RequestSent(
address indexed registry,
RequestType indexed requestType,
address indexed recipient,
uint256 index,
bytes32 id,
bytes32 metadataUrl
);
event RequestApproved(
address indexed registry,
RequestType indexed requestType,
address indexed recipient,
uint256 index,
bytes32 id,
bytes32 metadataUrl
);
event RequestRejected(
address indexed registry,
RequestType indexed requestType,
address indexed recipient,
uint256 index,
bytes32 id,
bytes32 metadataUrl
);

/// @notice Custom errors
error OperationError();

/// @notice Send the request to the Registry
/// @param request user request
function send(Request calldata request) external;

/// @notice Approve the request and call registry function
/// @param index The index of the request
function approve(uint256 index) external;

/// @notice Reject the request
/// @param index The index of the request
function reject(uint256 index) external;

/// @notice Get a request
/// @param index The index of the request
/// @return request The request with index and data
/// @return status The request status
function getRequest(uint256 index) external view returns (Request memory request, Status status);

/// @notice Get the number of requests
/// @return The number of requests
function requestCount() external view returns (uint256);
}
13 changes: 13 additions & 0 deletions packages/contracts/contracts/mocks/MockRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { BaseRegistry } from "../registry/BaseRegistry.sol";

/// @title MockRegistry
/// @notice Mock registry contract
contract MockRegistry is BaseRegistry {
/// @notice Create a new instance of the registry contract
/// @param maxRecipients The maximum number of projects that can be registered
/// @param metadataUrl The metadata url
constructor(uint256 maxRecipients, bytes32 metadataUrl) payable BaseRegistry(maxRecipients, metadataUrl) {}

Check notice

Code scanning / Slither

Local variable shadowing Low

Check notice

Code scanning / Slither

Local variable shadowing Low

}

Check warning

Code scanning / Slither

Contracts that lock Ether Medium

Contract locking ether found:
Contract MockRegistry has payable functions:
- BaseRegistry.constructor(uint256,bytes32)
- MockRegistry.constructor(uint256,bytes32)
But does not have a function to withdraw the ether
3 changes: 2 additions & 1 deletion packages/contracts/contracts/registry/BaseRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { Common } from "../common/Common.sol";
import { IRecipientRegistry } from "../interfaces/IRecipientRegistry.sol";

/// @title BaseRegistry
/// @notice Base contract for a registry
abstract contract BaseRegistry is IRecipientRegistry {
abstract contract BaseRegistry is IRecipientRegistry, Common {
/// @notice The storage of recipients
mapping(uint256 => Recipient) internal recipients;

Expand Down
22 changes: 14 additions & 8 deletions packages/contracts/contracts/registry/EASRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,26 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IEAS } from "../interfaces/IEAS.sol";
import { BaseRegistry } from "./BaseRegistry.sol";

/// @title EASRegistry
/// @notice EAS registry contract
contract EASRegistry is Ownable, BaseRegistry, IEAS {
/// @notice The EAS contract
IEAS public immutable eas;

/// @notice Create a new instance of the registry contract
/// @param _maxRecipients The maximum number of projects that can be registered
/// @param _metadataUrl The metadata url
/// @param _eas The EAS address
/// @param maxRecipients The maximum number of projects that can be registered
/// @param metadataUrl The metadata url
/// @param easAddress The EAS address
constructor(
uint256 _maxRecipients,
bytes32 _metadataUrl,
address _eas
) payable Ownable(msg.sender) BaseRegistry(_maxRecipients, _metadataUrl) {
eas = IEAS(_eas);
uint256 maxRecipients,

Check notice

Code scanning / Slither

Local variable shadowing Low

bytes32 metadataUrl,

Check notice

Code scanning / Slither

Local variable shadowing Low

address easAddress
) payable Ownable(msg.sender) BaseRegistry(maxRecipients, metadataUrl) {
if (easAddress == address(0)) {
revert InvalidAddress();
}

eas = IEAS(easAddress);
}

/// @notice Add multiple recipients to the registry
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IEAS } from "../interfaces/IEAS.sol";
import { IRecipientRegistry } from "../interfaces/IRecipientRegistry.sol";

Check warning on line 5 in packages/contracts/contracts/registryManager/EASRegistryManager.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

imported name IRecipientRegistry is not used
import { RegistryManager } from "./RegistryManager.sol";

/// @title EASRegistryManager
/// @notice Contract that allows to use send, approve, reject requests to EASRegistry.
contract EASRegistryManager is RegistryManager {
/// @notice custom errors
error NotYourAttestation();

/// @notice EAS
IEAS public eas;

Check warning

Code scanning / Slither

State variables that could be declared immutable Warning

EASRegistryManager.eas should be immutable

/// @notice Initialize EASRegistryManager
/// @param easAddress EAS contract address
constructor(address easAddress) payable {
if (easAddress == address(0)) {
revert InvalidAddress();
}

eas = IEAS(easAddress);
}

/// @notice Check recipient has an EAS attestation
/// @param request request to the registry
modifier onlyWithAttestation(Request memory request) {
if (request.requestType != RequestType.Change) {
_;
return;
}

IEAS.Attestation memory attestation = eas.getAttestation(request.recipient.id);

if (attestation.recipient != request.recipient.recipient) {
revert NotYourAttestation();
}

_;
}

/// @inheritdoc RegistryManager
function send(
Request calldata request
) public virtual override isValidRequest(request) onlyWithAttestation(request) {
super.send(request);
}
}

Check warning

Code scanning / Slither

Contracts that lock Ether Medium

Contract locking ether found:
Contract EASRegistryManager has payable functions:
- RegistryManager.constructor()
- EASRegistryManager.constructor(address)
But does not have a function to withdraw the ether
Loading

0 comments on commit 1377d82

Please sign in to comment.