Area: Smart contract · lib.rs (set_metadata) + frontend rendering
Description
set_metadata accepts any String as metadata_uri — no scheme check, no length bound beyond
Soroban's value limits, no CID shape check — and then locks it forever (MetadataAlreadySet). Two
distinct problems compound:
- Garbage-in, locked forever: a typo'd CID, an
https:// URL to a server that later dies, or an
outright empty/junk string becomes the token's permanent metadata pointer. There is no
correct-after-mistake path, not even for the token creator, and no admin override — a single
mis-click permanently disfigures a token that may carry real economic value.
- Untrusted sink: clients (this app, explorers, other dApps) resolve whatever the string says.
The frontend guards ipfs:// at fetch time, but the contract-level contract ("metadata_uri is an
IPFS URI", as the docs claim) is unenforced, so every consumer must implement its own defenses
against javascript:, data:, oversized, or malicious URIs.
Tasks
Acceptance criteria
- The contract rejects any metadata URI that is not a bounded-length
ipfs:// (or explicitly
allow-listed) string, proven by tests.
- A creator can recover from a wrong URI through the governed update path, and can deliberately
freeze metadata; both states are visible in the UI.
Issue 19 of 20 from the codebase audit tracked in ISSUES.md.
Area: Smart contract ·
lib.rs(set_metadata) + frontend renderingDescription
set_metadataaccepts anyStringasmetadata_uri— no scheme check, no length bound beyondSoroban's value limits, no CID shape check — and then locks it forever (
MetadataAlreadySet). Twodistinct problems compound:
https://URL to a server that later dies, or anoutright empty/junk string becomes the token's permanent metadata pointer. There is no
correct-after-mistake path, not even for the token creator, and no admin override — a single
mis-click permanently disfigures a token that may carry real economic value.
The frontend guards
ipfs://at fetch time, but the contract-level contract ("metadata_uri is anIPFS URI", as the docs claim) is unenforced, so every consumer must implement its own defenses
against
javascript:,data:, oversized, or malicious URIs.Tasks
ipfs://prefix (or the documented allow-list ofschemes), enforce a sane maximum length (e.g. ≤ 128 bytes), and reject empty strings with a
dedicated
InvalidMetadataUrierror.a bounded number of times or within a grace window, or add an explicit
freeze_metadata(token_address)the creator calls to make it immutable intentionally. Emitmetaevents on every change so history stays auditable.client-side before paying the metadata fee.
event emission per change.
Acceptance criteria
ipfs://(or explicitlyallow-listed) string, proven by tests.
freeze metadata; both states are visible in the UI.
Issue 19 of 20 from the codebase audit tracked in
ISSUES.md.