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(contracts): add eas registry contract #259

Merged
merged 1 commit into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,9 @@ jobs:
run: |
pnpm install

- name: Build
run: |
pnpm build

- name: ${{ matrix.command }}
run: pnpm run ${{ matrix.command }}
1 change: 1 addition & 0 deletions .github/workflows/contracts-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
- name: Build
run: |
pnpm run build
working-directory: packages/contracts

- name: Test
run: pnpm run test
Expand Down
3 changes: 3 additions & 0 deletions packages/contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ GAS_PRICE=
FORKING_BLOCK_NUM=
# Hardhat logging level (true/false)
HARDHAT_LOGGING=
# Block gas limit
BLOCK_GAS_LIMIT=

1 change: 1 addition & 0 deletions packages/contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ deployed-contracts.json
cache
artifacts
typechain-types
coverage.json

35 changes: 35 additions & 0 deletions packages/contracts/contracts/interfaces/IEAS.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title IEAS
/// @notice An interface to the EAS contract.
interface IEAS {
/// @notice A struct representing a single attestation.
struct Attestation {
// A unique identifier of the attestation.
bytes32 uid;
// The unique identifier of the schema.
bytes32 schema;
// The time when the attestation was created (Unix timestamp).
uint64 time;
// The time when the attestation expires (Unix timestamp).
uint64 expirationTime;
// The time when the attestation was revoked (Unix timestamp).
uint64 revocationTime;
// The UID of the related attestation.
bytes32 refUID;
// The recipient of the attestation.
address recipient;
// The attester/sender of the attestation.
address attester;
// Whether the attestation is revocable.
bool revocable;
// Custom attestation data.
bytes data;
}

/// Get an attestation by its unique identifier.
/// @param uid The unique identifier of the attestation.
/// @return attestation The attestation.
function getAttestation(bytes32 uid) external view returns (Attestation memory);
}
20 changes: 14 additions & 6 deletions packages/contracts/contracts/interfaces/IRecipientRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,24 @@ pragma solidity ^0.8.20;
interface IRecipientRegistry {
/// @notice A struct representing a recipient
struct Recipient {
// recipient id (optional)
bytes32 id;
// recipient metadata url
bytes32 metadataUrl;
// recipient address
address recipient;
}

/// @notice Events
event RecipientAdded(uint256 indexed index, bytes32 id, bytes32 indexed metadataUrl, address indexed recipient);
event RecipientRemoved(uint256 indexed index, bytes32 id, address indexed recipient);
event RecipientChanged(uint256 indexed index, bytes32 id, bytes32 indexed metadataUrl, address indexed newAddress);

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

/// @notice Get a registry metadata url
/// @return The metadata url in bytes32 format
function getRegistryMetadataUrl() external view returns (bytes32);
Expand All @@ -27,7 +39,7 @@ interface IRecipientRegistry {

/// @notice Change a recipient
/// @param index The index of the recipient
function changeRecipient(uint256 index, Recipient calldata recipient) external view;
function changeRecipient(uint256 index, Recipient calldata recipient) external;

/// @notice Get a recipient
/// @param index The index of the recipient
Expand All @@ -38,11 +50,7 @@ interface IRecipientRegistry {
/// @return The max number of recipients
function maxRecipients() external view returns (uint256);

/// @notice Set the max number of recipients
/// @return The max number of recipients
function setMaxRecipients(uint256 maxRecipients) external returns (uint256);

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

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

/// @title MockEAS
/// @notice A mock contract to test the EASGatekeeper
contract MockEAS is IEAS {
address public immutable attester;
bytes32 public immutable schema;
address public immutable recipient;

/// @param _attester The address of the attester
/// @param _schema The schema of the attestation
/// @param _recipient The recipient of the attestation
constructor(address _attester, bytes32 _schema, address _recipient) {
attester = _attester;
schema = _schema;
recipient = _recipient;
}

/// @inheritdoc IEAS
function getAttestation(bytes32 attestationId) external view override returns (Attestation memory) {
// revoked
if (attestationId == 0x0000000000000000000000000000000000000000000000000000000000000001) {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: schema,
time: 0,
expirationTime: 0,
revocationTime: 1,
refUID: "0x000000000000000000000000000001",
recipient: recipient,
attester: attester,
revocable: true,
data: ""
});
// valid
} else {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: schema,
time: 0,
expirationTime: 0,
revocationTime: 0,
refUID: "0x000000000000000000000000000001",
recipient: recipient,
attester: attester,
revocable: false,
data: ""
});
}
}
}
88 changes: 88 additions & 0 deletions packages/contracts/contracts/registry/BaseRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

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

/// @inheritdoc IRecipientRegistry
uint256 public immutable maxRecipients;

/// @inheritdoc IRecipientRegistry
uint256 public recipientCount;

/// @notice The registry metadata url
bytes32 public immutable metadataUrl;

/// @notice Create a new instance of the registry contract
/// @param _maxRecipients The maximum number of recipients that can be registered
/// @param _metadataUrl The metadata url
constructor(uint256 _maxRecipients, bytes32 _metadataUrl) payable {
maxRecipients = _maxRecipients;
metadataUrl = _metadataUrl;
}

/// @inheritdoc IRecipientRegistry
function getRegistryMetadataUrl() public view virtual override returns (bytes32) {
return metadataUrl;
}

/// @inheritdoc IRecipientRegistry
function addRecipient(Recipient calldata recipient) public virtual override returns (uint256) {
uint256 index = recipientCount;

if (index >= maxRecipients) {
revert MaxRecipientsReached();
}

if (recipient.recipient == address(0)) {
revert InvalidInput();
}

recipients[index] = recipient;
recipientCount++;

emit RecipientAdded(index, recipient.id, recipient.metadataUrl, recipient.recipient);

return index;
}

/// @inheritdoc IRecipientRegistry
function removeRecipient(uint256 index) public virtual override {
if (index >= recipientCount) {
revert InvalidIndex();
}

Recipient memory recipient = recipients[index];

delete recipients[index];
recipientCount--;

emit RecipientRemoved(index, recipient.id, recipient.recipient);
}

/// @inheritdoc IRecipientRegistry
function changeRecipient(uint256 index, Recipient calldata recipient) public virtual override {
if (index >= recipientCount) {
revert InvalidIndex();
}

if (recipient.recipient == address(0)) {
revert InvalidInput();
}

recipients[index].recipient = recipient.recipient;
recipients[index].metadataUrl = recipient.metadataUrl;

emit RecipientChanged(index, recipient.id, recipient.metadataUrl, recipient.recipient);
}

/// @inheritdoc IRecipientRegistry
function getRecipient(uint256 index) public view virtual override returns (Recipient memory) {
return recipients[index];
}
}
60 changes: 60 additions & 0 deletions packages/contracts/contracts/registry/EASRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

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

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
constructor(
uint256 _maxRecipients,
bytes32 _metadataUrl,
address _eas
) payable Ownable(msg.sender) BaseRegistry(_maxRecipients, _metadataUrl) {
eas = IEAS(_eas);
}

/// @notice Add multiple recipients to the registry
/// @param recipients The recipients
function addRecipients(Recipient[] calldata recipients) external onlyOwner {
uint256 length = recipients.length;

for (uint256 i = 0; i < length; ) {
addRecipient(recipients[i]);

unchecked {
i++;
}
}
}

/// @inheritdoc BaseRegistry
function addRecipient(Recipient calldata recipient) public override onlyOwner returns (uint256) {
return super.addRecipient(recipient);
}

/// @notice Edit the address of a project
/// @param index The index of the project to edit
/// @param recipient The new recipient
function changeRecipient(uint256 index, Recipient calldata recipient) public override onlyOwner {
super.changeRecipient(index, recipient);
}

/// @inheritdoc BaseRegistry
function removeRecipient(uint256 index) public override onlyOwner {
super.removeRecipient(index);
}

/// @inheritdoc IEAS
function getAttestation(bytes32 id) public view override returns (Attestation memory) {
return eas.getAttestation(id);
}
}
2 changes: 1 addition & 1 deletion packages/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { EChainId, ESupportedChains, getEtherscanApiKeys, getNetworkRpcUrls } fr

dotenv.config();

const DEFAULT_BLOCK_GAS_LIMIT = 30_000_000;
const DEFAULT_BLOCK_GAS_LIMIT = process.env.BLOCK_GAS_LIMIT ? Number(process.env.BLOCK_GAS_LIMIT) : 30_000_000;
const DEFAULT_GAS_MUL = 2;
const TEST_MNEMONIC = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat";
const NETWORKS_RPC_URL = getNetworkRpcUrls();
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@types/chai": "^4.3.11",
"@types/lowdb": "^1.0.15",
"@types/mocha": "^10.0.7",
"@types/node": "^22.1.0",
"@types/node": "^22.2.0",
"@types/snarkjs": "^0.7.8",
"@types/uuid": "^10.0.0",
"chai": "^4.3.10",
Expand Down
2 changes: 0 additions & 2 deletions packages/contracts/tasks/helpers/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ export enum EChainId {
Coverage = 1337,
}

export const STATE_TREE_ARITY = 5;

/**
* Get network rpc urls object
*
Expand Down
Loading
Loading