From 034d8a12f4d43ec7871889b7a8c04cdf0d9f2618 Mon Sep 17 00:00:00 2001 From: "Alex.Karys" Date: Fri, 19 Jul 2024 20:31:02 +0200 Subject: [PATCH] First commit --- script/Counter.s.sol | 25 +++++----- src/Counter.sol | 14 ------ src/HookRegistry.sol | 102 ++++++++++++++++++++++++++++++++++++++++ test/Counter.t.sol | 24 ---------- test/HookRegistry.t.sol | 87 ++++++++++++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 50 deletions(-) delete mode 100644 src/Counter.sol create mode 100644 src/HookRegistry.sol delete mode 100644 test/Counter.t.sol create mode 100644 test/HookRegistry.t.sol diff --git a/script/Counter.s.sol b/script/Counter.s.sol index cdc1fe9..14729a4 100644 --- a/script/Counter.s.sol +++ b/script/Counter.s.sol @@ -1,19 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; -contract CounterScript is Script { - Counter public counter; +// import {Script, console} from "forge-std/Script.sol"; +// import {Counter} from "../src/Counter.sol"; - function setUp() public {} +// contract CounterScript is Script { +// Counter public counter; - function run() public { - vm.startBroadcast(); +// function setUp() public {} - counter = new Counter(); +// function run() public { +// vm.startBroadcast(); - vm.stopBroadcast(); - } -} +// counter = new Counter(); + +// vm.stopBroadcast(); +// } +// } diff --git a/src/Counter.sol b/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/src/HookRegistry.sol b/src/HookRegistry.sol new file mode 100644 index 0000000..a46e76b --- /dev/null +++ b/src/HookRegistry.sol @@ -0,0 +1,102 @@ +pragma solidity ^0.8.10; + +import "solmate/tokens/ERC721.sol"; +import "forge-std/console.sol"; + +contract HookRegistry is ERC721 { + uint256 private _tokenIdCounter; + + struct HookMetadata { + address hookAddress; + string ipfsMetadataHash; + address[] auditors; + } + + mapping(uint256 => HookMetadata) public hookMetadata; + mapping(uint256 => address) private hookOwners; + + event HookMinted(uint256 tokenId, address owner, string ipfsMetadataHash); + event MetadataUpdated(uint256 tokenId, string newIpfsMetadataHash); + event AuditorAdded(uint256 tokenId, address auditor); + + constructor() ERC721("HookRegistry", "HOOK") {} + + modifier onlyHookOwner(uint256 tokenId) { + require(msg.sender == hookOwners[tokenId], "Not the owner"); + _; + } + + function mintHookNFT( + address hookAddress, + string memory ipfsMetadataHash + ) external { + require(isContractDeployer(msg.sender, hookAddress), "Not the deployer of the hook"); + + uint256 tokenId = _tokenIdCounter++; + _mint(msg.sender, tokenId); + hookOwners[tokenId] = msg.sender; + + hookMetadata[tokenId] = HookMetadata({ + hookAddress: hookAddress, + ipfsMetadataHash: ipfsMetadataHash, + auditors: new address[](0) + }); + + emit HookMinted(tokenId, msg.sender, ipfsMetadataHash); + } + + function updateMetadataLink(uint256 tokenId, string memory newIpfsMetadataHash) external onlyHookOwner(tokenId) { + hookMetadata[tokenId].ipfsMetadataHash = newIpfsMetadataHash; + emit MetadataUpdated(tokenId, newIpfsMetadataHash); + } + + function signAudit(uint256 tokenId) external { + HookMetadata storage metadata = hookMetadata[tokenId]; + metadata.auditors.push(msg.sender); + emit AuditorAdded(tokenId, msg.sender); + } + + function isAuditedBy(uint256 tokenId, address auditor) external view returns (bool) { + HookMetadata storage metadata = hookMetadata[tokenId]; + for (uint256 i = 0; i < metadata.auditors.length; i++) { + if (metadata.auditors[i] == auditor) { + return true; + } + } + return false; + } + + function tokenURI(uint256 tokenId) public view override returns (string memory) { + HookMetadata storage metadata = hookMetadata[tokenId]; + return string(abi.encodePacked("ipfs://", metadata.ipfsMetadataHash)); + } + + function isContractDeployer(address deployer, address contractAddress) internal view returns (bool) { + uint256 size; + assembly { size := extcodesize(contractAddress) } + + if (size == 0) { + // Contract doesn't exist, cannot verify deployer + revert("Contract doesn't exist"); + // return false; + } + bytes memory code; + assembly { + code := mload(0x40) + mstore(0x40, add(code, size)) + extcodecopy(contractAddress, add(code, 0x20), 0, size) + } + // bytes32 codeHash = keccak256(code); + + // Recreate the address based on deployer and nonce + address expectedAddress; + bytes32 dataHash = keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))); + assembly { + mstore(0x0, dataHash) + expectedAddress := mload(0x0) + } + console.log(expectedAddress); + + return expectedAddress == contractAddress; + } +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/test/HookRegistry.t.sol b/test/HookRegistry.t.sol new file mode 100644 index 0000000..35e2c30 --- /dev/null +++ b/test/HookRegistry.t.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {HookRegistry} from "../src/HookRegistry.sol"; + +contract MockHook { + constructor() {} +} + +contract HookRegistryTest is Test { + HookRegistry public hookRegistry; + MockHook public mockHook; + address public deployer; + address public otherUser; + + function setUp() public { + deployer = address(this); + otherUser = address(0x2); + + hookRegistry = new HookRegistry(); + mockHook = new MockHook(); + } + + function testMintHookNFT() public { + string memory ipfsMetadataHash = "QmSomeHash"; + + hookRegistry.mintHookNFT(address(mockHook), ipfsMetadataHash); + + uint256 tokenId = 0; + string memory tokenURI = hookRegistry.tokenURI(tokenId); + assertEq(tokenURI, string(abi.encodePacked("ipfs://", ipfsMetadataHash))); + } + + function testMintHookNFT_NotDeployer() public { + string memory ipfsMetadataHash = "QmSomeHash"; + + vm.prank(otherUser); + vm.expectRevert("Not the deployer of the hook"); + hookRegistry.mintHookNFT(address(mockHook), ipfsMetadataHash); + } + + function testUpdateMetadataLink() public { + string memory ipfsMetadataHash = "QmSomeHash"; + + hookRegistry.mintHookNFT(address(mockHook), ipfsMetadataHash); + + string memory newIpfsMetadataHash = "QmNewHash"; + hookRegistry.updateMetadataLink(0, newIpfsMetadataHash); + + uint256 tokenId = 0; + string memory tokenURI = hookRegistry.tokenURI(tokenId); + assertEq(tokenURI, string(abi.encodePacked("ipfs://", newIpfsMetadataHash))); + } + + function testUpdateMetadataLink_NotOwner() public { + string memory ipfsMetadataHash = "QmSomeHash"; + + hookRegistry.mintHookNFT(address(mockHook), ipfsMetadataHash); + + vm.prank(otherUser); + vm.expectRevert("Not the owner"); + hookRegistry.updateMetadataLink(0, "QmNewHash"); + } + + function testSignAudit() public { + string memory ipfsMetadataHash = "QmSomeHash"; + + hookRegistry.mintHookNFT(address(mockHook), ipfsMetadataHash); + + vm.prank(otherUser); + hookRegistry.signAudit(0); + + bool isAudited = hookRegistry.isAuditedBy(0, otherUser); + assertTrue(isAudited); + } + + function testTokenURI() public { + string memory ipfsMetadataHash = "QmSomeHash"; + + hookRegistry.mintHookNFT(address(mockHook), ipfsMetadataHash); + + uint256 tokenId = 0; + string memory tokenURI = hookRegistry.tokenURI(tokenId); + assertEq(tokenURI, string(abi.encodePacked("ipfs://", ipfsMetadataHash))); + } +}