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 asrarity = 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.
setmarketfeeandaddbonusfeereject a configuration whose fees plus the maximum collection fee exceed the price, andinternal_payout_saleasserts a positive seller payout as the runtime backstop. is_permutationsize guards.assertsale,assertauctandacceptbuyouse 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_tis undefined behavior. - Empty-table guard. The AtomicAssets offers table is checked before
--end()inacceptbuyoandfulfilltbuyo. - Balance-merge guard.
migratebalassertsfrom != to, preventing a balance-doubling fund loss. - ABI verification in CI.
patch-abi.pyself-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.