-
Notifications
You must be signed in to change notification settings - Fork 628
Create IHookMetadata #382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Create IHookMetadata #382
Changes from all commits
4021ce3
ac79dcf
c93d4b8
ecedc53
f8cd75c
2115888
c57b25b
7e78c5c
2c91c7b
2a0c75a
53d62fa
9b080fb
da33a30
a27cb9c
a5f5003
b371d61
3df37ea
0fbc8b4
f941f1e
67f11d7
735033e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| /** | ||
| * @title IEIP7512 | ||
| * @notice This interface is used for an EIP7512 implementation. | ||
| */ | ||
| interface IEIP7512 { | ||
| /// @notice Defines different types of signature standards. | ||
| enum SignatureType { | ||
| SECP256K1, | ||
| BLS, | ||
| ERC1271, | ||
| SECP256R1 | ||
| } | ||
|
|
||
| /// @notice Represents the auditor. | ||
| /// @param name The name of the auditor. | ||
| /// @param uri The URI with additional information about the auditor. | ||
| /// @param authors List of authors responsible for the audit. | ||
| struct Auditor { | ||
| string name; | ||
| string uri; | ||
| string[] authors; | ||
| } | ||
|
|
||
| /// @notice Represents a summary of the audit. | ||
| /// @param auditor The auditor who performed the audit. | ||
| /// @param issuedAt The timestamp at which the audit was issued. | ||
| /// @param ercs List of ERC standards that were covered in the audit. | ||
| /// @param bytecodeHash Hash of the audited smart contract bytecode. | ||
| /// @param auditHash Hash of the audit document. | ||
| /// @param auditUri URI with additional information or the full audit report. | ||
| struct AuditSummary { | ||
| Auditor auditor; | ||
| uint256 issuedAt; | ||
| uint256[] ercs; | ||
| bytes32 bytecodeHash; | ||
| bytes32 auditHash; | ||
| string auditUri; | ||
| } | ||
|
|
||
| /// @notice Represents a cryptographic signature. | ||
| /// @param signatureType The type of the signature (e.g., SECP256K1, BLS, etc.). | ||
| /// @param data The actual signature data. | ||
| struct Signature { | ||
| SignatureType signatureType; | ||
| bytes data; | ||
| } | ||
|
|
||
| /// @notice Represents a signed audit summary. | ||
| /// @param summary The audit summary being signed. | ||
| /// @param signedAt Timestamp indicating when the audit summary was signed. | ||
| /// @param auditorSignature Signature of the auditor for authenticity. | ||
| struct SignedAuditSummary { | ||
| AuditSummary summary; | ||
| uint256 signedAt; | ||
| Signature auditorSignature; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {IEIP712_v4} from "./IEIP712_v4.sol"; | ||
| import {IEIP7512} from "./IEIP7512.sol"; | ||
|
|
||
| /** | ||
| * @title IHookMetadata | ||
| * @notice This interface is designed so that external indexing services can discover | ||
| * and display essential information about a Uniswap v4 hook. It extends the | ||
| * on-chain audit representation outlined in EIP‑7512, thereby allowing the hook | ||
| * to store and provide signed audit summaries for third-party verification. | ||
| * | ||
| * ---------------------------------------------------------------------------- | ||
| * HOOK METADATA FLOW | ||
| * | ||
| * 1. Required Metadata | ||
| * - Every hook must implement: name(), repository(), logoURI(), websiteURI(), | ||
| * description(), version(). | ||
| * - These fields are effectively immutable and indexed at deployment time. | ||
| * | ||
| * 2. Audit Summaries | ||
| * - The function auditSummaries(auditId) returns a completed audit summary. | ||
| * - Audits can be appended over time, and each newly added audit summary must | ||
| * emit the AuditSummaryRegistered event for indexers. | ||
| * | ||
| * 3. EIP-712 Domain Information | ||
| * - The contract must provide an EIP-712 DOMAIN_SEPARATOR to allow auditors | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a standard format you'd want to enforce in the DOMAIN_SEPARATOR? Could give a recommendation for which fields should/must be specified. ie Im guessing the verifyingContract is the hook but maybe worth specifying?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We created a template for IHookMetadata usage - uniswapfoundation/v4-template#58, used https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/EIP712_v4.sol there |
||
| * to produce valid signatures for the audit summaries. | ||
| * | ||
| * 4. Signature Types | ||
| * - Auditors may choose from multiple signature standards (SECP256K1, BLS, ERC1271, SECP256R1) | ||
| * - The chosen SignatureType is stored alongside the signature data. | ||
| * | ||
| * 5. Auditor Process | ||
| * - The auditor generates an EIP-712 signature over the audit summary, choosing | ||
| * the appropriate SignatureType. | ||
| * - The final signed audit summary is then delivered to the hook owner/deployer. | ||
| * | ||
| * 6. Developer / Deployer Process | ||
| * - Implement this interface in the hook contract, storing all required metadata. | ||
| * - Emit the AuditSummaryRegistered event whenever a new audit summary is added. | ||
| * - Treat core metadata (name, repository, etc.) as immutable after deployment, | ||
| * but you may append new audit summaries as needed. | ||
| * | ||
| * ---------------------------------------------------------------------------- | ||
| * | ||
| * For more details, see: | ||
| * - EIP‑7512: https://eips.ethereum.org/EIPS/eip-7512 | ||
| * - EIP‑712: https://eips.ethereum.org/EIPS/eip-712 | ||
| */ | ||
| interface IHookMetadata is IEIP712_v4, IEIP7512 { | ||
| /// @notice An error emitted when a wrong audit ID is used. | ||
| error WrongAuditId(); | ||
|
|
||
RostyslavBortman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// @notice Emitted when a new audit summary is registered. | ||
| /// @dev This event must be emitted so that all indexing services can | ||
| /// index the newly added audit record. | ||
| /// @param auditId The identifier for the audit record. | ||
| /// @param auditHash The hash of the audit document. | ||
| /// @param auditUri The URI pointing to additional audit info or the full report. | ||
| event AuditSummaryRegistered(uint256 indexed auditId, bytes32 auditHash, string auditUri); | ||
|
|
||
| /// @notice Returns the name of the hook. | ||
| /// @return The hook's name as a string. | ||
| function name() external view returns (string memory); | ||
|
|
||
| /// @notice Returns the repository URI for the hook's source code. | ||
| /// @return The repository URI. | ||
| function repositoryURI() external view returns (string memory); | ||
|
|
||
| /// @notice Returns the URI for the hook's logo. | ||
| /// @return The logo URI. | ||
| function logoURI() external view returns (string memory); | ||
|
|
||
| /// @notice Returns the URI for the hook's website. | ||
| /// @return The website URI. | ||
| function websiteURI() external view returns (string memory); | ||
|
|
||
| /// @notice Returns a description of the hook. | ||
| /// @return The hook's description. | ||
| function description() external view returns (string memory); | ||
|
|
||
| /// @notice Returns the version of the hook. | ||
| /// @return The version identifier as bytes32. | ||
| function version() external view returns (bytes32); | ||
|
|
||
| /// @notice Returns the audit summary record for a given audit ID. | ||
| /// @param auditId The identifier used to look up a specific SignedAuditSummary. | ||
| /// @return summary A SignedAuditSummary struct containing the audit details and signature. | ||
| function auditSummaries(uint256 auditId) external view returns (SignedAuditSummary memory); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {IHookMetadata} from "../interfaces/IHookMetadata.sol"; | ||
|
|
||
| /** | ||
| * @title HookMetadata | ||
| * @notice An abstract implementation of the HookMetadata contract wich internaly implements registration of audits | ||
| * summaries and their retrivals using internal counting mechanism. | ||
| */ | ||
| abstract contract HookMetadata is IHookMetadata { | ||
| mapping(uint256 auditId => SignedAuditSummary signedAuditSummary) private signedAuditsSummaries; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason to store on chain? If offchain indexing services are already indexing against the registration, whats the reason to also store on chain?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question. The primary reason for storing the audit summary on-chain is to ensure immutability and verifiable proof that an audit actually occurred. By storing the audit data, including the auditor’s signature, directly on-chain, we guarantee that nobody can modify the audit content after submission. While off-chain indexing services can indeed store a copy of the audit, they can't provide the same cryptographic guarantees of immutability. Additionally, by having an on-chain registry of trusted auditor addresses, anyone can verify directly on-chain whether a hook was audited by a trusted auditor or not. It's important to note that builders aren't forced to submit audits on-chain—it's an optional but recommended approach if verifiable audit proof is desired directly within the onchain environment. |
||
| uint256 public auditsCount; | ||
|
|
||
| /// @notice Returns a summary about audit using signed audit ID. | ||
| /// @dev Throws an error in case of wrong audit ID. | ||
| /// @param auditId An ID of the audit to retrieve information about. | ||
| function auditSummaries(uint256 auditId) external view returns (SignedAuditSummary memory) { | ||
| if (auditId < auditsCount) return signedAuditsSummaries[auditId]; | ||
|
|
||
| revert IHookMetadata.WrongAuditId(); | ||
| } | ||
|
|
||
| /// @notice An internal method that registers a new signed audit summury and emits an event that may be useful for | ||
| /// external indexing services to discover and display essential information about a Uniswap V4 hook. | ||
| /// @dev This internal method should be called in the child hook contract whenever new audit summary is registered | ||
| /// (for example, in the constructor or in the custom owner/admin/DAO controlled method). | ||
| /// @param signedAuditSummary A new signed audit summury to register. | ||
| /// @return A new signed audit summary ID. | ||
| function _registerAuditSummary(SignedAuditSummary memory signedAuditSummary) internal returns (uint256) { | ||
|
Comment on lines
+24
to
+30
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if I'm in love with the idea of adding an audit after audits, is there a reason why audit information cannot be provided at the time of deployment (via constructor)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @saucepoint There are a few reasons why we came out with this solution:
|
||
| uint256 _auditsCount = auditsCount; | ||
|
|
||
| signedAuditsSummaries[_auditsCount] = signedAuditSummary; | ||
|
|
||
| emit AuditSummaryRegistered( | ||
| _auditsCount, signedAuditSummary.summary.auditHash, signedAuditSummary.summary.auditUri | ||
| ); | ||
|
|
||
| ++auditsCount; | ||
|
|
||
| return _auditsCount; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does "effectively" immutable mean? When can they be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We assume that after deploy they will not be changed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are not actually changeable in the contracts though no? So I think you can just declare them to be immutable
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@snreynolds Unfortunately, it is impossible to declare string variables as immutable in Solidity at this moment.
From the documentation:
Not all types for constants and immutables are implemented at this time. The only supported types are strings (only for constants) and value types.
However, there is one workaround we can implement instead: put all 6 variables in the HookMetadata storage, mark them as private (not immutable, as this is impossible) and allow them to be set up through the constructor only. No setters will be provided, only getters that will allow reading these state variables from outside or child contracts.
What do you think about this approach?