diff --git a/contracts/GeyserRegistry.sol b/contracts/GeyserRegistry.sol new file mode 100644 index 0000000..b99da86 --- /dev/null +++ b/contracts/GeyserRegistry.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.24; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/// @title GeyserRegistry +contract GeyserRegistry is Ownable { + mapping(address => bool) public geysers; + + event InstanceAdded(address instance); + event InstanceRemoved(address instance); + + constructor() Ownable(msg.sender) {} + + function register(address instance) external onlyOwner { + require(!geysers[instance], "GeyserRegistry: Geyser already registered"); + geysers[instance] = true; + emit InstanceAdded(instance); + } + + function deregister(address instance) external onlyOwner { + require(geysers[instance], "GeyserRegistry: Geyser not registered"); + delete geysers[instance]; + emit InstanceRemoved(instance); + } +} diff --git a/test/registry.ts b/test/registry.ts new file mode 100644 index 0000000..7a283e6 --- /dev/null +++ b/test/registry.ts @@ -0,0 +1,112 @@ +import { ethers } from "hardhat"; +import { expect } from "chai"; +import { Contract } from "ethers"; + +// Generated by chat-gpt +describe("GeyserRegistry Contract", () => { + let GeyserRegistry: Contract; + let deployer: any; + let nonOwner: any; + let geyserAddress: string; + let unregisteredAddress: string; + + beforeEach(async () => { + [deployer, nonOwner] = await ethers.getSigners(); + geyserAddress = ethers.Wallet.createRandom().address; // Mock address + unregisteredAddress = ethers.Wallet.createRandom().address; // Mock address + + const GeyserRegistryFactory = await ethers.getContractFactory( + "GeyserRegistry", + deployer, + ); + GeyserRegistry = await GeyserRegistryFactory.deploy(); + }); + + describe("Deployment", () => { + it("should deploy with the correct owner", async () => { + const owner = await GeyserRegistry.owner(); + expect(owner).to.equal(deployer.address); + }); + + it("should have no geysers initially", async () => { + const isGeyser = await GeyserRegistry.geysers(geyserAddress); + expect(isGeyser).to.equal(false); + }); + }); + + describe("Registering a Geyser", () => { + it("should allow the owner to register a geyser", async () => { + const tx = await GeyserRegistry.register(geyserAddress); + await tx.wait(); + + const isGeyser = await GeyserRegistry.geysers(geyserAddress); + expect(isGeyser).to.equal(true); + }); + + it("should emit InstanceAdded when a geyser is registered", async () => { + await expect(GeyserRegistry.register(geyserAddress)) + .to.emit(GeyserRegistry, "InstanceAdded") + .withArgs(geyserAddress); + }); + + it("should revert if a non-owner tries to register", async () => { + await expect( + GeyserRegistry.connect(nonOwner).register(geyserAddress), + ).to.be.revertedWithCustomError(GeyserRegistry, "OwnableUnauthorizedAccount"); + }); + + it("should revert if the geyser is already registered", async () => { + await GeyserRegistry.register(geyserAddress); + await expect(GeyserRegistry.register(geyserAddress)).to.be.revertedWith( + "GeyserRegistry: Geyser already registered", + ); + }); + }); + + describe("Deregistering a Geyser", () => { + beforeEach(async function () { + await GeyserRegistry.register(geyserAddress); + }); + + it("should allow the owner to deregister a geyser", async () => { + const tx = await GeyserRegistry.deregister(geyserAddress); + await tx.wait(); + + const isGeyser = await GeyserRegistry.geysers(geyserAddress); + expect(isGeyser).to.equal(false); + }); + + it("should emit InstanceRemoved when a geyser is deregistered", async () => { + await expect(GeyserRegistry.deregister(geyserAddress)) + .to.emit(GeyserRegistry, "InstanceRemoved") + .withArgs(geyserAddress); + }); + + it("should revert if a non-owner tries to deregister", async () => { + await expect( + GeyserRegistry.connect(nonOwner).deregister(geyserAddress), + ).to.be.revertedWithCustomError(GeyserRegistry, "OwnableUnauthorizedAccount"); + }); + + it("should revert if the geyser is not registered", async () => { + await expect(GeyserRegistry.deregister(unregisteredAddress)).to.be.revertedWith( + "GeyserRegistry: Geyser not registered", + ); + }); + }); + + describe("Edge Cases", () => { + it("should not allow re-registering the same geyser address", async () => { + await GeyserRegistry.register(geyserAddress); + await expect(GeyserRegistry.register(geyserAddress)).to.be.revertedWith( + "GeyserRegistry: Geyser already registered", + ); + }); + + it("should revert when deregistering an unregistered address", async () => { + await expect(GeyserRegistry.deregister(unregisteredAddress)).to.be.revertedWith( + "GeyserRegistry: Geyser not registered", + ); + }); + }); +});