From f92a89a7651260d5622a609a1a7143dcc407eff8 Mon Sep 17 00:00:00 2001 From: Mark Sackerberg <93528482+MarkSackerberg@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:41:02 +0200 Subject: [PATCH] add DAS features --- README.md | 6 +- skills/metaplex/SKILL.md | 7 +- skills/metaplex/references/sdk-agent.md | 22 ++ skills/metaplex/references/sdk-bubblegum.md | 9 +- skills/metaplex/references/sdk-core.md | 17 +- skills/metaplex/references/sdk-das.md | 213 ++++++++++++++++++++ skills/metaplex/references/sdk-umi.md | 32 +-- 7 files changed, 283 insertions(+), 23 deletions(-) create mode 100644 skills/metaplex/references/sdk-das.md diff --git a/README.md b/README.md index f82c45f..6d9bb95 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This skill gives your AI coding agent full knowledge of Metaplex programs, CLI c | **Agent Registry** | On-chain agent identity, wallets, execution delegation | Yes | Yes | - | | **Genesis** | Token launches (launchpool + bonding curve) + Raydium graduation | Yes | Yes | - | | **Core** | Next-gen NFTs — single account, plugins, royalty enforcement | Yes | Yes | - | +| **DAS API** | Indexed asset queries (owner/collection/group/agents) + Core helpers | - | Yes | - | | **Token Metadata** | Fungible tokens, NFTs, pNFTs, editions | Yes | Yes | Yes | | **Bubblegum** | Compressed NFTs via Merkle trees — massive scale at minimal cost | Yes | Yes | - | | **Candy Machine** | NFT drops with guards (allowlists, payments, limits) | Yes | Yes | - | @@ -33,7 +34,7 @@ This skill gives your AI coding agent full knowledge of Metaplex programs, CLI c - Agent Registry: mint agents, register identity, delegate execution, set agent token - Genesis Launch API: launchpool and bonding curve launches, agent integration, creator fees, first buy - Bonding curve swap integration: fetch state, lifecycle helpers, quotes, slippage, execute swaps -- All CLI operations plus: fetch by owner/collection/creator, DAS API queries, delegates, lock/unlock, print editions, verify/unverify creators and collections, freeze/thaw, soulbound NFTs, plugin management +- All CLI operations plus: fetch by owner/collection/creator/group, DAS API queries (`digital-asset-standard-api` + `mpl-core-das`), delegates, lock/unlock, print editions, verify/unverify creators and collections, freeze/thaw, soulbound NFTs, plugin management **Kit SDK** (@solana/kit) — minimal dependencies: - Token Metadata operations: create/transfer NFTs, pNFTs, fungibles, PDAs @@ -46,9 +47,10 @@ The skill uses progressive disclosure — a lightweight router (SKILL.md, ~100 l SKILL.md Router — loaded when skill activates references/cli.md Shared CLI setup (loaded for any CLI task) references/cli-core.md Core CLI commands + references/sdk-das.md DAS API + mpl-core-das helpers references/sdk-token-metadata.md TM SDK patterns references/concepts.md Account structures, PDAs - ... (12 reference files total) + ... (reference files) ``` ## Install diff --git a/skills/metaplex/SKILL.md b/skills/metaplex/SKILL.md index 2b94e65..1d5c09f 100644 --- a/skills/metaplex/SKILL.md +++ b/skills/metaplex/SKILL.md @@ -4,7 +4,7 @@ description: Metaplex development on Solana — NFTs, tokens, compressed NFTs, c license: Apache-2.0 metadata: author: metaplex-foundation - version: "0.3.0" + version: "0.4.0" openclaw: {"emoji":"💎","os":["darwin","linux","win32"],"requires":{"bins":["node"]},"homepage":"https://metaplex.com/docs"} --- @@ -48,6 +48,7 @@ Metaplex provides the standard infrastructure for NFTs and tokens on Solana: | CLI: Fungible tokens | `./references/cli.md` + `./references/cli-toolbox.md` | | SDK setup (Umi) | `./references/sdk-umi.md` | | SDK: Core NFTs | `./references/sdk-umi.md` + `./references/sdk-core.md` + `./references/metadata-json.md` | +| SDK: DAS API (asset queries, Core listing helpers) | `./references/sdk-umi.md` + `./references/sdk-das.md` | | SDK: Token Metadata | `./references/sdk-umi.md` + `./references/sdk-token-metadata.md` + `./references/metadata-json.md` | | SDK: Compressed NFTs (Bubblegum) | `./references/sdk-umi.md` + `./references/sdk-bubblegum.md` + `./references/metadata-json.md` | | SDK: Token Metadata with Kit | `./references/sdk-token-metadata-kit.md` + `./references/metadata-json.md` | @@ -83,7 +84,7 @@ The `mplx` CLI can handle most Metaplex operations directly. **Read `./reference | Compressed NFTs (cNFTs) | ✅ (batch limit ~100, use SDK for larger) | | Execute (asset-signer wallets) | ✅ | | Check SOL balance / Airdrop | ✅ | -| Query assets by owner/collection | ❌ SDK only (DAS API) | +| Query assets by owner/collection/group | ❌ SDK only (DAS API — see `./references/sdk-das.md`) | | Token launch (Genesis) | ✅ | | Bonding curve swap (Genesis) | ✅ | @@ -104,7 +105,7 @@ Core Candy: CMACYFENjoBMHzapRXyo1JZkVS6EtaDDzkjMrmQLvr4J ### Autonomous Agents -Use **Agent Registry** to register on-chain identity and execution delegation for MPL Core assets. The **Mint Agent API** (`mintAndSubmitAgent`) is the recommended path — it creates the Core asset and registers identity in a single transaction. For existing assets, use `registerIdentityV1` directly. Any Core asset already has a built-in wallet (Asset Signer PDA) via Core's Execute hook — the registry adds discoverable identity records and lets owners delegate an off-chain executive to operate the agent. Agents can optionally link a Genesis token via `setAgentTokenV1`. Read `./references/cli-agent.md` (CLI) or `./references/sdk-umi.md` + `./references/sdk-agent.md` (SDK). +Use **Agent Registry** to register on-chain identity and execution delegation for MPL Core assets. The **Mint Agent API** (`mintAndSubmitAgent`) is the recommended path — it creates the Core asset and registers identity in a single transaction. For existing assets, use `registerIdentityV1` directly. Any Core asset already has a built-in wallet (Asset Signer PDA) via Core's Execute hook — the registry adds discoverable identity records and lets owners delegate an off-chain executive to operate the agent. Agents can optionally link a Genesis token via `setAgentTokenV1`. Discover agents via DAS (`searchAssets({ isAgent: true })` — see `./references/sdk-das.md`). Read `./references/cli-agent.md` (CLI) or `./references/sdk-umi.md` + `./references/sdk-agent.md` (SDK). ### Token Launches (Token Generation Event / Fair Launch / Bonding Curve) diff --git a/skills/metaplex/references/sdk-agent.md b/skills/metaplex/references/sdk-agent.md index 842e070..23397ef 100644 --- a/skills/metaplex/references/sdk-agent.md +++ b/skills/metaplex/references/sdk-agent.md @@ -238,6 +238,28 @@ await setAgentTokenV1(umi, { > The agent token can only be set once. Calling on an identity that already has a token fails with `AgentTokenAlreadySet`. +### Discover Agents via DAS + +Registered agents are queryable through the DAS API (requires `dasApi()`). Prefer Core-typed helpers when you need `AssetV1`: + +```typescript +import { dasApi } from '@metaplex-foundation/digital-asset-standard-api'; +import { das } from '@metaplex-foundation/mpl-core-das'; + +umi.use(dasApi()); + +const agents = await das.searchAssets(umi, { + isAgent: true, + // agentToken: genesisMint, // optional + // assetSigner: assetSignerPda, // optional + skipDerivePlugins: true, +}); + +// agents[i].is_agent, .agent_token, .asset_signer when indexed +``` + +See `./sdk-das.md` for full DAS coverage. + --- ## Check Registration diff --git a/skills/metaplex/references/sdk-bubblegum.md b/skills/metaplex/references/sdk-bubblegum.md index 2e875ae..5a3f22c 100644 --- a/skills/metaplex/references/sdk-bubblegum.md +++ b/skills/metaplex/references/sdk-bubblegum.md @@ -274,7 +274,7 @@ await unverifyCreatorV2(umi, { ## Fetch cNFTs (DAS API) -> Requires a DAS-compatible RPC (e.g., Helius, Triton, QuickNode) and the `dasApi()` plugin. See `./sdk-umi.md` DAS section. +> Requires a DAS-compatible RPC (e.g., Helius, Triton, QuickNode) and the `dasApi()` plugin. See `./sdk-das.md`. ```typescript import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'; @@ -294,9 +294,10 @@ const asset = await umi.rpc.getAsset(assetId); // By owner const assets = await umi.rpc.getAssetsByOwner({ owner: walletAddress }); -// By collection -const collectionAssets = await umi.rpc.getAssetsByCollection({ - collection: collectionAddress, +// By collection (base DAS — cNFTs are not Core AssetV1) +const collectionAssets = await umi.rpc.getAssetsByGroup({ + groupKey: 'collection', + groupValue: collectionAddress, }); ``` diff --git a/skills/metaplex/references/sdk-core.md b/skills/metaplex/references/sdk-core.md index 90ee76e..ab2f175 100644 --- a/skills/metaplex/references/sdk-core.md +++ b/skills/metaplex/references/sdk-core.md @@ -243,7 +243,20 @@ const ownerAssets = await fetchAssetsByOwner(umi, ownerAddress); const collectionAssets = await fetchAssetsByCollection(umi, collectionAddress); ``` -> `fetchAssetsByOwner` and `fetchAssetsByCollection` use GPA (getProgramAccounts) queries. They may throw deserialization errors if the wallet/collection has burned asset account remnants. For production, prefer DAS API queries (see `./sdk-umi.md` DAS section). +> `fetchAssetsByOwner` and `fetchAssetsByCollection` use GPA (getProgramAccounts) queries. They may throw deserialization errors if the wallet/collection has burned asset account remnants. For production, prefer DAS via `@metaplex-foundation/mpl-core-das` (see `./sdk-das.md`): + +```typescript +import { dasApi } from '@metaplex-foundation/digital-asset-standard-api'; +import { das } from '@metaplex-foundation/mpl-core-das'; + +umi.use(dasApi()); + +const ownerAssets = await das.getAssetsByOwner(umi, { owner: ownerAddress }); +const collectionAssets = await das.getAssetsByCollection(umi, { + collection: collectionAddress, +}); +const groupMembers = await das.getAssetsByGroup(umi, { group: groupAddress }); +``` ## Transfer Asset @@ -579,7 +592,7 @@ await execute(umi, { Core uses a **single-account model** — asset and collection addresses are the public keys of the `generateSigner()` used at creation, not PDAs derived from other accounts. This means: - **No PDA derivation needed** to find an asset. The address returned from `create()` IS the asset address. -- To look up assets, use `fetchAssetsByOwner`, `fetchAssetsByCollection`, or DAS API queries. +- To look up assets, prefer `das.getAssetsByOwner` / `das.getAssetsByCollection` / `das.getAssetsByGroup` (see `./sdk-das.md`). GPA helpers (`fetchAssetsByOwner`, `fetchAssetsByCollection`) are fine for small local sets. - Core collections are also direct accounts (not PDAs like TM's Metadata/MasterEdition). This differs from Token Metadata, where you derive Metadata, MasterEdition, and TokenRecord PDAs from a mint address. diff --git a/skills/metaplex/references/sdk-das.md b/skills/metaplex/references/sdk-das.md new file mode 100644 index 0000000..7999129 --- /dev/null +++ b/skills/metaplex/references/sdk-das.md @@ -0,0 +1,213 @@ +# DAS API SDK Reference + +Query Solana digital assets via the Metaplex Digital Asset Standard (DAS) API. + +> **Prerequisites**: Umi setup — see `./sdk-umi.md`. +> +> **Important**: DAS requires a DAS-compatible RPC (Helius, Triton, QuickNode, Shyft, Hello Moon). The default public Solana RPC does **not** support DAS methods. + +## Which package? + +| Need | Package | Call style | +|------|---------|------------| +| Core assets/collections/groups as `AssetResult` / `CollectionResult` / `GroupResult` | `@metaplex-foundation/mpl-core-das` | `das.getAssetsByCollection(umi, …)` | +| Multi-standard assets, cNFT proofs, editions, token accounts, raw DAS shapes | `@metaplex-foundation/digital-asset-standard-api` | `umi.rpc.getAsset(…)` | + +Always register the base plugin. Add `mpl-core-das` when reading Core data as Core types. Requires DAS client `>=2.1.0` for groups / `getGrouping` / agent filters, and `mpl-core` `>=1.9.0` for `GroupV1`. + +```bash +npm install @metaplex-foundation/digital-asset-standard-api +# Core-typed helpers (recommended for Core apps): +npm install @metaplex-foundation/mpl-core-das @metaplex-foundation/mpl-core +``` + +```typescript +import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'; +import { dasApi } from '@metaplex-foundation/digital-asset-standard-api'; + +const umi = createUmi('https://mainnet.helius-rpc.com/?api-key=YOUR_KEY').use(dasApi()); + +// Helius-only param names for getNftEditions / getTokenAccounts: +// umi.use(dasApi({ heliusCompatibility: true })); +``` + +--- + +## Core helpers (`mpl-core-das`) + +Preferred for Core listing. Returns `AssetResult` / `CollectionResult` / `GroupResult` (Core types + DAS `content`, and optional `is_agent` / `agent_token` / `asset_signer`) and derives collection plugins by default. + +```typescript +import { publicKey } from '@metaplex-foundation/umi'; +import { das } from '@metaplex-foundation/mpl-core-das'; + +const byOwner = await das.getAssetsByOwner(umi, { owner: wallet }); +const byCollection = await das.getAssetsByCollection(umi, { + collection: collectionAddress, +}); +const byGroup = await das.getAssetsByGroup(umi, { group: groupAddress }); + +const asset = await das.getAsset(umi, assetAddress); +const collection = await das.getCollection(umi, collectionAddress); +const group = await das.getGroup(umi, groupAddress); + +// Agent discovery +const agents = await das.searchAssets(umi, { + isAgent: true, + skipDerivePlugins: true, +}); +// agents[i].is_agent, .agent_token, .asset_signer when indexed +``` + +| Helper | Description | +|--------|-------------| +| `das.getAsset` / `getCollection` / `getGroup` | Single account by pubkey | +| `das.getAssetsByOwner` / `ByAuthority` / `ByCollection` | List Core assets | +| `das.getAssetsByGroup` | Members of an mpl-core `GroupV1` — assets, collections, or nested groups (`groupKey: 'group'`) | +| `das.getGrouping` | Group summary (`group_name`, `group_size`) without listing members | +| `das.searchAssets` / `searchCollections` / `searchGroups` | Filtered search | +| `das.getCollectionsByUpdateAuthority` / `getGroupsByUpdateAuthority` | By update authority | +| `das.dasAssetsToCoreAssets` | Raw `MplCoreAsset` items → `AssetResult[]` | +| `das.dasAssetToCoreCollection` | Raw `MplCoreCollection` → `CollectionResult` | +| `das.dasAssetToCoreGroup` | Raw `MplCoreGroup` → `GroupResult` | + +Options: `skipDerivePlugins: true` to skip collection plugin inheritance. Only `displayOptions.showCollectionMetadata` is supported. + +> `GroupResult` membership vectors may be empty from DAS — use `fetchGroupV1` from `mpl-core` for authoritative on-chain membership. + +> Do **not** call `umi.rpc.getAssetsByCollection` — that method does not exist on base DAS. Use `das.getAssetsByCollection` (Core) or `umi.rpc.getAssetsByGroup({ groupKey: 'collection', … })` (base). + +--- + +## Base DAS methods (`umi.rpc`) + +| Method | Description | +|--------|-------------| +| `getAsset` | Single asset by ID | +| `getAssets` | Multiple assets by IDs | +| `getAssetProof` / `getAssetProofs` | Merkle proofs (cNFTs) | +| `getAssetsByOwner` | Assets owned by a wallet | +| `getAssetsByAuthority` | Assets by authority | +| `getAssetsByCreator` | Assets by creator (`onlyVerified`) | +| `getAssetsByGroup` | By grouping key/value (`collection` or `group`) | +| `getGrouping` | Grouping metadata (name + size) | +| `getAssetSignatures` | Transaction signatures for a compressed asset | +| `getNftEditions` | Print editions for a master edition mint | +| `getTokenAccounts` | Token accounts by owner and/or mint | +| `searchAssets` | Flexible multi-filter search | + +### Common examples + +```typescript +import { publicKey } from '@metaplex-foundation/umi'; + +const asset = await umi.rpc.getAsset(assetId); +// or with display options: +const asset2 = await umi.rpc.getAsset({ + assetId, + displayOptions: { showCollectionMetadata: true }, +}); + +const batch = await umi.rpc.getAssets([assetId1, assetId2]); + +const byOwner = await umi.rpc.getAssetsByOwner({ + owner: wallet, + limit: 100, + page: 1, +}); + +// Collections (TM or Core) — NOT getAssetsByCollection +const byCollection = await umi.rpc.getAssetsByGroup({ + groupKey: 'collection', + groupValue: collectionAddress, +}); + +// mpl-core GroupV1 members +const byCoreGroup = await umi.rpc.getAssetsByGroup({ + groupKey: 'group', + groupValue: groupAddress, +}); + +const grouping = await umi.rpc.getGrouping({ + groupKey: 'group', + groupValue: groupAddress, +}); + +const byCreator = await umi.rpc.getAssetsByCreator({ + creator: creatorAddress, + onlyVerified: true, +}); + +const proof = await umi.rpc.getAssetProof(cnftAssetId); +``` + +### searchAssets + +```typescript +const results = await umi.rpc.searchAssets({ + owner: wallet, + burnt: false, + compressed: true, + interface: 'MplCoreAsset', // or MplCoreCollection, MplCoreGroup, V1_NFT, … + limit: 50, + page: 1, +}); + +// Agent filters (MPL Core only) +const agents = await umi.rpc.searchAssets({ + isAgent: true, + agentToken: genesisMint, // optional + assetSigner: assetSignerPda, // optional + interface: 'MplCoreAsset', +}); +``` + +Useful filters: `owner`, `creator`, `authority`, `grouping: ['collection'|'group', value]`, `delegate`, `frozen`, `compressed`, `burnt`, `jsonUri`, `name`, `tokenType`, `royaltyModel`, `isAgent`, `agentToken`, `assetSigner`, `negate`, `conditionType`. + +Core response extras when present: `is_agent`, `agent_token`, `asset_signer`, plus `plugins` / `external_plugins` / `mpl_core_info`. + +### Editions & token accounts + +```typescript +const editions = await umi.rpc.getNftEditions({ + mintAddress: masterEditionMint, + page: 1, +}); + +const tokenAccounts = await umi.rpc.getTokenAccounts({ + ownerAddress: wallet, + // mintAddress: optionalMint, + options: { showZeroBalance: false }, +}); +``` + +With `dasApi({ heliusCompatibility: true })`, these methods send Helius param names (`mint` / `owner`) instead of Metaplex names (`mintAddress` / `ownerAddress`). + +### Pagination + +Use **either** `page` **or** `before`/`after` — not both (the client throws `DasApiError`). `cursor` is also supported where the RPC provides it. `sortBy: { sortBy, sortDirection }`. + +### Display options + +`showUnverifiedCollections`, `showCollectionMetadata`, `showFungible`, `showInscription` (plus `showZeroBalance` on `getTokenAccounts`). + +--- + +## GPA vs DAS (Core) + +`fetchAssetsByOwner` / `fetchAssetsByCollection` in `mpl-core` use GPA and can fail on burned account remnants. Prefer: + +```typescript +import { das } from '@metaplex-foundation/mpl-core-das'; + +const assets = await das.getAssetsByOwner(umi, { owner: wallet }); +``` + +--- + +## For more info + +- DAS API: https://metaplex.com/docs/dev-tools/das-api +- Getting started: https://metaplex.com/docs/dev-tools/das-api/getting-started +- Core groups: https://metaplex.com/docs/smart-contracts/core/groups +- OpenRPC playground: https://playground.open-rpc.org/?url=https://raw.githubusercontent.com/metaplex-foundation/digital-asset-standard-api/main/specification/metaplex-das-api.json diff --git a/skills/metaplex/references/sdk-umi.md b/skills/metaplex/references/sdk-umi.md index a8af725..01d978e 100644 --- a/skills/metaplex/references/sdk-umi.md +++ b/skills/metaplex/references/sdk-umi.md @@ -14,6 +14,7 @@ Umi is Metaplex's modular JavaScript framework for Solana program clients. | `@metaplex-foundation/umi-signer-wallet-adapters` | Wallet adapter integration | | `@metaplex-foundation/mpl-toolbox` | SPL token helpers (transfers, compute budget) | | `@metaplex-foundation/digital-asset-standard-api` | DAS API (asset queries) | +| `@metaplex-foundation/mpl-core-das` | Core-typed DAS helpers (`das.*`) | ## Installation @@ -22,7 +23,8 @@ npm install @metaplex-foundation/umi-bundle-defaults \ @metaplex-foundation/mpl-core \ @metaplex-foundation/mpl-token-metadata \ @metaplex-foundation/mpl-toolbox \ - @metaplex-foundation/digital-asset-standard-api + @metaplex-foundation/digital-asset-standard-api \ + @metaplex-foundation/mpl-core-das # Optional — add if needed: # @metaplex-foundation/umi-uploader-irys (for uploading files) # @metaplex-foundation/umi-signer-wallet-adapters (for browser wallet integration) @@ -76,6 +78,7 @@ umi.use(keypairIdentity(keypair)); | Core NFTs | `./sdk-core.md` | | Token Metadata | `./sdk-token-metadata.md` | | Bubblegum (compressed NFTs) | `./sdk-bubblegum.md` | +| DAS API (asset queries) | `./sdk-das.md` | | Genesis (token launches) | `./sdk-genesis.md` | | Token Metadata with Kit | `./sdk-token-metadata-kit.md` | @@ -146,31 +149,36 @@ const metadataUri = await umi.uploader.uploadJson({ ## DAS API (Asset Queries) -> **Important**: DAS API requires a DAS-compatible RPC provider (e.g., Helius, Triton, QuickNode). The default public Solana RPC does **not** support DAS methods. +> Full method reference: `./sdk-das.md`. +> +> **Important**: DAS requires a DAS-compatible RPC (e.g., Helius, Triton, QuickNode). Public Solana RPC does **not** support DAS. ```typescript import { dasApi } from '@metaplex-foundation/digital-asset-standard-api'; +import { das } from '@metaplex-foundation/mpl-core-das'; const umi = createUmi('https://mainnet.helius-rpc.com/?api-key=YOUR_KEY').use(dasApi()); -// Single asset by ID -const asset = await umi.rpc.getAsset(assetId); - -// By owner -const assets = await umi.rpc.getAssetsByOwner({ owner: walletAddress }); - -// By collection -const collectionAssets = await umi.rpc.getAssetsByCollection({ - collection: collectionAddress +// Core-typed (preferred for Core apps) +const coreAssets = await das.getAssetsByOwner(umi, { owner: walletAddress }); +const byCollection = await das.getAssetsByCollection(umi, { + collection: collectionAddress, }); -// Search +// Base DAS (multi-standard / raw shapes) +const asset = await umi.rpc.getAsset(assetId); +const byGroup = await umi.rpc.getAssetsByGroup({ + groupKey: 'collection', + groupValue: collectionAddress, +}); const results = await umi.rpc.searchAssets({ owner: walletAddress, burnt: false, }); ``` +> There is no `umi.rpc.getAssetsByCollection`. Use `das.getAssetsByCollection` (Core) or `umi.rpc.getAssetsByGroup({ groupKey: 'collection', … })` (base DAS). + --- ## Error Handling