Skip to content

Add version, admin upgrade, two-step admin transfer and platform fee (#100 #101 #102 #103) - #411

Merged
EmmanuelOchaje merged 9 commits into
StellarTickets:mainfrom
sanmipaul:feat/admin-transfer-upgrade-version-platform-fee
Oct 21, 2026
Merged

EmmanuelOchaje merged 9 commits into
StellarTickets:mainfrom
sanmipaul:feat/admin-transfer-upgrade-version-platform-fee

Conversation

@sanmipaul

Copy link
Copy Markdown
Contributor

Closes #100
Closes #101
Closes #102
Closes #103

Summary

One branch covering the four issues assigned to @sanmipaul in the Stellar Wave program.

Also included: duplicate error code

main does not compile. ZoneNotAllowed and GiftClaimAlreadyExists are both = 61, which rustc rejects (E0081). GiftClaimAlreadyExists landed first and keeps 61. ZoneNotAllowed moves to 66, the next free code, since codes are append-only. This is its own commit (26c5e94) and can be split out if maintainers prefer.

Review notes

  • The auth-matrix rows for the new admin and fee entry points are in test/auth_matrix.rs.
  • test/upgrade.rs uploads a hand-built second module. It carries only the contractenvmetav0 section with protocol 28 and pre-release 0, which matches the host's interface version. Please confirm this fixture is accepted by the test harness.
  • docs/MAINNET.md, docs/THREAT_MODEL.md, docs/EVENTS.md, docs/ERRORS.md, docs/CONTRACT_API.md and CHANGELOG.md are updated to match. docs/AUDIT.md is a dated snapshot and was left as is.

Not verified

I did not run cargo build, cargo test, cargo clippy or stellar contract build while preparing this PR. Please run cargo test --workspace and cargo clippy --all-targets -- -D warnings before merging. The new tests have only been checked by reading.

ZoneNotAllowed and GiftClaimAlreadyExists were both assigned 61 after two
merges landed on main. Rust rejects duplicate explicit discriminants, so
the crate did not compile.

GiftClaimAlreadyExists landed first and keeps 61. ZoneNotAllowed moves to
the next free code, 66, since error codes are append-only.
Consumers cannot tell which contract build is deployed. version() returns
the crate version from Cargo.toml, captured at compile time, so it needs no
storage and works before initialize.
The contract could not be upgraded, so a bug fix needed a redeploy and a
full data migration. upgrade(admin, new_wasm_hash) replaces the contract
code through update_current_contract and keeps storage. It requires the
admin's auth, rejects other callers with NotAdmin, and publishes
ContractUpgraded.

docs/UPGRADES.md now covers the upgrade procedure and the state migration
rules: append-only DataKey variants, no field changes on stored structs,
and idempotent migration entry points. The admin-upgradeable trust model
is reflected in MAINNET.md and THREAT_MODEL.md.

Tests: a second WASM build is uploaded and the admin switches to it. A
non-admin is rejected. The auth matrix covers the new entry point.
DataKey::Admin was set at initialize and could never change, so a lost or
rotated admin key left the contract unmanageable.

propose_admin(admin, new_admin) lets the current admin nominate a successor
(auth from the current admin). accept_admin(new_admin) completes the
transfer and needs auth from the nominee, so the role cannot land on an
address that never agreed to it. A new proposal replaces the pending one.

Adds NoPendingAdmin (67) and NotPendingAdmin (68), and the AdminProposed and
AdminTransferred events. Covered by unit tests and auth-matrix rows.
Resale settlement paid only the organizer royalty, so the platform could not
take a cut of resales.

set_platform_fee(admin, fee_bps, recipient) sets a fee taken from the
seller's proceeds next to the royalty and paid to recipient. Zero turns it
off. platform_fee() reads the setting. The fee and any event royalty must
together stay within 10000 bps:

- set_platform_fee rejects a fee over 10000, or a fee that plus the highest
  royalty recorded so far exceeds 10000 (InvalidPlatformFee). The highest
  royalty is tracked in DataKey::MaxRoyaltyBps, updated on event creation.
- create_event rejects a royalty that plus the current fee exceeds 10000
  (InvalidRoyalty).

buy_resale and quote_resale share one split helper, so the royalty, fee and
seller remainder are computed the same way in both. Each is rounded down and
the seller keeps the remainder. quote_resale's seller_amount is now net of
the fee. Its tuple shape is unchanged.

Adds PlatformFeeUpdated, the InvalidPlatformFee error (69) and tests for
settlement, rounding, the quote, zero fee, the admin check and both 100%
bounds.
@netlify

netlify Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for stellarticketscontracts ready!

Name Link
🔨 Latest commit 90c22a6
🔍 Latest deploy log https://app.netlify.com/projects/stellarticketscontracts/deploys/6ad86995dc1842f6d52c56b3
😎 Deploy Preview https://deploy-preview-411--stellarticketscontracts.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@drips-wave

drips-wave Bot commented Oct 19, 2026

Copy link
Copy Markdown

@sanmipaul Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@EmmanuelOchaje
EmmanuelOchaje merged commit a372904 into StellarTickets:main Oct 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants