Area: Smart contract Β· lib.rs (all storage access)
Description
Every piece of per-token state β TokenInfo(index), CreatorTokens(Address) (an append-only
Vec<u32>), TokenIndex(Address), Metadata(Address), the per-token owner and supply keys, and
the whitelist entries β is written to env.storage().instance(). Soroban instance storage is a
single ledger entry shared with the contract instance itself, subject to the ledger-entry size limit
(~64 KiB) and loaded/serialized in full on every invocation.
Consequences as adoption grows:
- Hard brick: once cumulative instance data approaches the entry-size limit, every state-writing
call (create_token, set_metadata, even pause) starts failing. There is no admin action that
can fix it β the factory is permanently unusable and all token bookkeeping is trapped.
- Cost blow-up before the brick: every invocation pays read/write fees proportional to the entire
instance entry, so create_token becomes progressively more expensive for all users as unrelated
tokens accumulate.
- Single TTL: one archival event takes down all bookkeeping at once (see Issue 7).
The MAX_TOKENS_BY_CREATOR_PAGE cap addresses the read path only; the underlying write-side growth
is unbounded. This is the single largest scalability defect in the contract and requires a storage
migration to persistent storage with per-key TTLs.
Tasks
Acceptance criteria
- Instance storage size is O(1) with respect to token count, demonstrated by the stress test.
- All existing view/mutation entrypoints behave identically after migration (full test suite green).
migrate converts a pre-migration state snapshot correctly and is proven idempotent by tests.
Issue 3 of 20 from the codebase audit tracked in ISSUES.md.
Area: Smart contract Β·
lib.rs(all storage access)Description
Every piece of per-token state β
TokenInfo(index),CreatorTokens(Address)(an append-onlyVec<u32>),TokenIndex(Address),Metadata(Address), the per-tokenownerandsupplykeys, andthe whitelist entries β is written to
env.storage().instance(). Soroban instance storage is asingle ledger entry shared with the contract instance itself, subject to the ledger-entry size limit
(~64 KiB) and loaded/serialized in full on every invocation.
Consequences as adoption grows:
call (
create_token,set_metadata, evenpause) starts failing. There is no admin action thatcan fix it β the factory is permanently unusable and all token bookkeeping is trapped.
instance entry, so
create_tokenbecomes progressively more expensive for all users as unrelatedtokens accumulate.
The
MAX_TOKENS_BY_CREATOR_PAGEcap addresses the read path only; the underlying write-side growthis unbounded. This is the single largest scalability defect in the contract and requires a storage
migration to
persistentstorage with per-key TTLs.Tasks
TokenInfo,TokenIndex,Metadata,owner,supply, and whitelist keys toenv.storage().persistent(); keep onlyFactoryState(and the fee split) in instance storage.CreatorTokensVec<u32>with paginated persistent buckets (e.g.CreatorTokens(Address, page: u32)holding at most N indices each) so no single entry growsunboundedly.
extend_ttlcorrectly per persistent key on access (see Issue 7).CURRENT_SCHEMA_VERSIONand write amigratestep that moves existing instance entries topersistent storage; the migration must be idempotent and chunk-safe (callable repeatedly if it
cannot complete in one invocation's resource budget).
docs/contract-abi.mdand the README architecture section.Acceptance criteria
migrateconverts a pre-migration state snapshot correctly and is proven idempotent by tests.Issue 3 of 20 from the codebase audit tracked in
ISSUES.md.