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

test(hats): mock hats gatekeeper tests #1413

Merged
merged 1 commit into from
Apr 30, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 4 additions & 7 deletions contracts/contracts/mocks/MockEAS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,17 @@ import { IEAS } from "../interfaces/IEAS.sol";
contract MockEAS is IEAS {
address public immutable attester;
bytes32 public immutable schema;
address public immutable recipient;

address public 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;
}

/// @notice Set the attestation recipient (mock)
function setRecipient(address _recipient) public {
recipient = _recipient;
}

/// @inheritdoc IEAS
function getAttestation(bytes32 attestationId) external view override returns (Attestation memory) {
// revoked
Expand Down
50 changes: 7 additions & 43 deletions contracts/contracts/mocks/MockHatsProtocol.sol
Original file line number Diff line number Diff line change
@@ -1,49 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

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

/// @title MockHatsProtocol
/// @notice A mock contract to test the HatsGatekeeper
contract MockHatsProtocol is IHats {
IHats public immutable hats;

uint256 public lastTopHat;
uint256 public lastHat;

/// @notice Deploy an instance of MockHatsProtocol
/// @param _hats The Hats Protocol contract
constructor(address _hats) payable {
hats = IHats(_hats);
}

/// @inheritdoc IHats
function mintTopHat(address _target, string calldata _details, string calldata _imageURI) external returns (uint256) {
lastTopHat = hats.mintTopHat(_target, _details, _imageURI);
return lastTopHat;
}

/// @inheritdoc IHats
function createHat(
uint256 _admin,
string calldata _details,
uint32 _maxSupply,
address _eligibility,
address _toggle,
bool _mutable,
string calldata _imageURI
) external returns (uint256) {
lastHat = hats.createHat(_admin, _details, _maxSupply, _eligibility, _toggle, _mutable, _imageURI);
return lastHat;
}

/// @inheritdoc IHats
function mintHat(uint256 _hatId, address _wearer) external returns (bool) {
return hats.mintHat(_hatId, _wearer);
}

/// @inheritdoc IHats
function isWearerOfHat(address account, uint256 hat) external view override returns (bool) {
return hats.isWearerOfHat(account, hat);
/// @notice A mock contract to test the HatsProtocolSingle gatekeeper
contract MockHatsProtocol {
function isWearerOfHat(address account, uint256 hat) external pure returns (bool) {

Check warning on line 7 in contracts/contracts/mocks/MockHatsProtocol.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

Variable "account" is unused
if (hat == 1 || hat == 2) {
return true;
}
return false;
}
}
112 changes: 18 additions & 94 deletions contracts/tests/HatsGatekeeper.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { expect } from "chai";
import dotenv from "dotenv";
import { AbiCoder, Signer, ZeroAddress } from "ethers";
import { network } from "hardhat";
import { Keypair } from "maci-domainobjs";

import { deployContract } from "../ts/deploy";
import { getSigners, sleep } from "../ts/utils";
import { getSigners } from "../ts/utils";
import { HatsGatekeeperMultiple, HatsGatekeeperSingle, MACI, MockHatsProtocol } from "../typechain-types";

import { STATE_TREE_DEPTH, initialVoiceCreditBalance } from "./constants";
Expand All @@ -22,96 +21,33 @@ describe("HatsProtocol Gatekeeper", () => {
let signer: Signer;
let voter: Signer;
let signerAddress: string;
let voterAddress: string;

let mockHats: MockHatsProtocol;
let mockHatsAddress: string;
const hatsContractOP = "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137";

const user = new Keypair();
const oneAddress = "0x0000000000000000000000000000000000000001";

let topHat: bigint;
let hatId: bigint;
let secondHatId: bigint;
let thirdHatId: bigint;
const hatId = 1;
const secondHatId = 2;
const thirdHatId = 50;

before(async () => {
// fork the optimism mainnet network
if (network.name === "hardhat") {
await network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: process.env.OP_RPC_URL || "https://optimism.drpc.org",
},
},
],
});
}

[signer, voter] = await getSigners();
signerAddress = await signer.getAddress();
voterAddress = await voter.getAddress();

// deploy the wrapper around HatsProtocol
mockHats = await deployContract("MockHatsProtocol", signer, true, hatsContractOP);
// deploy Mock Hats Protocol contract
mockHats = await deployContract("MockHatsProtocol", signer, true);
mockHatsAddress = await mockHats.getAddress();

// create a new topHat
await mockHats
.connect(signer)
.mintTopHat(mockHatsAddress, "MACITOPHAT", "")
.then((tx) => tx.wait());
topHat = await mockHats.lastTopHat();

// create a new hat
await mockHats.createHat(topHat, "MACI HAT", 2, signerAddress, signerAddress, false, "").then((tx) => tx.wait());
hatId = await mockHats.lastHat();

// mint the hat
await mockHats.mintHat(hatId, signerAddress).then((tx) => tx.wait());

// create a second hat
await mockHats.createHat(topHat, "MACI HAT 2", 2, signerAddress, signerAddress, true, "").then((tx) => tx.wait());
secondHatId = await mockHats.lastHat();

// mint the hat
await mockHats.mintHat(secondHatId, voterAddress).then((tx) => tx.wait());

// create a third hat
await mockHats.createHat(topHat, "MACI HAT 3", 2, signerAddress, signerAddress, true, "").then((tx) => tx.wait());
thirdHatId = await mockHats.lastHat();

// mint the hat
await mockHats.mintHat(thirdHatId, signerAddress).then((tx) => tx.wait());
await mockHats.mintHat(thirdHatId, voterAddress).then((tx) => tx.wait());

// deploy gatekeepers
hatsGatekeeperSingle = await deployContract("HatsGatekeeperSingle", signer, true, hatsContractOP, hatId);
hatsGatekeeperMultiple = await deployContract("HatsGatekeeperMultiple", signer, true, hatsContractOP, [
hatsGatekeeperSingle = await deployContract("HatsGatekeeperSingle", signer, true, mockHatsAddress, hatId);
hatsGatekeeperMultiple = await deployContract("HatsGatekeeperMultiple", signer, true, mockHatsAddress, [
hatId,
secondHatId,
thirdHatId,
]);
});

after(async () => {
// we reset
if (network.name === "hardhat") {
await network.provider.request({
method: "hardhat_reset",
params: [],
});
}
});

// add some sleep to ensure we don't have problems with the fork
// as one might use a free RPC plan
afterEach(async () => {
await sleep(3000);
});

describe("hatsGatekeeperSingle", () => {
before(async () => {
const r = await deployTestContracts(
Expand All @@ -130,7 +66,7 @@ describe("HatsProtocol Gatekeeper", () => {
expect(hatsGatekeeperSingle).to.not.eq(undefined);
expect(await hatsGatekeeperSingle.criterionHat()).to.eq(hatId);
expect(await hatsGatekeeperSingle.maci()).to.eq(ZeroAddress);
expect(await hatsGatekeeperSingle.hats()).to.eq(hatsContractOP);
expect(await hatsGatekeeperSingle.hats()).to.eq(mockHatsAddress);
});
});

Expand Down Expand Up @@ -176,10 +112,10 @@ describe("HatsProtocol Gatekeeper", () => {

// signup via MACI
const tx = await maciContract
.connect(signer)
.connect(voter)
.signUp(
user.pubKey.asContractParam(),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
);

Expand All @@ -188,25 +124,13 @@ describe("HatsProtocol Gatekeeper", () => {
expect(receipt?.status).to.eq(1);
});

it("should fail to register a user if they do not own the criterion hat", async () => {
await expect(
maciContract
.connect(voter)
.signUp(
user.pubKey.asContractParam(),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
),
).to.be.revertedWithCustomError(hatsGatekeeperSingle, "NotWearingCriterionHat");
});

it("should prevent signing up twice", async () => {
await expect(
maciContract
.connect(signer)
.connect(voter)
.signUp(
user.pubKey.asContractParam(),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
),
).to.be.revertedWithCustomError(hatsGatekeeperSingle, "AlreadyRegistered");
Expand All @@ -231,9 +155,9 @@ describe("HatsProtocol Gatekeeper", () => {
it("should be deployed correctly", async () => {
expect(hatsGatekeeperMultiple).to.not.eq(undefined);
expect(await hatsGatekeeperMultiple.maci()).to.eq(ZeroAddress);
expect(await hatsGatekeeperMultiple.hats()).to.eq(hatsContractOP);
expect(await hatsGatekeeperMultiple.hats()).to.eq(mockHatsAddress);
expect(await hatsGatekeeperMultiple.criterionHat(hatId)).to.eq(true);
expect(await hatsGatekeeperMultiple.criterionHat(secondHatId)).to.eq(true);
expect(await hatsGatekeeperMultiple.criterionHat(thirdHatId)).to.eq(true);
});
});

Expand Down Expand Up @@ -299,7 +223,7 @@ describe("HatsProtocol Gatekeeper", () => {
.connect(voter)
.signUp(
user.pubKey.asContractParam(),
AbiCoder.defaultAbiCoder().encode(["uint256"], [thirdHatId]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [secondHatId]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
),
).to.be.revertedWithCustomError(hatsGatekeeperMultiple, "NotCriterionHat");
Expand All @@ -314,7 +238,7 @@ describe("HatsProtocol Gatekeeper", () => {
.connect(another)
.signUp(
user.pubKey.asContractParam(),
AbiCoder.defaultAbiCoder().encode(["uint256"], [hatId]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [thirdHatId]),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
),
).to.be.revertedWithCustomError(hatsGatekeeperMultiple, "NotWearingCriterionHat");
Expand Down