Skip to content

Releases: atomicassets/atomicmarket-contract

v2.0.0

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.

AtomicMarket v2.0.0-rc2

Pre-release

Choose a tag to compare

@robrigo robrigo released this 03 Jul 05:59
b7b757b

The AtomicMarket v2 contract, release candidate 2. Built from master (integration branch v2/integration-rc1) with CDT 4.1.1. This describes the whole v2 change and supersedes rc1.

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. The v2 dependency on AtomicAssets is now limited to reading templates2 mutable template data for royalty attribute matching. 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.

Changes since rc1: custodial rentals descoped

Custodial rentals have been removed from the v2 release train so the rest of v2 can ship without them (#13). The implementation is preserved on the archive/v2-custodial-rentals branch; whether and how rentals return has not been decided. The removal takes out:

  • Actions: announcerent, cancelrent, rentasset, endrent, payrentram
  • Log actions: lognewrent, logrentstart, logrental
  • Table: rentals
  • The rental branch of the transfer memo handling, the rental ricardian contracts, and the rentals wiki and API documentation

The ABI diff against rc1 is exactly this surface. With rentals gone, v2 no longer uses the AtomicAssets v2 holders table or move action (which the paired AtomicAssets v2.0.0-rc4 also removed).

This candidate also completes the test migration off Hydra (#10): the remaining v1 suite gaps were ported to VeRT as new admin, auctions, buyoffers, deposit-withdraw and sales suites, taking the suite from 47 to 220 tests. Test-only, no contract 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.

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. 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. The final surface is 51 actions and 12 tables.

New tables

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 carried from rc1

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 rc1:

  • #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.

To review the full v2 change, diff the tags: git diff v1.3.3 v2.0.0-rc2. v1.3.3 is the pre-v2 baseline and the version currently deployed on WAX mainnet. For the delta from the previous candidate alone, git diff v2.0.0-rc1 v2.0.0-rc2.

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 (6 suites, 220 tests) and the release ABI round-trips the CI --verify-against check.

rc2 is deployed and verified on both FACINGS testnets: the on-chain code hash on the WAX testnet and jungle4 equals the atomicmarket.wasm sha256 below, and get_abi on both chains shows the full royalty and admin surface with no rental actions. This is the first AtomicMarket v2 deployment on any chain.

Artifacts

asset sha256
atomicmarket.wasm 5016d9560574cce18f511de41d4ba1e81c4d30526c4452f0f70c3e59d64e7ed3
atomicmarket.abi e1b480faeb6f59f8f474af28677907a25d0d58e455d5a951f2315a87012177a9

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

AtomicMarket v2.0.0-rc1

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.