Skip to content

v2.0.0

Latest

Choose a tag to compare

@robrigo robrigo released this 03 Aug 23:10
b7b757b

The AtomicMarket v2 contract. Built with CDT 4.1.1.

Unlike AtomicAssets v2, this is a breaking upgrade. Three changes alter the behavior of listings that already exist: multi-asset bundles are withdrawn, the collection fee is charged at execution time rather than listing time, and every new listing must reference exactly one asset. Read Breaking changes before upgrading or integrating.

Upgrading is a setcode plus setabi. On-chain state is preserved, and existing single-asset sales, auctions and buyoffers keep working.

Breaking changes

Multi-asset bundles are withdrawn

A sale, auction or buyoffer must now reference exactly one asset. announcesale, announceauct and createbuyo reject more than one. Listing several assets in one transaction covers what bundles were used for, and single-asset listings are what keep per-asset attribution of the collection fee and royalties exact.

Existing bundle rows are cancelled when touched, not migrated:

Action on a legacy bundle Result
purchasesale Sale cancels, buyer is charged nothing
Bid or claim on a bundle auction Auction dissolves, bid refunded, assets returned
acceptbuyo Buyer refunded
cancelsale, cancelauct May be called by anyone
Activating a bundle (offer memo sale, transfer memo auction) Aborts

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.

The collection fee is charged at execution time

Settlement reads the collection's market_fee live from AtomicAssets at execution, not the value stored when the listing was created. A fee change by the author, up or down within the 15% cap, applies immediately to every existing listing. The listing row's collection_fee is informational only.

The buyer always pays the listed price. Only the split between seller and collection moves. A front end should display the live fee at the point of sale rather than the stored one.

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,
  • attribute rules matching a (field, value) on the asset, such as rarity = legendary.

Configuration lives in the new royaltyconf, royaltytemp and royaltyattr tables. Settlement emits logroyfound, logroytempl, logroyattr and logroydust actions whose amounts sum exactly to the collection fee, so an indexer records 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.

Default-marketplace administration

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, and notification handlers bind with [[eosio::on_notify]].

Interface changes

Additive in full: nothing is removed and no existing struct changes shape. Behavior changes are described above rather than expressed in the ABI. The final surface is 51 actions and 12 tables.

Added
Royalty configuration setroyalconf, delroyalconf, settemplroy, deltemplroy, setattrroy, delattrroy
Royalty settlement logs logroyfound, logroytempl, logroyattr, logroydust
Default-marketplace admin setdefmktcr, migratebal
Tables royaltyconf, royaltytemp, royaltyattr

Existing tables (sales, auctions, buyoffers, tbuyoffers, config, balances, marketplaces, bonusfees, counters) keep their layout. The ABI version moves from eosio::abi/1.1 to 1.2.

Chains that do not yet run template buyoffers also gain createtbuyo, canceltbuyo, fulfilltbuyo, lognewtbuyo and the tbuyoffers table in this upgrade.

Dependency on AtomicAssets

Limited to reading templates2 mutable template data for royalty attribute matching, and reading the collections row for the live market fee.

Hardening

The v2 contract went through three adversarial review passes, which found no critical or high theft or fund-loss issue. The findings that held were carried by six follow-up changes:

  • 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.
  • is_permutation size guards. assertsale, assertauct and acceptbuyo use the four-iterator overload, which compares lengths and avoids an out-of-bounds read.
  • Execution-time fee re-assertion. The fee is re-checked to be between 0 and 15% at settlement, since a negative double cast to uint64_t is undefined behavior.
  • Empty-table guard. The AtomicAssets offers table is checked before --end() in acceptbuyo and fulfilltbuyo.
  • Balance-merge guard. migratebal asserts from != to, preventing a balance-doubling fund loss.
  • ABI verification in CI. patch-abi.py self-asserts idempotency and proves only the two sanctioned spelling changes were applied; CI parses the patched ABI with @wharfkit/antelope.

Custodial rentals are not part of v2. That implementation is preserved on the archive/v2-custodial-rentals branch; whether and how rentals return has not been decided.

Artifacts

Asset sha256
atomicmarket.wasm 5016d9560574cce18f511de41d4ba1e81c4d30526c4452f0f70c3e59d64e7ed3
atomicmarket.abi e1b480faeb6f59f8f474af28677907a25d0d58e455d5a951f2315a87012177a9

Verify against the attached SHA256SUMS. On a deployed account the wasm sha256 equals the on-chain code hash.

Build and verification

Built with cdt-cpp 4.1.1. The published ABI is the legacy-compat build produced by make release: vector<uint8_t> fields render as uint8[] and pair fields keep their first and second spellings, so integrations reading the ABI are unaffected. The VeRT suite passes at 6 suites and 220 tests, and the release ABI round-trips the CI --verify-against check.

Deploying

Pinned by sha256 in the FACINGS monorepo chain-config and deployed through the contracts-deploy workflow. Where the contract account is governed by a multi-party authority, the upgrade reaches chain as an eosio.msig proposal that the authority's signers verify, approve and execute. Signers should check the proposal's wasm sha256 against the table above and its packed ABI against the published .abi, since the chain does not validate setabi payloads.

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 changes, the XPR port, the VeRT test migration and this release were done by the FACINGS team.