Skip to content

AtomicMarket v2.0.0-rc1

Pre-release
Pre-release

Choose a tag to compare

@robrigo robrigo released this 28 Jun 16:51

The AtomicMarket v2 contract, release candidate 1. Built from master (integration branch v2/integration-rc1) with CDT 4.1.1.

AtomicMarket v2 is a breaking upgrade, unlike the non-breaking AtomicAssets v2. Every listing now holds exactly one asset, legacy multi-asset bundles auto-cancel when touched, and the collection fee is charged at execution time. v2 depends on AtomicAssets v2: rentals use its holders table and move action, and royalty attribute matching reads its templates2 mutable template data. Upgrading is a setcode plus setabi. Existing single-asset sales, auctions and buyoffers keep working and on-chain state is preserved; legacy bundle rows are cancelled when touched rather than migrated, as described under Breaking changes.

What v2 adds

Royalty splits. By default the collection fee goes to the collection author. An author can instead split it across weighted categories: a global founders list, per-template recipient lists, and attribute rules that match a (field, value) on the asset, such as rarity = legendary. Configuration lives in the new royaltyconf, royaltytemp and royaltyattr tables. Settlement emits logroy* actions whose amounts sum exactly to the collection fee, so an indexer can record the final per-recipient amounts without reimplementing the split. Payouts accrue to balances and are claimed with withdraw; nothing is transferred inline, so a recipient contract cannot block a collection's settlements.

Custodial rentals. An owner lists an asset for per-hour rental with announcerent and transfers it into the contract's custody; a renter pays from their balance with rentasset. Rentals use AtomicAssets v2 holdership: the contract keeps title while the renter becomes the holder, so the renter cannot remove the asset. The current renter can extend an active rental, endrent (which anyone may call) resets an expired one, and cancelrent returns the asset to the owner. Rentals settle through the same payout path as sales, charging maker, taker and collection fees plus royalty splits on each rental and each extension. A single rental, including extensions, is capped at 28 days. State lives in the new rentals table.

Single-asset listings (breaking). announcesale, announceauct and createbuyo reject more than one asset. This keeps per-asset attribution of the collection fee and royalties exact. Listing or buying several assets in one transaction covers what bundles were previously used for.

Execution-time collection fee (breaking). Settlement reads the collection's market_fee live from AtomicAssets at execution time, not the fee stored when the listing was created. A fee change by the author, whether a discount or an increase, applies immediately to every existing listing. The listing row's collection_fee is informational only.

Default-marketplace admin (XPR portability). setdefmktcr redirects the empty-name default marketplace's fee recipient at runtime, and migratebal merges accumulated balances. Together they let one binary run on chains where the seeded fees.atomic account does not exist.

CPU optimizations. Tables are constructed lazily per action, the config singleton is deserialized at most once per action, the AtomicAssets collections row is read through a size-capped partial read that takes only the author and market fee rather than the full row, and notification handlers bind with [[eosio::on_notify]].

New actions

Royalty config: setroyalconf, delroyalconf, settemplroy, deltemplroy, setattrroy, delattrroy. Rentals: announcerent, cancelrent, rentasset, endrent, payrentram. Rental logs: lognewrent, logrentstart, logrental. Royalty logs: logroyfound, logroytempl, logroyattr, logroydust. Default-marketplace admin: setdefmktcr, migratebal. The existing sale, auction, buyoffer and tbuyoffer actions keep their signatures; their behavior changes are noted above.

New tables

rentals, royaltyconf, royaltytemp, royaltyattr. The existing tables (sales, auctions, buyoffers, tbuyoffers, config, balances, marketplaces, bonusfees, counters) keep their layout.

Breaking changes and migration

  • Single-asset only. A new sale, auction or buyoffer must reference exactly one asset.
  • Legacy bundles are cancelled, not migrated. A pre-v2 row with more than one asset auto-cancels when touched. purchasesale cancels the sale and charges the buyer nothing. A bid or claim on a bundle auction dissolves it, refunding the bid and returning the assets. acceptbuyo on a bundle refunds the buyer. cancelsale and cancelauct on bundles may be called by anyone. A partially-claimed bundle auction is finished through the normal claim actions, with the collection fee paid to the author in full and no royalty logs emitted. Activating a bundle (offer memo sale or transfer memo auction with more than one asset) aborts.
  • Execution-time fee. Because the fee is read at settlement, an author can change it within the 15% cap after a listing is created. The buyer always pays the listed price; only the split between seller and collection moves. A front end should show the live fee at the point of sale.

Hardening in this candidate

The base v2 contract (PRs #1 to #3, authored by t-break) went through three adversarial review passes that found no critical or high theft or fund-loss. Six follow-up PRs merged into this candidate:

  • #4 fee-ceiling bound. setmarketfee and addbonusfee reject a configuration whose fees plus the maximum collection fee exceed the price, and internal_payout_sale asserts a positive seller payout as the runtime backstop.
  • #5 is_permutation size guards. assertsale, assertauct and acceptbuyo use the four-iterator overload, which compares lengths and avoids the out-of-bounds read the v2 rewrite had introduced.
  • #6 low-level hardening. Re-assert that the execution-time fee is between 0 and 15% (a negative double cast to uint64_t is undefined behavior), and guard the empty AtomicAssets offers table before --end() in acceptbuyo and fulfilltbuyo.
  • #7 default-marketplace admin. Adds setdefmktcr and migratebal, with a check(from != to) guard that prevents a balance-doubling fund loss.
  • #8 ABI verification in CI. patch-abi.py now self-asserts idempotency and gains a --verify-against mode that proves only the two sanctioned spelling changes were applied; CI also parses the patched ABI with @wharfkit/antelope.
  • #9 documentation. Documents the seller-facing tradeoff of the execution-time fee.

A final cleanup commit addresses Copilot review feedback: include guards on the vendored checkformat.hpp and base58.hpp headers, two typo fixes, and removal of the unused collection_fee parameter from internal_payout_sale, which now uses the live execution-time fee.

To review the full v2 change, diff the tags: git diff v1.3.3 v2.0.0-rc1. v1.3.3 is the pre-v2 baseline, the last commit before t-break's work and the version currently deployed on WAX mainnet.

Build and verification

Built with cdt-cpp 4.1.1. The published ABI is the legacy-compat patched build produced by make release: vector<uint8_t> fields render as uint8[] and pair fields keep their first and second key/value spellings, so integrations that read the ABI keep working. The VeRT suite passes (47 tests) and CI verifies the patched ABI. v2 is not yet deployed on-chain and targets the jungle4 and WAX testnets first.

Artifacts

asset sha256
atomicmarket.wasm 33e82f4d3774a3b20e51d3c2f9f9c74a395203d721f252ee97ac6b5ebefae490
atomicmarket.abi 72e29e966d17f7e4efc7455e6c43eeaf7951128431b228d812713c06aa0d16bf

Verify against the attached SHA256SUMS.

Deploying

The artifacts are pinned by sha256 in the FACINGS monorepo chain-config (the testnet atomicmarket entries) and deployed via the contracts-deploy workflow using the atomicmarket@deploy permission, which is linked to eosio::setcode and eosio::setabi. Mainnet and XPR keep compiling the vendored v1.3.3 source until the v2 production cutover. Pre-release, for testnet.

Credits

AtomicMarket is part of the AtomicAssets NFT ecosystem created by Pink Network, with stewardship passing from Pink Network to Spielworks and then to FACINGS. The v1 contract was written by Jona Wilmsmann (Pink Network). Most of the v2 contract was written by t-break (@on-a-t-break). The v2 review, the hardening PRs, the XPR port and this release were done by the FACINGS team.