---
id: TIP-1026
title: Token Logo URI
description: Adds a logoURI string field to TIP-20 tokens for on-chain token icon metadata.
authors: Dan Robinson
status: Approved
related: TIP-20
protocolVersion: T5
---

# TIP-1026: Token Logo URI

## Abstract

Adds a `logoURI` field to TIP-20 tokens — a string capped at 256 bytes, mutable by the token admin and validated against a small allowlist of URI schemes. This allows wallets and explorers to read a token's icon URI directly from the contract for *metadata distribution*; curation/verification of trusted tokens remains an off-chain concern (see [Out of Scope](#out-of-scope)).

## Motivation

Token icons are currently distributed through an off-chain token list registry ([tokenlist.tempo.xyz](https://tokenlist.tempo.xyz)). Wallets, explorers, and other apps must query this external service to display token icons, and issuers must submit a PR to a separate repository after deploying their token.

Adding `logoURI` as a first-class on-chain field makes token metadata self-describing: any token deployed with TIP-1026 metadata gets discoverability without an off-chain registry round-trip. The 256-byte cap prevents abuse (e.g., storing excessively large strings that could degrade indexer or explorer performance).

### Out of Scope

This TIP is limited to metadata distribution. It does not define which tokens are trusted, verified, or suitable for integration by wallets, DEXes, or explorers. Decisions around trust and curation, such as gas tokens or swappable assets remain offchain and at the discretion of integrators.

---

# Specification

## Interface

The following functions are added to `ITIP20`:

```solidity
/// @notice Returns the logo URI for this token
/// @return The logo URI string (max 256 bytes)
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 — see Behavior).
/// @dev Reverts with LogoURITooLong if the URI exceeds 256 bytes.
///      Reverts 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;
```

## Events

```solidity
/// @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);
```

## Errors

```solidity
/// @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 (see Behavior).
error InvalidLogoURI();
```

## Behavior

- `logoURI()` returns the current logo URI. Returns an empty string if not set.
- `setLogoURI(string)` updates the logo URI. Restricted to `DEFAULT_ADMIN_ROLE`.
  - Reverts with `LogoURITooLong` if `bytes(newLogoURI).length > 256`.
  - Reverts with `InvalidLogoURI` if `newLogoURI` is non-empty and either does not have a scheme parseable per RFC 3986 §3.1 (`scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )` followed by `:`) or the scheme is not in the **allowlist**:

    | Scheme   | Notes                                                            |
    |----------|------------------------------------------------------------------|
    | `https`  | Recommended for general web-hosted assets.                       |
    | `http`   | Allowed for completeness; integrators should prefer `https`.     |
    | `ipfs`   | Recommended for content-addressed assets.                        |
    | `data`   | Useful for small inline images; subject to the 256-byte cap.     |

    Scheme matching is ASCII-case-insensitive (e.g. `HTTPS` and `https` are equivalent).
  - Empty strings (`""`) are explicitly valid and clear the logo URI; no scheme check is performed in that case.

### Factory Support

A new Solidity overload of `TIP20Factory.createToken` is added that accepts an additional `logoURI` argument:

```solidity
function createToken(
    string calldata name,
    string calldata symbol,
    string calldata currency,
    address quoteToken,
    address admin,
    bytes32 salt,
    string calldata logoURI
) external returns (address);
```

The new overload validates `logoURI` with the same rules as `setLogoURI`. Validation MAY occur before or after deployment, but a rejected URI MUST cause the entire transaction to revert so no partially-created token is observable. If `logoURI` is non-empty, the overload writes it and emits `LogoURIUpdated` from the **new token's address** with `updater = msg.sender`. An empty `logoURI` skips both the slot write and the event.

This TIP is non-breaking. The existing `createToken` selector and `TokenCreated` event remain unchanged, new functionality is additive and gated behind the activating hardfork, and existing integrations continue to work without modification.

## Recommended Formats

- HTTPS: `https://example.com/icon.png` (~40–120 bytes)
- IPFS:  `ipfs://QmXfzKRvjZz3u5JRgC4v5mGVbm9ahrUiB4DgzHBsnWbTMM` (~53 bytes)

Square aspect ratio is recommended. **Rasterized formats (PNG / WebP / static, single-frame) are recommended over SVG**; integrators that accept SVG must follow the SVG-handling guidance in [Security Considerations](#security-considerations).

## Security Considerations

The protocol enforces only structural checks: a 256-byte length cap and a scheme allowlist (https, http, ipfs, data). The resolved endpoint and its content must be treated as untrusted by all consumers.

The protocol only validates URI structure. Integrators are responsible for accounting for tracking, as direct logo fetches expose user metadata such as IP address to the endpoint operator, for executable SVG content which should not be rendered inline, for image decoder vulnerabilities since all fetched images are untrusted input, and for impersonation since tokens may claim arbitrary branding and trust must come from offchain curation.

# Invariants

- `bytes(logoURI()).length <= 256` must always hold.
- The legacy 6-argument `createToken` selector and the `TokenCreated` event signature are unchanged by this TIP.
