const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("AereBlockSTMRegistry", () => { let alice, bob, target; let reg; const SELECTOR = "0x12345678"; const SLOT_A = ethers.keccak256(ethers.toUtf8Bytes("slot-a")); const SLOT_B = ethers.keccak256(ethers.toUtf8Bytes("slot-b")); beforeEach(async () => { [alice, bob, target] = await ethers.getSigners(); const F = await ethers.getContractFactory("AereBlockSTMRegistry"); reg = await F.deploy(); await reg.waitForDeployment(); }); it("first publisher claims (target, selector); only they can update", async () => { // AUDIT FIX (HIGH #27): first publisher must be target itself OR target's owner. // We have `target` as an EOA so we use it as msg.sender (== target.address). await reg.connect(target).publish(target.address, SELECTOR, [ { slotPrefix: SLOT_A, mode: 0 /*READ*/ }, { slotPrefix: SLOT_B, mode: 1 /*WRITE*/ }, ]); expect(await reg.publisherOf(target.address, SELECTOR)).to.equal(target.address); // Random alice cannot squat someone else's (target, selector). await expect( reg.connect(alice).publish(target.address, SELECTOR, [{ slotPrefix: SLOT_A, mode: 0 }]) ).to.be.revertedWithCustomError(reg, "NotPublisher"); // Bob can't either. await expect( reg.connect(bob).publish(target.address, SELECTOR, [{ slotPrefix: SLOT_A, mode: 0 }]) ).to.be.revertedWithCustomError(reg, "NotPublisher"); }); it("hintsFor returns the latest set + count is correct", async () => { await reg.connect(target).publish(target.address, SELECTOR, [ { slotPrefix: SLOT_A, mode: 2 /*READWRITE*/ }, ]); const out = await reg.hintsFor(target.address, SELECTOR); expect(out.publisher).to.equal(target.address); expect(out.hints.length).to.equal(1); expect(out.hints[0].slotPrefix).to.equal(SLOT_A); expect(out.hints[0].mode).to.equal(2); }); it("publish replaces previous hints (not append)", async () => { await reg.connect(target).publish(target.address, SELECTOR, [ { slotPrefix: SLOT_A, mode: 0 }, { slotPrefix: SLOT_B, mode: 0 }, ]); await reg.connect(target).publish(target.address, SELECTOR, [ { slotPrefix: SLOT_A, mode: 1 }, ]); const out = await reg.hintsFor(target.address, SELECTOR); expect(out.hints.length).to.equal(1); expect(out.hints[0].mode).to.equal(1); }); it("rejects > 64 hints", async () => { const hints = []; for (let i = 0; i < 65; i++) hints.push({ slotPrefix: SLOT_A, mode: 0 }); await expect( reg.connect(target).publish(target.address, SELECTOR, hints) ).to.be.revertedWithCustomError(reg, "TooManyHints"); }); it("clear only by publisher; emits", async () => { await reg.connect(target).publish(target.address, SELECTOR, [{ slotPrefix: SLOT_A, mode: 0 }]); await expect(reg.connect(bob).clear(target.address, SELECTOR)).to.be.revertedWithCustomError(reg, "NotPublisher"); await expect(reg.connect(target).clear(target.address, SELECTOR)).to.emit(reg, "HintsCleared"); const out = await reg.hintsFor(target.address, SELECTOR); expect(out.hints.length).to.equal(0); }); });