From e91959d412f7edf88d975f3b8df9c5940f6c8247 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Mon, 4 May 2026 13:15:34 +0000 Subject: [PATCH] feat(tip-1026): add logoURI/setLogoURI, LogoURIUpdated, and createToken overload --- src/interfaces/ITIP20.sol | 28 ++++++++++++++++++++++++++++ src/interfaces/ITIP20Factory.sol | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/interfaces/ITIP20.sol b/src/interfaces/ITIP20.sol index e74102a..5c36b45 100644 --- a/src/interfaces/ITIP20.sol +++ b/src/interfaces/ITIP20.sol @@ -261,6 +261,34 @@ interface ITIP20 is ITIP20RolesAuthErr { /// @notice Returns the EIP-712 domain separator for this token function DOMAIN_SEPARATOR() external view returns (bytes32); + + // TIP-1026: Token Logo URI + + /// @notice The provided logo URI exceeds the maximum length of 256 bytes. + error LogoURITooLong(); + + /// @notice The provided logo URI is non-empty and is either not a syntactically + /// valid URI or its scheme is not in the allowlist (`https`, `http`, + /// `ipfs`, `data`, ASCII-case-insensitive). + error InvalidLogoURI(); + + /// @notice Emitted when the logo URI is updated. + /// @param updater The account that performed the update. + /// @param newLogoURI The new logo URI. + event LogoURIUpdated(address indexed updater, string newLogoURI); + + /// @notice Returns the logo URI for this token (TIP-1026). + /// @return The logo URI string (max 256 bytes; empty if not set). + function logoURI() external view returns (string memory); + + /// @notice Sets the logo URI for this token (requires DEFAULT_ADMIN_ROLE). + /// @param newLogoURI The new logo URI (must be <= 256 bytes and, if non-empty, + /// a valid URI with an allowed scheme). + /// @dev Reverts with `LogoURITooLong` if the URI exceeds 256 bytes, or with + /// `InvalidLogoURI` if the URI is non-empty and either not syntactically + /// a URI or its scheme is not in the allowlist. An empty string is valid + /// and clears the logo URI. + function setLogoURI(string calldata newLogoURI) external; } /// @title The interface for TIP-20 compliant tokens diff --git a/src/interfaces/ITIP20Factory.sol b/src/interfaces/ITIP20Factory.sol index 9e69962..77b109b 100644 --- a/src/interfaces/ITIP20Factory.sol +++ b/src/interfaces/ITIP20Factory.sol @@ -44,6 +44,28 @@ interface ITIP20Factory { bytes32 salt ) external returns (address); + /// @notice Creates a new TIP-20 token and atomically sets its `logoURI` (TIP-1026). + /// @dev Solidity overload of `createToken` with an additional `logoURI` argument. + /// The legacy 6-argument selector is unchanged and remains supported. + /// The logo URI is validated **before** the token is deployed. + /// Reverts with `LogoURITooLong` if `bytes(logoURI).length > 256`, or with + /// `InvalidLogoURI` if `logoURI` is non-empty and either has no parseable + /// scheme (RFC 3986 ยง3.1) or its scheme is not in the allowlist + /// (`https`, `http`, `ipfs`, `data`, case-insensitive). + /// The `LogoURIUpdated` event is emitted by the new token (not the factory) + /// with `updater = msg.sender`. An empty `logoURI` is valid and skips both + /// the slot write and the event. + /// @return The address of the newly created token contract + function createToken( + string memory name, + string memory symbol, + string memory currency, + ITIP20 quoteToken, + address admin, + bytes32 salt, + string memory logoURI + ) external returns (address); + /// @notice Checks if a given address is a TIP-20 compliant token /// @param token The address of the token to check /// @return True if the address is a TIP-20 token, false otherwise