Skip to content

Commit

Permalink
test(hats): mock hats gatekeeper tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ctrlc03 committed Apr 30, 2024
1 parent 945a985 commit b744b11
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 144 deletions.
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

0 comments on commit b744b11

Please sign in to comment.