From 962da2bcb177460520aef5ee783a97d22e45f5a3 Mon Sep 17 00:00:00 2001 From: Rob Konsdorf Date: Tue, 18 Aug 2026 02:03:34 -0400 Subject: [PATCH 1/5] docs: give the write snippets a session they can actually build Every write snippet in this corpus opened against a session that no page constructed, and the corpus stated no chain id anywhere, so a reader could produce a working read and could not plan a testnet mint. The signer page closes both gaps: the two install lines, the session constructor with its two identity branches, the actor and permission pair, and one chain id per chain, each read live from a running node and cross-checked against the pinned library constants. Each guide that opens a snippet against a session now says where that session comes from, so the construction arrives before the first transact call rather than never. --- guides/asset-lifecycle.md | 2 +- guides/auctions.md | 2 +- guides/buyoffers.md | 2 +- guides/deposits.md | 2 +- guides/links.md | 2 +- guides/offers.md | 2 +- guides/sales.md | 2 +- guides/signing.md | 93 +++++++++++++++++++++++++++++++++++ reference/sdk/atomicassets.md | 2 +- validation-log.md | 4 +- 10 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 guides/signing.md diff --git a/guides/asset-lifecycle.md b/guides/asset-lifecycle.md index ec29d4b..ec0fbdf 100644 --- a/guides/asset-lifecycle.md +++ b/guides/asset-lifecycle.md @@ -11,7 +11,7 @@ key-modules: The full creator flow on the `atomicassets` contract: create a collection, define a schema, optionally create a template, mint assets, edit mutable data, transfer, and burn. Baseline is AtomicAssets V2; notes call out where V1 differed. Every data shape, required authorization, RAM payer, and failure mode below was checked against tag `v2.0.0-rc4` of `atomicassets-contract` (the release pinned for both testnets), `src/atomicassets.cpp`. -Each step shows the action's data as plain JSON first, then the same call through `@wharfkit/session`'s `session.transact()`. `ATTRIBUTE_MAP` fields (`data`, `immutable_data`, `mutable_data`) serialize as an array of `{key, value}` pairs, where `value` is a two-element `[type, value]` variant. The `key`/`value` naming is only present in the patched release ABI; the raw CDT build names the same fields `first`/`second`. See [Contract releases and deployment](../reference/contract-releases.md#raw-vs-patched-abi) ("Raw vs patched ABI"). +Each step shows the action's data as plain JSON first, then the same call through `@wharfkit/session`'s `session.transact()`. See [Build a session and sign](signing.md) for how that `session` is constructed and which chain it points at. `ATTRIBUTE_MAP` fields (`data`, `immutable_data`, `mutable_data`) serialize as an array of `{key, value}` pairs, where `value` is a two-element `[type, value]` variant. The `key`/`value` naming is only present in the patched release ABI; the raw CDT build names the same fields `first`/`second`. See [Contract releases and deployment](../reference/contract-releases.md#raw-vs-patched-abi) ("Raw vs patched ABI"). ## Collection naming rules diff --git a/guides/auctions.md b/guides/auctions.md index 8a03537..ae4c172 100644 --- a/guides/auctions.md +++ b/guides/auctions.md @@ -11,7 +11,7 @@ key-modules: The full lifecycle of an AtomicMarket auction (V2 baseline): announcing, transferring the asset into escrow, bidding, ending, claiming, and cancelling. -Unlike a sale, an auction takes actual custody of the asset: the seller transfers it to the `atomicmarket` contract account, and it sits there until claimed or the auction is cancelled before any bid lands. Bids are deposit-backed: a bidder's AtomicMarket balance is debited when they bid, and refunded if outbid. See [Balances and deposits](deposits.md) for the transfer-with-memo deposit flow; this guide only shows where a step requires a sufficient balance. +Unlike a sale, an auction takes actual custody of the asset: the seller transfers it to the `atomicmarket` contract account, and it sits there until claimed or the auction is cancelled before any bid lands. Bids are deposit-backed: a bidder's AtomicMarket balance is debited when they bid, and refunded if outbid. See [Balances and deposits](deposits.md) for the transfer-with-memo deposit flow; this guide only shows where a step requires a sufficient balance. Each write below runs through a `session` built in [Build a session and sign](signing.md). ## Announce an auction diff --git a/guides/buyoffers.md b/guides/buyoffers.md index 35e526f..232fa08 100644 --- a/guides/buyoffers.md +++ b/guides/buyoffers.md @@ -11,7 +11,7 @@ key-modules: How to create, accept, decline, and cancel AtomicMarket buyoffers, for both single assets and templates. Baseline is AtomicMarket V2 (`atomicmarket-contract`); "Changed in V2" notes call out where V1 behaved differently. Lifecycle-state facts for the indexer and hosted API (LISTED/CANCELED/SOLD, no row cleanup, `state` filtering) are validated in [atomicassets-api HTTP API](../reference/api.md) and [Query the API and chain tables](querying-the-api.md); this guide cross-links them rather than repeating them. Deposit and balance mechanics are covered in [Balances and deposits](deposits.md). -A buyoffer is a buyer-initiated, escrowed offer: the price is deducted from the buyer's deposited balance immediately at creation (see [Balances and deposits](deposits.md)), before the counterparty has agreed to anything. WAX mainnet currently runs AtomicMarket V1; see [AtomicMarket tables](../reference/atomicmarket/tables.md#config) ("config") for the live-version check. The V1 behavior noted below is the live behavior only for as long as mainnet stays on V1. +A buyoffer is a buyer-initiated, escrowed offer: the price is deducted from the buyer's deposited balance immediately at creation (see [Balances and deposits](deposits.md)), before the counterparty has agreed to anything. WAX mainnet currently runs AtomicMarket V1; see [AtomicMarket tables](../reference/atomicmarket/tables.md#config) ("config") for the live-version check. The V1 behavior noted below is the live behavior only for as long as mainnet stays on V1. Each write below runs through a `session` built in [Build a session and sign](signing.md). ## Asset buyoffers diff --git a/guides/deposits.md b/guides/deposits.md index 2142cbe..3da153d 100644 --- a/guides/deposits.md +++ b/guides/deposits.md @@ -14,7 +14,7 @@ AtomicMarket never moves tokens directly between accounts during a sale, auction A deposit is a normal token transfer to the `atomicmarket` account with the memo `deposit`, exactly. `receive_token_transfer` (the `*::transfer` notification handler) checks the memo case-sensitively and rejects anything else with `"invalid memo"`. No other memo is treated as a deposit, and there is no separate `deposit` action to call. It also validates the incoming token against the config's supported-token list by both the sending contract and the symbol together (see "Supported tokens" below); a token with a matching symbol from an unrecognized contract is rejected even if another supported contract issues that same symbol. -The first deposit for an owner and symbol creates the `balances` row; a later deposit of the same symbol increments the existing entry in place; a deposit of a different (already-supported) symbol appends a new entry to that owner's `quantities` vector. +The first deposit for an owner and symbol creates the `balances` row; a later deposit of the same symbol increments the existing entry in place; a deposit of a different (already-supported) symbol appends a new entry to that owner's `quantities` vector. The `session` the snippet below signs through is built in [Build a session and sign](signing.md). ```json { diff --git a/guides/links.md b/guides/links.md index dc8e6ee..7dd109e 100644 --- a/guides/links.md +++ b/guides/links.md @@ -8,7 +8,7 @@ key-modules: ["atomictools-contract (commit d89ce79e4): src/link.cpp, include/at A claim link (or "claimlink") lets someone hand a set of assets to a recipient who does not yet have an account name in hand: the sender escrows the assets against an off-chain key pair, and whoever receives the private key can claim the assets to any account they control. It is how "here is a link, open it to receive these assets" flows are built on Antelope chains. The contract is deployed as account `atomictoolsx` on WAX and under the same name on other chains; the `config.atomicassets_account` it escrows through is `atomicassets`. -The contract has no release tags. Source citations pin commit `d89ce79e4` of `pinknetworkx/atomictools-contract`. This guide shows each write as plain JSON action data first, then the same call through `@wharfkit/session`'s `session.transact()`; it does not broadcast any transaction. The read examples are live `get_table_rows` and hosted-API calls that were run read-only against `wax.greymass.com` and `wax.api.atomicassets.io`. Asset ids and the `link_counter` exceed the JavaScript safe-integer range and must be passed as strings; see [@wharfkit/antelope client behavior](../reference/wharfkit.md) and [AtomicMarket V2 changes](../reference/atomicmarket/v2-changes.md#large-integers-serialize-as-strings) ("Large integers serialize as strings"). +The contract has no release tags. Source citations pin commit `d89ce79e4` of `pinknetworkx/atomictools-contract`. This guide shows each write as plain JSON action data first, then the same call through `@wharfkit/session`'s `session.transact()`; it does not broadcast any transaction. See [Build a session and sign](signing.md) for how that `session` is constructed. The read examples are live `get_table_rows` and hosted-API calls that were run read-only against `wax.greymass.com` and `wax.api.atomicassets.io`. Asset ids and the `link_counter` exceed the JavaScript safe-integer range and must be passed as strings; see [@wharfkit/antelope client behavior](../reference/wharfkit.md) and [AtomicMarket V2 changes](../reference/atomicmarket/v2-changes.md#large-integers-serialize-as-strings) ("Large integers serialize as strings"). ## The shape of the flow diff --git a/guides/offers.md b/guides/offers.md index b4e7c5d..a465caf 100644 --- a/guides/offers.md +++ b/guides/offers.md @@ -8,7 +8,7 @@ key-modules: ["atomicmarket-contract (v2.0.0-rc2): src/atomicmarket.cpp", "atomi `atomicassets` offers are the contract's only built-in trade primitive: a sender proposes swapping some of their assets for some of a recipient's, the recipient accepts or declines, and either side can back out before that happens. AtomicMarket sales and buyoffers are built on top of this primitive rather than moving assets directly (sales use an offer with memo `"sale"`, buyoffers use memos `"buyoffer"` and `"tbuyoffer"`); see "How AtomicMarket sales use offers" below for how the two connect. AtomicMarket auctions are the exception: they do not use offers at all. A seller escrows the asset by a direct AtomicAssets `transfer` to the `atomicmarket` contract with the memo `"auction"`, which the contract's `receive_asset_transfer` handler picks up. Behavior here is unchanged between V1 and V2. AtomicAssets line citations below are against tag `v2.0.0-rc4` of `atomicassets-contract` (the release pinned for both testnets). -Each action's data shape is plain JSON first, then the same call through `@wharfkit/session`'s `session.transact()`. Only asset ids need the string treatment: see [AtomicMarket V2 changes](../reference/atomicmarket/v2-changes.md#large-integers-serialize-as-strings) ("Large integers serialize as strings") for why. `template_id` is an `int32_t`, and offer ids are a small contract-wide `uint64` counter (a live `offers` row reads `offer_id: 7`); both stay well inside the safe-integer range and serialize as bare JSON numbers, as the numeric `offer_id` examples below do. See [@wharfkit/antelope client behavior](../reference/wharfkit.md). +Each action's data shape is plain JSON first, then the same call through `@wharfkit/session`'s `session.transact()`, which [Build a session and sign](signing.md) constructs. Only asset ids need the string treatment: see [AtomicMarket V2 changes](../reference/atomicmarket/v2-changes.md#large-integers-serialize-as-strings) ("Large integers serialize as strings") for why. `template_id` is an `int32_t`, and offer ids are a small contract-wide `uint64` counter (a live `offers` row reads `offer_id: 7`); both stay well inside the safe-integer range and serialize as bare JSON numbers, as the numeric `offer_id` examples below do. See [@wharfkit/antelope client behavior](../reference/wharfkit.md). ## Create an offer: createoffer diff --git a/guides/sales.md b/guides/sales.md index 3ec71e4..8279cc7 100644 --- a/guides/sales.md +++ b/guides/sales.md @@ -13,7 +13,7 @@ The full lifecycle of an AtomicMarket instant sale (V2 baseline): announcing, es A sale is a lazy-accept escrow: `announcesale` only records a row, it never moves the asset. The asset stays in the seller's account until a buyer calls `purchasesale`, which accepts the underlying AtomicAssets offer and transfers the asset in the same transaction. Multiple live sale rows for the same asset are therefore valid chain state, typically a stale listing left by a previous owner after a transfer or an earlier purchase, or listings from one seller covering different asset bundles that share an asset. An indexer showing several listings for one asset is faithfully mirroring the chain; reconcilers should not delete them as drift. -Purchases and bids draw on the buyer's AtomicMarket balance rather than moving tokens directly. See [Balances and deposits](deposits.md) for the transfer-with-memo deposit flow and balance mechanics; this guide only shows where a step requires a sufficient balance. +Purchases and bids draw on the buyer's AtomicMarket balance rather than moving tokens directly. See [Balances and deposits](deposits.md) for the transfer-with-memo deposit flow and balance mechanics; this guide only shows where a step requires a sufficient balance. Each write below runs through a `session` built in [Build a session and sign](signing.md). ## Announce a sale diff --git a/guides/signing.md b/guides/signing.md new file mode 100644 index 0000000..20efe8b --- /dev/null +++ b/guides/signing.md @@ -0,0 +1,93 @@ +--- +scope: "Construct the WharfKit session every write snippet signs through: the install lines, the chain ids, the actor and permission pair, and which network runs V2" +depends-on: [reference/wharfkit.md, reference/atomicassets/v2-upgrade.md, guides/querying-the-api.md] +key-modules: + - "@wharfkit/session (1.6.1): src/index.ts, src/session.ts, src/transact.ts" + - "@wharfkit/common (1.5.0): src/common/chains.ts" + - "@wharfkit/wallet-plugin-privatekey (1.1.0): src/index.ts" +--- + +# Build a session and sign + +Every write snippet in this repository opens against a `session` and never builds one. This page builds it: the packages to install, the chain the session points at, and the account and permission it signs as. The flows stay in the guide that owns each one, and each of those links back here. + +## Reads need no key, no account, and no registration + +Reading AtomicAssets and AtomicMarket state takes no credential. The hosted API answers an anonymous request, and so does a chain table read against a public node. There is no API key to obtain, no account to create, and no registration step: a key signs a transaction, and nothing on the read path asks for one. See [Query the API and chain tables](querying-the-api.md) for that surface. Everything below is for the write path. + +Source: live anonymous reads, `GET https://wax.api.atomicassets.io/atomicassets/v1/assets?limit=1` (HTTP 200 with rows, no credential sent) and `POST https://wax.greymass.com/v1/chain/get_table_rows` for `atomicassets` `collections` (rows returned, no credential sent) + +## Install the signing library + +``` +npm install @wharfkit/session +``` + +A script that signs from a private key it holds itself adds the private-key signing plugin: + +``` +npm install @wharfkit/wallet-plugin-privatekey +``` + +Two packages are enough because `@wharfkit/session` re-exports `@wharfkit/antelope`, `@wharfkit/abicache`, `@wharfkit/common`, and `@wharfkit/signing-request` from its own entry point, so `Chains`, `PrivateKey`, `Name`, and the other Antelope types arrive with the one import and cannot drift to a second copy. The plugin declares `@wharfkit/session` as a peer dependency and has no runtime dependency of its own beyond `tslib`. A browser integration signs through a plugin for the signer the reader already has rather than this one, and that plugin implements the same `WalletPlugin` interface, so the flows in the guides are unchanged either way: they only call `session.transact()`. + +Source: `@wharfkit/session (1.6.1) src/index.ts:1-12` (the four re-exports and the default `SessionKit` export), `package.json` (the four `@wharfkit` dependencies); `@wharfkit/wallet-plugin-privatekey (1.1.0) package.json` (the `@wharfkit/session` peer dependency, `tslib` the only runtime dependency), `src/index.ts:23` (the class implements `WalletPlugin`) + +## Construct the session + +```ts +import { Chains, Session } from '@wharfkit/session' +import { WalletPluginPrivateKey } from '@wharfkit/wallet-plugin-privatekey' + +const actor = process.env.WAX_TESTNET_ACTOR +const privateKey = process.env.WAX_TESTNET_PRIVATE_KEY +if (actor === undefined || privateKey === undefined) { + throw new Error('Set WAX_TESTNET_ACTOR and WAX_TESTNET_PRIVATE_KEY.') +} + +const session = new Session({ + actor, + permission: 'active', + chain: Chains.WAXTestnet, + walletPlugin: new WalletPluginPrivateKey(privateKey), +}) +``` + +`chain` and `walletPlugin` are required, and the identity arrives either as `actor` plus `permission` or as a single `permissionLevel`. With neither form present the constructor throws `Either a permissionLevel or actor/permission must be provided when creating a new Session.` before any network call. `session.actor` and `session.permission` are read off `session.permissionLevel`, which is the value the snippets in the guides pass as `authorization: [session.permissionLevel]`, so the session is the single place the signing identity is written down. + +`session.transact()` accepts one `action`, an `actions` array, a whole `transaction`, or a signing request, which is why some guides show `{ action: ... }` and others `{ actions: [...] }` for the same session. + +The private-key plugin takes the key itself as its one constructor argument, holds it in memory unencrypted, and signs the transaction digest for the chain id the session carries. That shape belongs in a script or a continuous-integration job: read the key from the environment, never from a committed file, and use a testnet key for anything a guide walks through. + +Source: `@wharfkit/session (1.6.1) src/session.ts:51-57` (`SessionArgs`), `:125-138` (the required fields, the two identity branches, and the throw), `:192-201` (`actor` and `permission` read off `permissionLevel`), `:206-208` (the `APIClient` built from `chain.url`), `src/transact.ts:187-200` (`TransactArgs`); `@wharfkit/wallet-plugin-privatekey (1.1.0) src/index.ts:33-46` (the constructor), `:68-83` (`sign`, digest over `context.chain.id`) + +## The account and permission shape + +An Antelope signing identity is a pair written `actor@permission`: the account that signs and the named permission it signs with. Use `active` unless the account has a custom permission linked to the action, which is the case the pair exists to express. The constructor applies no default of its own: `actor` and `permission` are composed into `actor@permission` and parsed as a permission level, so omitting `permission` and passing no `permissionLevel` throws rather than assuming `active`. `permissionLevel: 'mycreator11@active'` is the same identity written as one string. + +Source: `@wharfkit/session (1.6.1) src/session.ts:130-138` (the `permissionLevel` branch, the `actor` and `permission` branch composing the pair, and the throw when neither form is present) + +## Chain ids + +A chain definition is a chain id and a node URL. The id is what a signature commits to, so a wrong one produces a transaction the target chain rejects rather than a network error. `Chains` carries a ready definition per chain, and a deployment of your own goes in as a plain `{ id, url }` object instead. + +| Chain | `Chains` constant | Chain id | Probed node | +| --- | --- | --- | --- | +| WAX mainnet | `Chains.WAX` | `1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4` | `https://wax.greymass.com` | +| WAX testnet | `Chains.WAXTestnet` | `f16b1833c747c43682f4386fca9cbb327929334a762755ebec17f6f23c9b8a12` | `https://waxtestnet.greymass.com` | +| Jungle4 testnet | `Chains.Jungle4` | `73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d` | `https://jungle4.api.eosnation.io` | +| Vaulta | `Chains.Vaulta` | `aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906` | `https://vaulta.api.atomicassets.io` | +| XPR Network | `Chains.XPR` | `384da888112027f0321850a169f737c33e53b388aad48b5adace4bab97f437e0` | `https://xpr.api.atomicassets.io` | +| XPR Network testnet | `Chains.XPRTestnet` | `71ee83bcf52142d61019d95f9cc5427ba6a0d7ff8accd9e2088ae2abeaf3d3dd` | `https://test.xpr.api.atomicassets.io` | + +Two pairs of constants carry one id each, because each pair is one chain under two names: `Chains.Vaulta` and `Chains.EOS` both carry `aca376f2...`, and `Chains.XPR` and `Chains.Proton` both carry `384da888...`. Only the Vaulta pair differs at all, in the system token it names, which is a rebrand rather than a second chain; the XPR pair is identical field for field. A signature commits to the same id whichever constant the code reaches for. + +These six are the chains the SDK network factories name (`wax`, `wax-testnet`, `vaulta`, `xpr`, `xpr-testnet`, `jungle4`); see [@atomichub/atomicassets SDK](../reference/sdk/atomicassets.md#network-factories-carry-atomichubs-public-hosts) ("Network factories carry AtomicHub's public hosts") for the host each key resolves to. + +Source: live `GET /v1/chain/get_info` against each node in the table, each returning the `chain_id` on its row; the same six ids read from `@wharfkit/common (1.5.0) src/common/chains.ts:202-334` (`Chains`) and `:339-354` (`chainIdsToIndices`), which agree with the live reads + +## WAX testnet is where V2 runs + +WAX testnet runs the full V2 AtomicAssets and AtomicMarket contracts, WAX mainnet still runs V1, and jungle4 carries the V2 code with its tables unseeded, so a session that has to exercise V2 behavior points at `Chains.WAXTestnet`. See [AtomicAssets V2 upgrade](../reference/atomicassets/v2-upgrade.md#deployment-status) ("Deployment status") for the live reads behind that split and for why the contract's own `version` field does not settle it. + +Source: `reference/atomicassets/v2-upgrade.md` ("Deployment status"), which cites the live `get_abi` and `get_table_rows` reads across the three chains diff --git a/reference/sdk/atomicassets.md b/reference/sdk/atomicassets.md index 4f6e6b9..473c522 100644 --- a/reference/sdk/atomicassets.md +++ b/reference/sdk/atomicassets.md @@ -209,7 +209,7 @@ Source: atomicassets-sdk (v2.1.1, 5c70c62) src/Errors/ApiError.ts, src/Errors/Rp ## SDK action output composes directly with @wharfkit session.transact -The action objects the generator returns are already in the shape WharfKit's `session.transact({ actions })` accepts: `{ account, name, authorization, data }`, with each authorization entry an `{ actor, permission }` object matching the SDK's `EosioAuthorizationObject`. So a signer flow is `session.transact({ actions: await explorerApi.action.then(a => a.mintasset(auth, ...)) })`, where `auth = [{ actor, permission }]`. Because the data-bearing actions carry the attribute-map shape (not serialized bytes), the ABI encoding happens inside WharfKit and nodeos at transact time, the same as any hand-built action. The SDK's job ends at producing the action array. For WharfKit's table-read and authority behavior, and its eosjs-migration caveats, see [@wharfkit/antelope client behavior](../wharfkit.md). +The action objects the generator returns are already in the shape WharfKit's `session.transact({ actions })` accepts: `{ account, name, authorization, data }`, with each authorization entry an `{ actor, permission }` object matching the SDK's `EosioAuthorizationObject`. So a signer flow is `session.transact({ actions: await explorerApi.action.then(a => a.mintasset(auth, ...)) })`, where `auth = [{ actor, permission }]`. Because the data-bearing actions carry the attribute-map shape (not serialized bytes), the ABI encoding happens inside WharfKit and nodeos at transact time, the same as any hand-built action. The SDK's job ends at producing the action array. See [Build a session and sign](../../guides/signing.md) for the `session` that array is handed to. For WharfKit's table-read and authority behavior, and its eosjs-migration caveats, see [@wharfkit/antelope client behavior](../wharfkit.md). Source: atomicassets-sdk (v2.1.1, 5c70c62) src/Actions/Generator.ts:4-18 (`EosioActionObject`, `EosioAuthorizationObject`), src/Actions/Generator.ts:855-857 (`_authorize`) diff --git a/validation-log.md b/validation-log.md index b067767..2bec346 100644 --- a/validation-log.md +++ b/validation-log.md @@ -17,6 +17,7 @@ This log traces how every fact in `reference/` and `guides/` was checked before - `atomicassets-sdk` at tag `v2.1.1`, commit `5c70c62` (published as `@atomichub/atomicassets` 2.1.1) - `atomicmarket-sdk` at tag `v2.4.1`, commit `437300b` (published as `@atomichub/atomicmarket` 2.4.1) - `@wharfkit/antelope` at `1.1.1` +- `@wharfkit/session` at `1.6.1`, with `@wharfkit/common` at `1.5.0` and `@wharfkit/wallet-plugin-privatekey` at `1.1.0` - `@atomichub/vert` at `2.2.0`, commit `a8a4160` A page's `key-modules` frontmatter names the specific baseline(s) it draws from; entries below carry the same pin unless noted otherwise. @@ -64,11 +65,12 @@ WAX mainnet still runs the V1 `atomicassets` and `atomicmarket` contracts (confi | `guides/offers.md` | `atomicassets-contract` (v2.0.0-rc4): `src/atomicassets.cpp` | source-read | The underlying `createoffer`/`acceptoffer` primitive and how AtomicMarket sales build on it, all cited to contract source. | | `guides/querying-the-api.md` | Synthesizes `reference/api.md`, `reference/wharfkit.md`, `reference/chain.md`, `reference/atomicmarket/v2-changes.md`, `reference/sdk/atomicassets.md`; live curl examples against `wax.api.atomicassets.io` | both | This page has no `Source:` line of its own; it links back to the reference page carrying each cited fact and shows live curl output for the pagination and lifecycle-state examples. The percent-encoding section is source-read from both SDK Explorer clients and live-confirmed for the colon case: `data:text.rarity=Common` and `data%3Atext.rarity=Common` return the same rows from the WAX deployment. The testnet-host guidance is promoted to its own section with a host table and no new facts. | | `guides/sales.md` | `atomicmarket-contract` (v2.0.0-rc2): `src/atomicmarket.cpp`; `atomicmarket-sdk` (`v2.4.1`, `437300b`): `src/Actions/Generator.ts`, `src/Actions/Delphi.ts`; live `get_table_rows` curl examples against `wax.greymass.com` | both | Lifecycle steps (`announcesale`, `purchasesale`, `cancelsale`, Delphi pricing) cite contract source; three sections show live `get_table_rows` curl calls. The two composer sections and the `settlement_quantity` rules are source-read from the 2.4.1 builder: the emitted action order, the `sale` and `deposit` memo literals, the `allow_v1_bundle_sale` throw, and both settlement branches are read by line. No transaction was broadcast. | +| `guides/signing.md` | `@wharfkit/session` (1.6.1): `src/index.ts`, `src/session.ts`, `src/transact.ts`; `@wharfkit/common` (1.5.0): `src/common/chains.ts`; `@wharfkit/wallet-plugin-privatekey` (1.1.0): `src/index.ts`; live `GET /v1/chain/get_info` against one public node per chain in the table | both | Live-chain: every chain id on the page was read from a running node (`wax.greymass.com`, `waxtestnet.greymass.com`, `jungle4.api.eosnation.io`, `vaulta.api.atomicassets.io`, `xpr.api.atomicassets.io`, `test.xpr.api.atomicassets.io`), and the six values match the constants in the pinned `@wharfkit/common` source, so the table is confirmed twice over. The no-credential read claim is live-chain too: an anonymous `GET /atomicassets/v1/assets?limit=1` against the hosted WAX deployment and an anonymous `get_table_rows` of `atomicassets` `collections` both answer with rows. Source-read at the pinned versions: the `Session` constructor's required fields, its two identity branches and the exact throw text, `actor` and `permission` reading off `permissionLevel`, the `TransactArgs` shapes, the re-export set that makes two packages enough, and the private-key plugin's single-argument constructor and digest signing. No transaction was signed or broadcast. The V2 deployment sentence carries no source of its own and cites `reference/atomicassets/v2-upgrade.md`. | | `guides/testing-with-vert.md` | `@atomichub/vert` (2.2.0, commit `a8a4160`): `src/antelope/blockchain.ts`, `src/antelope/vm.ts`, `src/antelope/table.ts`, `examples/` | source-read | Cites the emulator, VM host functions, and table store by line range. Beyond the source read, every code snippet was executed: the library's own suite runs 35 passing on Node 22, the `fixtures` and `timer` examples pass end-to-end, and purpose-built probe contracts confirmed the notification/inline transaction-context limit, the `set`-injected `modify` abort, the permission-exists throw, and the `createContract` load timing. Tiered source-read rather than live-chain because in-process WASM emulation is not a chain read. | ## Tier distribution -22 source-read, 1 live-chain, 13 both. 36 pages total. +22 source-read, 1 live-chain, 14 both. 37 pages total. ## Pages with an ambiguous tier signal From 27449268c23932a9eb2e43efed6f404528bcd539 Mon Sep 17 00:00:00 2001 From: Rob Konsdorf Date: Tue, 18 Aug 2026 02:03:38 -0400 Subject: [PATCH 2/5] docs: route AGENTS.md by the outcome an agent arrives with The routing table answered "which document covers this subject" while an agent arrives holding an outcome, so "mint an asset on testnet" resolved only by inference across three pages. Each row is now an outcome and the one page that serves it, ordered the way an integration meets them. The reading rules also name the pins a version-sensitive fact has to be re-checked against. The instruction to re-check such facts was already there and gave the reader nothing to check against. --- AGENTS.md | 95 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 11abed2..52f852c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,56 +1,61 @@ --- -scope: Domain-to-file routing table and entry point for agents working in this repository +scope: Outcome-to-file routing for agents working in this repository, plus the version pins a fact has to be re-checked against depends-on: [] key-modules: [] --- # AGENTS.md -Start here. Identify the domain the task touches, then read the matching file below in full; see README.md for what this repository covers and how facts are validated. +Start here. Find the outcome you are working toward below, read the file on that row in full, then come back for the next one. See README.md for what this repository covers and how its facts were validated. ## Routing table -| Working on | Read | +| To do this | Read | | --- | --- | -| AtomicMarket fee model: maker/taker fees, collection fee timing, royalty splits, bonus fees | `reference/atomicmarket/fees-and-royalties.md` | -| AtomicMarket marketplaces: registration, name constraints, maker/taker attribution, fee collection | `reference/atomicmarket/marketplaces.md` | -| AtomicMarket RAM: who pays for which row, `pay*ram` actions, sizing for high-volume marketplaces | `reference/atomicmarket/ram.md` | -| AtomicMarket action reference: every action, parameters, RAM payer, "Changed in V2" notes | `reference/atomicmarket/actions.md` | -| AtomicMarket tables (schema, scopes, indexes) | `reference/atomicmarket/tables.md` | -| AtomicMarket V2 behavior changes and defensive guards: bundle retirement, execution-time fees, trace-only royalty logs, guards | `reference/atomicmarket/v2-changes.md` | -| AtomicAssets data model: collections, schemas, templates, assets | `reference/atomicassets/structure.md` | -| AtomicAssets action reference: every action, parameters, auth, notifications, RAM payer, "Changed in V2" notes | `reference/atomicassets/actions.md` | -| AtomicAssets tables (schema, scopes, indexes) | `reference/atomicassets/tables.md` | -| AtomicAssets attribute type system: FORMAT, ATOMIC_ATTRIBUTE, ATTRIBUTE_MAP | `reference/atomicassets/custom-types.md` | -| AtomicAssets attribute serialization: binary encoding of collection/template/asset data | `reference/atomicassets/serialization.md` | -| AtomicAssets attribute data precedence: template vs asset immutable vs asset mutable data | `reference/atomicassets/data-precedence.md` | -| AtomicAssets notifications: collection notify accounts, require_recipient targets per action | `reference/atomicassets/notifications.md` | -| AtomicAssets backing tokens: deposit/backasset flow, balances table, burnasset recovery | `reference/atomicassets/backing-tokens.md` | -| AtomicAssets V2 upgrade and indexer/chain compatibility | `reference/atomicassets/v2-upgrade.md` | -| AtomicTools claim-links action reference: announce, fund, claim, cancel, the signature claim mechanism | `reference/atomictools/actions.md` | -| AtomicTools tables (links, config): schema, scope, the assetidshash index | `reference/atomictools/tables.md` | -| Asset media: IPFS references, field-name conventions (img/backimg/video), bare-CID vs URL parsing, gateway resolution, V2 media-type descriptors | `reference/media.md` | -| Calling atomicassets-api HTTP endpoints: pagination limits, buyoffer lifecycle states, rate limits | `reference/api.md` | -| atomicassets-api realtime/streaming: Socket.IO namespaces, events, room subscription, socket auth and limits | `reference/api-streaming.md` | -| Running or extending the atomicassets-api indexer (also known as eosio-contract-api): handler configuration, drain gating, data freshness, migrations, CI | `reference/atomicassets-api.md` | -| Building or deploying contract releases: ABI artifacts, setcode resource needs, deploy permissions | `reference/contract-releases.md` | -| @wharfkit/antelope client behavior: table reads, authority checks, show_payer, eosjs migration | `reference/wharfkit.md` | -| @atomichub/atomicassets JS/TS SDK: ExplorerApi/RpcApi reads, attribute serialization, V2 action building | `reference/sdk/atomicassets.md` | -| @atomichub/atomicmarket JS/TS SDK: sales/auctions/buyoffers reads, V2 royalty read layer, royalty-config action building | `reference/sdk/atomicmarket.md` | -| Chain RPC error semantics: account-not-found and error classification | `reference/chain.md` | -| AtomicAssets creator flow end to end: create collection, schema, template, mint, transfer, burn | `guides/asset-lifecycle.md` | -| AtomicAssets offers: two-sided trade offers, accept, decline, cancel | `guides/offers.md` | -| AtomicMarket sales end to end: announce, purchase, cancel, Delphi-priced sales | `guides/sales.md` | -| AtomicMarket auctions end to end: announce, bid, claim, cancel | `guides/auctions.md` | -| AtomicMarket buyoffers and template buyoffers: create, accept, decline, cancel | `guides/buyoffers.md` | -| AtomicMarket deposits: balances, deposit and withdraw, which actions consume balance | `guides/deposits.md` | -| AtomicTools claim-links end to end: generate link key, announce, fund by transfer, claim by signature, cancel, reading links | `guides/links.md` | -| Building a contract that reacts to AtomicAssets notifications: on_notify wiring, exact-match signatures, same-transaction safety | `guides/notification-integration.md` | -| Testing Antelope contracts in-process with @atomichub/vert: setup, per-chain host functions, time and auth control, known limits | `guides/testing-with-vert.md` | -| Querying the API and reading chain tables end to end | `guides/querying-the-api.md` | - -## How to use this repo as an agent - -- Look up the domain file above before coding against a contract, the indexer, or the API: the facts there encode behavior you cannot guess from method signatures. -- Each fact stands alone; take a section at face value and do not extrapolate beyond what it states. -- When a fact names a specific library version (for example @wharfkit/antelope 1.1.1), re-check that fact when the dependency is upgraded. +| Read assets, sales, and collections over HTTP, with no key and no account | `guides/querying-the-api.md` | +| Look up one hosted-API endpoint: pagination caps, lifecycle states, rate limits, the two sales routes | `reference/api.md` | +| Classify a chain RPC error, and tell a missing account apart from a broken node | `reference/chain.md` | +| Read asset data and build AtomicAssets actions from JavaScript or TypeScript | `reference/sdk/atomicassets.md` | +| Read market data and compose AtomicMarket flows from JavaScript or TypeScript | `reference/sdk/atomicmarket.md` | +| Read a table or check an authority through the client library, or migrate off eosjs | `reference/wharfkit.md` | +| Subscribe to live asset, sale, and auction events over Socket.IO | `reference/api-streaming.md` | +| Show an asset's image or video from what the chain actually stores | `reference/media.md` | +| Sign anything: build the session, pick the chain, install the signer | `guides/signing.md` | +| Mint an asset on testnet, from collection and schema through transfer and burn | `guides/asset-lifecycle.md` | +| Work out what a collection, a schema, a template, and an asset each own | `reference/atomicassets/structure.md` | +| Look up one AtomicAssets action: parameters, authorization, RAM payer, V2 changes | `reference/atomicassets/actions.md` | +| Read or index one AtomicAssets table row | `reference/atomicassets/tables.md` | +| Choose an attribute type, or find out why a value was rejected | `reference/atomicassets/custom-types.md` | +| Decode an attribute blob read straight from a chain table | `reference/atomicassets/serialization.md` | +| Decide which layer an attribute value comes from when template and asset disagree | `reference/atomicassets/data-precedence.md` | +| Find out whether a chain runs V2 yet, and what V2 added | `reference/atomicassets/v2-upgrade.md` | +| Handle a token-backed asset minted before backing was deprecated | `reference/atomicassets/backing-tokens.md` | +| List an asset for sale and settle the purchase, oracle-priced sales included | `guides/sales.md` | +| Run an auction from announce through claim | `guides/auctions.md` | +| Offer to buy an asset nobody has listed, or accept such an offer | `guides/buyoffers.md` | +| Swap assets with another account without going through the market | `guides/offers.md` | +| Fund a buyer's balance, or find out why a payout is still parked | `guides/deposits.md` | +| Work out what a settlement actually pays: fee stack, royalty splits, the dust rule | `reference/atomicmarket/fees-and-royalties.md` | +| Look up one AtomicMarket action | `reference/atomicmarket/actions.md` | +| Read or index one AtomicMarket table row | `reference/atomicmarket/tables.md` | +| Find what V2 changed on the market, bundle retirement and the guards included | `reference/atomicmarket/v2-changes.md` | +| Run a storefront that earns maker and taker fees | `reference/atomicmarket/marketplaces.md` | +| Size RAM for a busy marketplace, and find out who pays for which row | `reference/atomicmarket/ram.md` | +| Hand assets to someone who has no account yet, through a claim link | `guides/links.md` | +| Look up one AtomicTools action | `reference/atomictools/actions.md` | +| Read the claim-link tables | `reference/atomictools/tables.md` | +| React to contract notifications from a contract of your own | `guides/notification-integration.md` | +| Find which action notifies which account, and what a handler may assume | `reference/atomicassets/notifications.md` | +| Test contract logic in-process before spending a testnet deploy | `guides/testing-with-vert.md` | +| Build and deploy a contract release: ABI artifacts, setcode resources, permissions | `reference/contract-releases.md` | +| Run or extend the atomicassets-api indexer | `reference/atomicassets-api.md` | + +## Working in this repository + +Read the routed file in full rather than searching it. The facts there encode behavior no method signature carries: which account is billed for a row, which read answers with a null instead of an error, which flag lets a transaction commit while delivering nothing. + +Take each section at face value and do not extrapolate past what it states. A page says what was checked, and what it does not say was not checked. `validation-log.md` records how every page was validated and against what. + +Re-check any fact that names a version when that dependency moves. The two SDK pages are pinned to `@atomichub/atomicassets` 2.1.1, read at tag `v2.1.1`, and `@atomichub/atomicmarket` 2.4.1, read at tag `v2.4.1`; the client-library page is pinned to `@wharfkit/antelope` 1.1.1, and the AtomicAssets and AtomicMarket contract pages to `v2.0.0-rc4` and `v2.0.0-rc2`. A fact read at one of those pins is a fact about that release, not about the package name. + +Two skills sit beside these pages. `skills/atomic-integration/SKILL.md` carries the procedures a routing table cannot: the mint flow, the market composers, and the network choice. `skills/report/SKILL.md` writes a difficulty report when a page here turned out wrong, missing, or misleading, in the field shape this repository's issue forms accept. From cd1efcda6e1e17c73c12ed228c33dec5fefbe1e6 Mon Sep 17 00:00:00 2001 From: Rob Konsdorf Date: Tue, 18 Aug 2026 02:03:42 -0400 Subject: [PATCH 3/5] docs: cut CLAUDE.md down to the pointer it is Six of its lines were content and three of those restated AGENTS.md, so a vendor-named file at the root of a neutral protocol corpus held a second copy of rules that drift apart the moment one side is edited. --- CLAUDE.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e2e945a..c8b4662 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,15 +1,9 @@ --- -scope: Claude Code entry point; points to AGENTS.md for domain routing +scope: Claude Code entry point; points at AGENTS.md, which carries the routing and the rules depends-on: [] key-modules: [] --- # CLAUDE.md -This file orients Claude Code sessions in this repository. See README.md for what it covers and how facts are validated. - -Read AGENTS.md for the routing table. - -- Statements here are validated, but versions move: re-verify version-sensitive facts (library versions, contract releases, operator-configurable limits) against current source before relying on them. -- Prefer `reference/` for facts and `guides/` for workflows. -- Each fact stands alone; do not extrapolate beyond what a section states. +Read [AGENTS.md](AGENTS.md). From 8f3ecc2515d65c5585dcf0904e39012cf9a5dfac Mon Sep 17 00:00:00 2001 From: Rob Konsdorf Date: Tue, 18 Aug 2026 02:03:47 -0400 Subject: [PATCH 4/5] docs: put the mint and composer procedure in the integration skill The skill cost a load to say "see AGENTS.md", which is the file the caller had just read. It now carries what a routing table cannot: the five-step mint with the RAM payer that bills the minter rather than the recipient, the five market composers with their memo literals and the last-offer placement rule, the two bundle opt-outs that guard the caller error which commits instead of reverting, and what a settlement quantity has to be. The network choice leads, because reaching for mainnet first is the one mistake here that costs real assets. --- skills/atomic-integration/SKILL.md | 83 ++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/skills/atomic-integration/SKILL.md b/skills/atomic-integration/SKILL.md index c878c2d..01e236a 100644 --- a/skills/atomic-integration/SKILL.md +++ b/skills/atomic-integration/SKILL.md @@ -1,7 +1,7 @@ --- name: atomic-integration -description: "Use when building or debugging an integration with AtomicAssets/AtomicMarket contracts, the atomicassets-api indexer, or WAX chain reads: routes to the matching reference file" -scope: Routes Atomic integration tasks to the matching reference file +description: "Use when building or debugging an integration with the AtomicAssets or AtomicMarket contracts, the atomicassets-api indexer, or WAX and Antelope chain reads: carries the mint flow, the market composers, and the network choice" +scope: "The procedures a routing table cannot carry: choosing the network, minting an asset end to end, and composing an AtomicMarket flow without tripping a guard" depends-on: [] key-modules: [] --- @@ -10,15 +10,78 @@ key-modules: [] ## When to use -Use this skill whenever the task touches the Atomic ecosystem on WAX/Antelope chains: listing or trading through the AtomicMarket contract, reading AtomicAssets state, calling atomicassets-api endpoints, running or extending the atomicassets-api indexer, deploying contract releases, or reading chain tables and accounts through @wharfkit/antelope or raw RPC. +Use this skill whenever the task touches the Atomic ecosystem on WAX or another Antelope chain: minting or editing assets through AtomicAssets, listing or trading through AtomicMarket, calling atomicassets-api endpoints, running the indexer, or reading chain tables and accounts through WharfKit or raw RPC. -## Steps +What follows is what an integration gets wrong from the reference pages alone: the order the actions go in, the guard that commits instead of reverting, and the flag that turns it off. The per-fact detail lives in `AGENTS.md` and the files it routes to; read the routed file in full before writing code against it. -1. Identify the domain the task touches (contract, indexer, API, chain RPC, client library, releases). -2. Read the matching `reference/` file(s) in full; see the routing table in `AGENTS.md` for the domain-to-file mapping. -3. Follow `guides/querying-the-api.md` for API access patterns (pagination, state filters, table reads, error classification). -4. Re-verify version-pinned facts (library versions, contract releases, operator-configurable limits) against current source before relying on them. +## Choose the network before anything else -## Routing table +Work on WAX testnet first. Every flow below is the same on mainnet, and the mistakes below are recoverable on testnet and not on mainnet. -For the domain-to-file routing table, see `AGENTS.md`. +| Read | WAX mainnet | WAX testnet | +| --- | --- | --- | +| Chain tables and RPC | `https://wax.greymass.com` | `https://waxtestnet.greymass.com` | +| Hosted HTTP API | `https://wax.api.atomicassets.io` | `https://test.wax.api.atomicassets.io` | + +Switching network swaps both hosts, never one. The `atomicassets` and `atomicmarket` contract accounts carry the same names on both chains, so nothing in an action's data changes and a half-switched integration reads an unrelated chain while every request keeps answering 200. + +WAX testnet is where V2 runs. WAX mainnet still runs the V1 contracts, and jungle4 carries the V2 code with its tables unseeded, so any V2-only behavior (the royalty layer, the bundle retirement, the new tables) is exercisable on testnet and absent on mainnet. The contract's own `version` field does not settle which one a chain runs; table presence does. + +Reads need no key, no account, and no registration. A key is needed only to sign. Build the session first, from `guides/signing.md`, which carries the install lines, the chain ids, and the actor and permission pair. + +## Mint an asset + +Five steps, in this order. Steps 2 through 5 are each a separate transaction carrying its own authorization. + +1. Build the session. It carries the actor, the permission, and the chain id every action below signs against. +2. `createcol` creates the collection. The `author` signs and pays the RAM. A name that is already a registered account, or that carries a dot suffix, needs the matching account to co-sign; a plain name must be exactly 12 characters. +3. `createschema` defines the field names and their ABI types for the collection. The `authorized_creator` signs and pays. +4. `createtempl` is optional. A template stores data shared by many assets once instead of once per asset, and sets the `transferable`, `burnable`, and `max_supply` policy those assets inherit; `max_supply: 0` means unlimited. Skip the step and mint templateless assets instead, passing `template_id: -1`. +5. `mintasset` mints. The `authorized_minter` signs and pays the RAM, not `new_asset_owner`: the row lives in the owner's scope, but the minter is billed, so a minter with too little RAM staked blocks its own mint however well resourced the recipient is. + +Native token backing is gone in V2. A non-empty `tokens_to_back` aborts the mint and `backasset` fails unconditionally, so pass an empty array. + +Build the mint through `@atomichub/atomicassets` rather than by hand. `ActionBuilder.mintasset()` takes the eight parameters in ABI order and returns one plain `{ account, name, data }` object for the session to sign, and `createAttributeMap` turns a plain object plus a per-field type lookup into the attribute-map shape, so no schema fetch is needed to build one. The builder checks the numeric parameters and throws a `SerializationError` naming the offending field before any transaction exists: `template_id` is checked as an int32, which keeps `-1` usable and rejects the `NaN` a string-to-number conversion produces, and `createtempl`'s `max_supply` is checked as a uint32, so a fractional or negative supply fails at the call. Without that check a `NaN` reaches the signer as `null`, because JSON has no form for it, and the mistake is invisible by the time the chain sees it. + +Full detail: `guides/asset-lifecycle.md` for the flow and every failure mode, `reference/sdk/atomicassets.md` for the builder surface. + +## Compose an AtomicMarket flow + +Never assemble a listing or a purchase action by action. `@atomichub/atomicmarket` ships five composers that emit the whole flow in the order the contract requires, with the memo literals and the owning contract account filled in. + +| Composer | Emits, in order | Refuses | +| --- | --- | --- | +| `announceSaleActions` | `announcesale`, then the AtomicAssets `createoffer` with memo `sale` | nothing; the rest is chain state | +| `purchaseSaleActions` | `assertsale`, the settlement token's `transfer` with memo `deposit`, then `purchasesale` | a bundle `asset_ids`, and a wrong `settlement_quantity` | +| `announceAuctionActions` | `announceauct`, then the AtomicAssets `transfer` with memo `auction` | nothing; the fixed order is the point | +| `acceptBuyofferActions` | the AtomicAssets `createoffer` with memo `buyoffer`, then `acceptbuyo` | a bundle `asset_ids` | +| `fulfillTemplateBuyofferActions` | the AtomicAssets `createoffer` with memo `tbuyoffer`, then `fulfilltbuyo` | nothing; a template buyoffer names one asset | + +`MarketActionBuilder` is synchronous and authorization-free. `MarketActionGenerator` wraps the same methods as `async` ones that take an authorization array first and return the action array a session accepts. + +The two offer-consuming composers carry a placement rule the caller has to respect. `acceptbuyo` and `fulfilltbuyo` read the globally last created row of the AtomicAssets offers table, so the `createoffer` has to sit in the same transaction immediately before the market action, with no other `createoffer` between them. Actions appended after the market action are safe, because its inline `acceptoffer` has consumed the row by then. Neither composer accepts the offer itself, for the same reason: a pre-accepted offer is gone from the table before the market contract looks. + +### The two bundle opt-outs + +`purchaseSaleActions` throws on more than one asset id unless `allow_v1_bundle_sale` is set, and `acceptBuyofferActions` does the same behind `allow_v1_bundle_buyoffer`. Each guards the one caller error in its family that commits instead of reverting. + +On a purchase, V2 `purchasesale` returns early for a multi-asset row: it declines the offer, erases the row, and returns before touching any balance, while `assertsale` has already passed and the deposit has already credited the buyer. The transaction commits with the buyer paid, nothing delivered, and the tokens recoverable only through a separate `withdraw`. On an accept, V2 `acceptbuyo` refunds the escrowed price and erases the buyoffer row before it reads the offers table, leaving the composer's offer dangling on the recipient's RAM until they cancel it. + +Set either flag only against a chain still running AtomicMarket V1, where bundle rows list and accept correctly. + +### What settlement_quantity has to be + +`purchasesale` spends the buyer's AtomicMarket balance, so the buyer funds it with the deposit transfer in the same transaction, and that transfer is `settlement_quantity`. Nothing on chain checks it: `assertsale` pins the listing terms and says nothing about the deposit. The discriminator is whether `listing_price` and `settlement_symbol` name one symbol, precision and code both. + +| Case | `settlement_quantity` | `intended_delphi_median` | +| --- | --- | --- | +| The two name different symbols | required, denominated in `settlement_symbol` | the median the purchase asserts | +| The two name one symbol | may be omitted; a supplied one equals `listing_price` exactly | `0` | + +Both refusals rule out a transaction the chain would take. Depositing more than the sale costs leaves the surplus sitting as balance, and depositing nothing lets a standing balance pay; each is legitimate for a caller who means it and indistinguishable from a wrong amount for one who does not, and the composer cannot see a balance to tell them apart. To do either deliberately, assemble `assertsale`, your own transfer, and `purchasesale` by hand, which assert nothing. + +Full detail: `reference/sdk/atomicmarket.md` for the composers and the settlement math, `guides/sales.md`, `guides/auctions.md`, and `guides/buyoffers.md` for the lifecycle each one drives. + +## Version pins + +Re-check a fact that names a version when that dependency moves. This skill is written against `@atomichub/atomicassets` 2.1.1 and `@atomichub/atomicmarket` 2.4.1, and the contract behavior against `atomicassets-contract` v2.0.0-rc4 and `atomicmarket-contract` v2.0.0-rc2. `validation-log.md` records how each page was validated and against what. From 2a844976bd2c490cd0853fef9e075776fc81ab5c Mon Sep 17 00:00:00 2001 From: Rob Konsdorf Date: Tue, 18 Aug 2026 02:03:54 -0400 Subject: [PATCH 5/5] docs: align the report fields with the issue forms that receive them A difficulty report and a fact-error issue asked for the same evidence in two different shapes, so filing the issue meant re-deriving the report. An entry now opens with the five fields both issue forms declare, in their order, and the report-only fields follow, which lets a maintainer paste one finding across instead of rewriting it. The field list becomes a table. A uniform run of label-and-colon bullets was already imitating one, and the body guidance stops asking for the quote and the observation the fields now hold. --- skills/report/SKILL.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/skills/report/SKILL.md b/skills/report/SKILL.md index 1f510de..63005d1 100644 --- a/skills/report/SKILL.md +++ b/skills/report/SKILL.md @@ -28,7 +28,7 @@ If a finding cannot be described without exposing something that must stay priva ## Steps 1. Review the conversation for every place atomic-knowledge docs caused friction: a fact that turned out false, a fact needed but absent, a fact that was true but pointed toward the wrong approach, or a design pattern the docs should have taught up front. -2. For each instance, work out which category it falls in (see below), which file and section it concerns, how confident you are, whether it got resolved and how, and roughly how many tool uses it cost before resolution. +2. For each instance, fill every field below: the category, the page and section, the claim quoted as the page writes it, what was observed instead, the chain and endpoint it was observed on, the version it was observed against, the confidence, whether it got resolved and how, and roughly how many tool uses it cost before resolution. 3. Sanitize every finding per the rule above. 4. Determine the consuming project's root (the top of its own git working tree, not atomic-knowledge's). Create `.claude/atomic-knowledge/reports/` there if it does not exist, and write the report to `.claude/atomic-knowledge/reports/.md`, where `` is a kebab-case summary of the task (3-6 words) with a 4-character random hex suffix (`openssl rand -hex 2`) to avoid collisions between reports from concurrent sessions. 5. Print the file path when done. If the session hit no documentation difficulties, write nothing and say so. @@ -48,7 +48,11 @@ Each finding: ``` ## [5-10 word summary of the finding] - category: [doc-error | doc-gap | doc-misleading | design-pattern] -- file: [atomic-knowledge path(s), e.g. reference/atomicmarket/fees-and-royalties.md, or "none identifiable"] +- page: [atomic-knowledge path(s), e.g. reference/atomicmarket/fees-and-royalties.md, or "none identifiable"] +- claim: [the sentence as the page writes it, quoted; for a gap, the fact the pages had to state] +- observed: [the chain response, error text, or API payload that settled it, verbatim] +- chain: [chain and endpoint, e.g. WAX mainnet through wax.greymass.com] +- contract-version: [the contract, indexer, or library version the observation was made against, or "unknown"] - confidence: [verified | inferred] - resolved: [yes, how it was resolved | no] - cost: [tool uses spent before resolving or giving up, rounded to the nearest 5] @@ -56,6 +60,8 @@ Each finding: [Freeform body] ``` +The five fields from `page` through `contract-version` are the atomic-knowledge issue forms, field for field, so a maintainer opens an issue by pasting one finding across rather than re-deriving it. The `category` picks the form: a `doc-error` or `doc-misleading` finding fills the fact-error form; a `doc-gap` or `design-pattern` finding fills the missing-fact form. The last three fields have no form field and stay in the report. + ## Categories | Category | Meaning | @@ -67,11 +73,17 @@ Each finding: ## Fields -- **category**: exactly one of the four above; split a finding that spans two. -- **file**: the specific atomic-knowledge file and, if identifiable, section or heading. Say "none identifiable" rather than guessing. -- **confidence**: `verified` if checked against contract/indexer source or a live chain or API read; `inferred` if it is your best understanding but unconfirmed. Default to `inferred` when unsure. -- **resolved**: state the actual fix if resolved (the working code or approach is the strongest evidence). If unresolved, say what was tried. -- **cost**: tool uses (searches, reads, failed attempts) spent on this specific issue. This is the proxy for how much the gap hurt; it drives ordering, not a precision metric. +| Field | What it holds | +| --- | --- | +| `category` | Exactly one of the four above. Split a finding that spans two. | +| `page` | The specific atomic-knowledge file and, where identifiable, the section or heading. For a `doc-gap`, every page that was read before concluding the fact was missing, because a fact present on one page and absent from the page the task started at is a missing cross-reference rather than missing content. Say "none identifiable" rather than guessing. | +| `claim` | The sentence as the page writes it, quoted rather than paraphrased. For a `doc-gap` or a `design-pattern`, the fact or pattern the pages had to state for the task to proceed. | +| `observed` | The chain response, error text, or API payload that settled the question, verbatim, with the contract or indexer source file and line range if source was read to settle it. | +| `chain` | The chain and the endpoint the observation came from, for example WAX mainnet through `wax.greymass.com`. | +| `contract-version` | The contract, indexer, or library version the observation was made against. Say "unknown" rather than guessing; the form treats it as optional. | +| `confidence` | `verified` if checked against contract or indexer source or a live chain or API read, `inferred` if it is a best understanding and unconfirmed. Default to `inferred` when unsure. | +| `resolved` | The actual fix if resolved, since the working code or approach is the strongest evidence. If unresolved, what was tried. | +| `cost` | Tool uses (searches, reads, failed attempts) spent on this specific issue. This is the proxy for how much the gap hurt; it drives ordering and is not a precision metric. | ## Prioritization @@ -79,16 +91,12 @@ Order findings by cost descending. A finding that took 30 tool uses to work arou ## Freeform body guidance -Write the body so a maintainer with no session context can act on it without asking follow-up questions. +Write the body so a maintainer with no session context can act on it without asking follow-up questions. The fields above already carry the quoted claim and the observation, so the body carries what they cannot: -Include: -- What the doc said (quote it) or state plainly that the fact was absent. -- What was expected based on the doc, and what actually happened (error text, chain response, indexer behavior) verbatim. +- What was expected from the page, and why the observed behavior contradicts it rather than merely differing from it. - The working pattern, if resolved: the strongest evidence a fix is correct is the code that now works. -- Contract or indexer source file and line range, if source was read to settle the question. This saves the maintainer from repeating the search. -- The workaround used, kept separate from what the doc should say instead; they are often different. - -For `doc-gap` findings, name which atomic-knowledge files were checked before concluding the fact was missing. A gap found after checking one file may really be a missing cross-reference, not missing content. +- The workaround used, kept separate from what the page should say instead; they are often different. +- Where the fact belongs, if the page it was looked for on is not the page it should live on. Do not include: - Vague complaints ("the docs were confusing") without the doc text and the observed behavior side by side.