record_shop is the first-party fixed/floor-price sales package for Miso
Records on Sui. A Listing<Currency> is the complete Record Shop state. There is
no global Record Shop or Distributor object.
Release
└── PressingKey(edition) → Pressing
├── RecordKey(number) → Record
└── ListingKey<Currency>() → Listing<Currency>
Each Listing is permanently bound to one Pressing, one Release, and one currency. It derives directly from the Pressing, so the same currency cannot be listed twice for one Pressing. Different currencies receive different Listing IDs.
The release administrator creates a Pressing, authorizes the exact witness type, creates a Listing, and shares the two independently:
pressing.authorize_distributor<record_shop::witness::Witness>(&pressing_cap);
let listing = record_shop::listing::new<SUI>(
&mut pressing,
&pressing_cap,
record_shop::listing::fixed(1_000_000_000),
);
pressing.share();
listing.share();Buyers pass an immutable Listing, its mutable Pressing, a Balance<Currency>,
their expected current pricing rule, Sui's Clock, and the transaction context. purchase
returns the Record; the PTB must transfer or otherwise consume it.
let record = listing.purchase(
&mut pressing,
payment,
expected_pricing,
clock,
ctx,
);
transfer::public_transfer(record, recipient);fixed(price)accepts exactlyprice.floor(price)accepts at leastpriceand forwards the entire payment.expected_pricingcompares the complete pricing rule, so a stale amount or a same-amount change between Fixed and Floor aborts instead of silently accepting new terms.set_priceandset_staterequire the matchingPressingAdminCap.- The returned Record stores the concrete currency type, actual amount paid, transaction sender, and Clock timestamp as immutable purchase provenance.
All proceeds go to the Release object's native Sui funds accumulator. The Release
administrator can withdraw through release.uid_mut(&release_cap) and
balance::withdraw_funds_from_object.
The authorized identity is exactly:
record_shop::witness::Witness
Witness has only drop; its constructor is public(package). Production code
constructs and immediately consumes it only inside listing::purchase. The privacy
fixtures prove that an external package can neither pack Witness() nor call
witness::new().
The package must be made immutable before any Pressing authorizes its Witness.
Publish the package, consume its UpgradeCap with package::make_immutable, verify
that the capability is gone, and only then authorize
record_shop::witness::Witness.
Requires Sui CLI 1.78 or a compatible toolchain:
make verifyThis runs the 18 Move tests, including the complete ownership and proceeds flow, then confirms both external witness-construction probes fail for the expected privacy diagnostics.
See architecture and security for the invariants and trust boundaries.
License: Apache-2.0.