diff --git a/contracts/src/Factory.sol b/contracts/src/Factory.sol index 49da8c1..4879a67 100644 --- a/contracts/src/Factory.sol +++ b/contracts/src/Factory.sol @@ -12,7 +12,7 @@ contract Factory { mapping(address => bool) public isDeployedTNT; mapping(address => mapping(address => bool)) private userHasTNT; - event TNTCreated(address indexed owner, address tntAddress); + event TNTCreated(address indexed owner, address indexed tntAddress); event TokenRegistered(address indexed user, address indexed tntAddress); event TokenUnregistered(address indexed user, address indexed tntAddress); diff --git a/contracts/src/TNT.sol b/contracts/src/TNT.sol index 1b78e48..1765be3 100644 --- a/contracts/src/TNT.sol +++ b/contracts/src/TNT.sol @@ -35,8 +35,8 @@ contract TNT is ERC721, AccessControl { mapping(uint256 => TokenMetadata) public metadata; - event TokenIssued(address indexed issuer, address indexed user, uint256 tokenId); - event TokenRevoked(address indexed revoker, uint256 tokenId); + event TokenIssued(address indexed issuer, address indexed user, uint256 indexed tokenId); + event TokenRevoked(address indexed revoker, uint256 indexed tokenId); constructor( address admin, diff --git a/hardhat.config.js b/hardhat.config.js index b63f0c2..8020dbd 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -2,5 +2,16 @@ require("@nomicfoundation/hardhat-toolbox"); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { - solidity: "0.8.28", -}; + solidity: "0.8.20", + paths: { + sources: "./contracts/src", + tests: "./test", + cache: "./cache", + artifacts: "./artifacts" + }, + networks: { + hardhat: { + allowUnlimitedContractSize: true + } + } +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f7b9216..337f3c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,11 +6,11 @@ "": { "name": "hardhat-project", "dependencies": { - "@openzeppelin/contracts": "^5.2.0", "openzeppelin": "^1.0.0" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@openzeppelin/contracts": "^5.2.0", "hardhat": "^2.22.17" } }, @@ -1512,7 +1512,9 @@ "node_modules/@openzeppelin/contracts": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.2.0.tgz", - "integrity": "sha512-bxjNie5z89W1Ea0NZLZluFh8PrFNn9DH8DQlujEok2yjsOlraUPKID5p1Wk3qdNbf6XkQ1Os2RvfiHrrXLHWKA==" + "integrity": "sha512-bxjNie5z89W1Ea0NZLZluFh8PrFNn9DH8DQlujEok2yjsOlraUPKID5p1Wk3qdNbf6XkQ1Os2RvfiHrrXLHWKA==", + "dev": true, + "license": "MIT" }, "node_modules/@scure/base": { "version": "1.1.9", diff --git a/package.json b/package.json index 372c68c..29b0cb7 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "hardhat-project", "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@openzeppelin/contracts": "^5.2.0", "hardhat": "^2.22.17" }, "dependencies": { - "@openzeppelin/contracts": "^5.2.0", "openzeppelin": "^1.0.0" } } diff --git a/test/EventIndexing.test.js b/test/EventIndexing.test.js new file mode 100644 index 0000000..7b999fd --- /dev/null +++ b/test/EventIndexing.test.js @@ -0,0 +1,124 @@ +const { expect } = require("chai"); +const { ethers } = require("hardhat"); + +describe("Event Indexing Validation", function () { + let factory; + let tnt; + let owner, user; + + before(async function () { + [owner, user] = await ethers.getSigners(); + + // Deploy Factory + const FactoryContract = await ethers.getContractFactory("Factory"); + factory = await FactoryContract.deploy(); + await factory.waitForDeployment(); + + console.log("\n✅ Factory deployed successfully"); + }); + + it("✅ TNTCreated event has indexed tntAddress", async function () { + const tx = await factory.createTNT("TestToken", "TST", true, ""); + const receipt = await tx.wait(); + + // Parse the event + const factoryInterface = factory.interface; + const eventLogs = receipt.logs.map(log => { + try { + return factoryInterface.parseLog(log); + } catch (e) { + return null; + } + }).filter(Boolean); + + const tntCreatedEvent = eventLogs.find(e => e.name === "TNTCreated"); + expect(tntCreatedEvent).to.not.be.undefined; + + // Check indexed parameters + // topics[0] = event signature hash + // topics[1] = indexed owner + // topics[2] = indexed tntAddress (NEW!) + const eventLog = receipt.logs.find(log => + log.topics[0] === factoryInterface.getEvent("TNTCreated").topicHash + ); + + console.log(" Event topics count:", eventLog.topics.length); + console.log(" ✓ tntAddress is now indexed (topic 2)"); + + expect(eventLog.topics.length).to.equal(3, "Should have 3 topics (signature + 2 indexed params)"); + + // Get TNT contract address for next tests + const tntAddress = tntCreatedEvent.args[1]; + tnt = await ethers.getContractAt("TNT", tntAddress); + }); + + it("✅ TokenIssued event has indexed tokenId", async function () { + // Grant minter role and issue a token + await tnt.grantMinterRole(owner.address); + const tx = await tnt.issueToken(user.address); + const receipt = await tx.wait(); + + const tntInterface = tnt.interface; + const eventLog = receipt.logs.find(log => + log.topics[0] === tntInterface.getEvent("TokenIssued").topicHash + ); + + console.log(" Event topics count:", eventLog.topics.length); + console.log(" ✓ tokenId is now indexed (topic 3)"); + + // topics[0] = event signature + // topics[1] = indexed issuer + // topics[2] = indexed user + // topics[3] = indexed tokenId (NEW!) + expect(eventLog.topics.length).to.equal(4, "Should have 4 topics (signature + 3 indexed params)"); + }); + + it("✅ TokenRevoked event has indexed tokenId", async function () { + // Create a revokable TNT and issue a token + const tx1 = await factory.createTNT("RevokableToken", "RVK", true, ""); + const receipt1 = await tx1.wait(); + + const factoryInterface = factory.interface; + const eventLogs = receipt1.logs.map(log => { + try { + return factoryInterface.parseLog(log); + } catch (e) { + return null; + } + }).filter(Boolean); + + const tntCreatedEvent = eventLogs.find(e => e.name === "TNTCreated"); + const revokableTNT = await ethers.getContractAt("TNT", tntCreatedEvent.args[1]); + + // Issue token + await revokableTNT.grantMinterRole(owner.address); + await revokableTNT.grantRevokerRole(owner.address); + await revokableTNT.issueToken(user.address); + + // Revoke token + const tx2 = await revokableTNT.revokeToken(0); + const receipt2 = await tx2.wait(); + + const tntInterface = revokableTNT.interface; + const eventLog = receipt2.logs.find(log => + log.topics[0] === tntInterface.getEvent("TokenRevoked").topicHash + ); + + console.log(" Event topics count:", eventLog.topics.length); + console.log(" ✓ tokenId is now indexed (topic 2)"); + + // topics[0] = event signature + // topics[1] = indexed revoker + // topics[2] = indexed tokenId (NEW!) + expect(eventLog.topics.length).to.equal(3, "Should have 3 topics (signature + 2 indexed params)"); + }); + + it("✅ Gas cost comparison", async function () { + // This test shows the gas cost increase from indexing + const tx = await factory.createTNT("GasTest", "GAS", true, ""); + const receipt = await tx.wait(); + + console.log("\n Gas used for TNT creation:", receipt.gasUsed.toString()); + console.log(" Note: +375 gas per indexed parameter is negligible"); + }); +}); diff --git a/test/TNT.test.js b/test/TNT.test.js index 3d5058f..d1f50f5 100644 --- a/test/TNT.test.js +++ b/test/TNT.test.js @@ -19,7 +19,7 @@ describe("TNT and Factory Contracts", function () { TNT = TNTContract; // Deploy revokable TNT - let tx = await factory.createTNT("TestToken", "TTK", true); + let tx = await factory.createTNT("TestToken", "TTK", true, ""); let receipt = await tx.wait(); let event = receipt.logs.find( (log) => 'fragment' in log && log.fragment.name === 'TNTCreated' @@ -28,7 +28,7 @@ describe("TNT and Factory Contracts", function () { tnt = await ethers.getContractAt("TNT", tntAddress); // Deploy non-revokable TNT - tx = await factory.createTNT("NonRevokableToken", "NRT", false); + tx = await factory.createTNT("NonRevokableToken", "NRT", false, ""); receipt = await tx.wait(); event = receipt.logs.find( (log) => 'fragment' in log && log.fragment.name === 'TNTCreated'