Skip to content

Fungible: migration path for the total supply moved out of instance storage - #933

Merged
ozgunozerk merged 1 commit into
mainfrom
fix/total-supply-migration
Oct 7, 2026
Merged

ozgunozerk merged 1 commit into
mainfrom
fix/total-supply-migration

Conversation

@ozgunozerk

@ozgunozerk ozgunozerk commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #889

Up to v0.7.x, Base tracked the total supply in instance storage. Since #795 the supply lives in a persistent entry owned by the TotalSupply contract type. A token upgraded in place keeps its balances but reads its supply as 0, and burns panic with #104 until the supply is migrated (with the knock-on effects on Capped, RWA and Vault listed in #889). This was also raised by the auditors.

Changes

  • total_supply::migrate_total_supply(e) -> i128, as proposed in 🐞 [Bug]: total supply moved to persistent storage without a migration path #889. It reads the legacy instance entry (the old FungibleStorageKey::TotalSupply encodes the same as TotalSupplyStorageKey::TotalSupply, since unit variants are stored by name), adds it to the persistent counter, and removes the old entry. It's additive, so mints between upgrade and migrate stay accounted for, and a second call is a no-op. No auth, so it carries a Security Warning. Unit tests cover the migrated value, the removed entry, the additive case, the no-op case, calling it twice, and the key-encoding equality against a copy of the old enum.
  • examples/upgradeable v1/v2 now demonstrate this upgrade instead of the abstract Config { rate } → Config { rate, active } change (already covered by lazy-v1/lazy-v2 and the module docs):
    • v1 is a fungible token built with the published stellar-tokens = "=0.7.2" (soroban-sdk 26.1.0).
    • v2 is the same token on the current library (Compose<(TotalSupply,)> + FungibleTotalSupply), with migrate(operator) behind #[only_role(operator, "migrator")], guarded by the schema version and calling migrate_total_supply.
    • Tests: before migrating, total_supply() == 0 and a burn fails. After migrating, the supply is correct and the old entry is gone. Later mints and burns update the supply, and a second migrate is rejected. A second test mints on v2 before migrating. The upgrader test now uses the token.
  • Module docs (total_supply, upgradeable) and packages/tokens/README.md point at the helper and the example.

Divergence from #889: v1 is a real 0.7.2 token

#889 suggested reproducing the old layout on current crates, on the assumption that an old-version v1 can't be a workspace member. It can: v1 pins its own dependencies (=0.7.2, soroban-sdk =26.1.0) and builds with stellar contract build. So the migration is tested against the storage a deployed token really has, not a hand-written copy. I also diffed v0.7.2 against main: the total supply is the only stored data that changed (access-control, upgradeable and other fungible keys are unchanged).

Costs, open for pushback:

  • Cargo.lock grows by about 680 lines and CI compiles a second SDK tree.
  • The sdk-26 test host refuses the protocol-28 v2 wasm ("contract protocol number is newer than host"), so the upgrade tests moved from v1 to v2. v2 registers the v1 wasm instead.
  • v1 no longer runs natively, so its lines count as uncovered in llvm-cov.

The testdata wasms were rebuilt with stellar-cli 27.0.0. Release notes for v0.9.0 should get an "Upgrading from 0.7 or earlier" section pointing at the helper; that lives outside the repo.

PR Checklist

  • Tests
  • Documentation

Summary by CodeRabbit

  • New Features
    • Added a total-supply migration for fungible tokens upgraded from stellar-tokens v0.7.x or earlier. It moves the legacy supply to current storage and removes the old entry; repeat calls do not change supply.
    • Supply minted after an upgrade but before migration is included in the migrated total. Existing balances, allowances, metadata, and roles remain available.
    • Added an upgrade example and tests covering migration, minting, and burning.
  • Important
    • Migrate immediately after upgrading: until then, supply reads as zero and burns can fail. Migration should be access-controlled.

…torage

Up to v0.7.x, `Base` tracked the total supply in `instance` storage. Since
#795 it lives in a `persistent` entry owned by the `TotalSupply` contract
type, so a token upgraded in place reads its supply as 0 and burns panic.

- Add `total_supply::migrate_total_supply`, which moves the legacy entry
  (same key encoding) into the persistent one, additively, and removes it.
- Turn the `upgradeable` v1/v2 example into this upgrade: v1 is a real
  `stellar-tokens` 0.7.2 token, v2 the same token on the current library
  with a `migrate()` calling the helper. The upgrade tests live in v2,
  since the soroban-sdk 26 test host cannot run the v2 wasm.
- Point the module docs and README at the helper and the example.

Closes #889
@ozgunozerk
ozgunozerk requested a review from brozorec October 7, 2026 12:26
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: d488152e-5800-4ae8-8cf0-554279f3fbfb
📥 Commits

Reviewing files that changed from the base of the PR and between 6eb047d and bc81afe.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • examples/upgradeable/testdata/upgradeable_v1_example.wasm is excluded by !**/*.wasm
  • examples/upgradeable/testdata/upgradeable_v2_example.wasm is excluded by !**/*.wasm
📒 Files selected for processing (14)
  • examples/upgradeable/upgrader/src/test.rs
  • examples/upgradeable/v1/Cargo.toml
  • examples/upgradeable/v1/src/contract.rs
  • examples/upgradeable/v1/src/lib.rs
  • examples/upgradeable/v1/src/test.rs
  • examples/upgradeable/v2/Cargo.toml
  • examples/upgradeable/v2/src/contract.rs
  • examples/upgradeable/v2/src/lib.rs
  • examples/upgradeable/v2/src/test.rs
  • packages/contract-utils/src/upgradeable/mod.rs
  • packages/tokens/README.md
  • packages/tokens/src/fungible/extensions/total_supply/mod.rs
  • packages/tokens/src/fungible/extensions/total_supply/storage.rs
  • packages/tokens/src/fungible/extensions/total_supply/test.rs
💤 Files with no reviewable changes (2)
  • examples/upgradeable/v1/src/lib.rs
  • examples/upgradeable/v1/src/test.rs

Included review availability: This review used your included allowance. 3 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.


Walkthrough

The pull request adds a helper to migrate fungible-token supply from instance storage to persistent storage. It updates the upgradeable v1 and v2 examples, adds migration tests, and documents the upgrade requirement.

Changes

Token Supply Migration

Layer / File(s) Summary
Legacy token baseline
examples/upgradeable/v1/Cargo.toml, examples/upgradeable/v1/src/*
The v1 example now implements a fungible token using stellar-tokens v0.7.2. The constructor mints its initial supply to the admin. The previous rate-based test is removed.
Supply migration helper
packages/tokens/src/fungible/extensions/total_supply/*, packages/tokens/README.md
migrate_total_supply adds legacy instance-stored supply to persistent supply, removes the legacy entry, and returns the migrated amount. Tests cover key encoding, migration, absent legacy supply, and repeated calls. Documentation describes the upgrade requirement.
v2 migration and upgrade tests
examples/upgradeable/v2/*, examples/upgradeable/upgrader/src/test.rs, packages/contract-utils/src/upgradeable/mod.rs
v2 calls the helper during migration and sets schema version 2. Tests cover migration and mints before migration. The upgrader test checks supply and admin balance after migration. The example description is updated.

Priority: ⬆️ High

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: High

Sequence Diagram(s)

sequenceDiagram
  participant V2 as ExampleContract v2
  participant Helper as migrate_total_supply
  participant Instance as Instance storage
  participant Persistent as Persistent supply storage
  V2->>Helper: Call after role and schema checks
  Helper->>Instance: Read legacy total supply
  Helper->>Persistent: Add legacy amount to current supply
  Helper->>Instance: Remove legacy entry
  Helper-->>V2: Return migrated amount
  V2->>V2: Set schema version to 2
Loading

Merge Risk: ⚪ Minimal · up to bc81a

The migration matches the legacy supply layout, and no merge-blocking issue is established. Normal checks can proceed.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Most coding objectives in #889 are implemented. migrate_total_supply adds the legacy instance value to persistent supply, removes the old entry, and returns zero when no legacy value exists. Tests c… Add the requested v0.9.0 release-note section for upgrading from the affected earlier versions, including the migration helper and its use from an access-controlled migration entrypoint.
Docstring Coverage ⚠️ Warning Docstring coverage is 48.28% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 29 functions across 9 files. (3 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the migration path for total supply moved out of instance storage.
Description check ✅ Passed The description identifies the issue, explains the migration helper and upgrade example, summarizes tests and documentation, and completes both checklist items.
Out of Scope Changes check ✅ Passed The helper, migration tests, upgradeable example changes, and documentation all support the migration objectives in #889. The use of a real 0.7.2 v1 token is a relevant alternative to reproducing the …
Full details: Linked Issues check

Explanation

Most coding objectives in #889 are implemented. migrate_total_supply adds the legacy instance value to persistent supply, removes the old entry, and returns zero when no legacy value exists. Tests cover additive and repeated migration behavior and key encoding. The v1/v2 example demonstrates migration from a real 0.7.2 token, role-restricted migration, pre-migration burn failure, and later mint and burn behavior. The module and README documentation point to the helper and example. However, #889 also requires a v0.9.0 release-note section explaining the upgrade path. The current changes do not include that documentation.

Full details: Docstring Coverage

Explanation

Docstring coverage is 48.28% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 29 functions across 9 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

I’m a rabbit with a ledger to keep,
I hop through old supplies in a heap.
From instance to persistent they go,
While balances stay ready to show.
A fresh migration, neat as can be,
Leaves a tidy key for me!

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ozgunozerk
ozgunozerk merged commit ce5c568 into main Oct 7, 2026
8 checks passed
@ozgunozerk
ozgunozerk deleted the fix/total-supply-migration branch October 7, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 [Bug]: total supply moved to persistent storage without a migration path

2 participants