From 485de177acfc158abb3285da5a711430aa45f3b8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Jul 2026 06:19:58 +0000 Subject: [PATCH 1/4] Auto-detect Core asset vs collection for plugin commands When adding or updating plugins, resolve the address as an asset first and fall back to a collection instead of failing with "Unable to fetch asset". The --collection flag remains as an explicit override. Co-authored-by: MarkSackerberg --- src/commands/core/plugins/add.ts | 42 +++++++++------- src/commands/core/plugins/update.ts | 52 +++++++++---------- src/lib/core/fetch/resolveCoreAccount.ts | 63 ++++++++++++++++++++++++ test/commands/core/core.plugins.test.ts | 48 ++++++++++++++++++ 4 files changed, 156 insertions(+), 49 deletions(-) create mode 100644 src/lib/core/fetch/resolveCoreAccount.ts diff --git a/src/commands/core/plugins/add.ts b/src/commands/core/plugins/add.ts index 4660049..f4cd287 100644 --- a/src/commands/core/plugins/add.ts +++ b/src/commands/core/plugins/add.ts @@ -1,11 +1,12 @@ import { Args, Flags } from '@oclif/core' -import { addCollectionPlugin, AddCollectionPluginArgsPlugin, addPlugin, AddPluginArgsPlugin, fetchAsset } from '@metaplex-foundation/mpl-core' +import { addCollectionPlugin, AddCollectionPluginArgsPlugin, addPlugin, AddPluginArgsPlugin } from '@metaplex-foundation/mpl-core' import { publicKey, transactionBuilder } from '@metaplex-foundation/umi' import { readFileSync } from 'fs' import ora from 'ora' import { BaseCommand } from '../../../BaseCommand.js' import { generateCoreExplorerUrl } from '../../../explorers.js' +import resolveCoreAccount from '../../../lib/core/fetch/resolveCoreAccount.js' import { Plugin } from '../../../lib/types/pluginData.js' import umiSendAllTransactionsAndConfirm from '../../../lib/umi/sendAllTransactionsAndConfirm.js' import { txSignatureToString } from '../../../lib/util.js' @@ -18,11 +19,15 @@ export default class CorePluginsAdd extends BaseCommand { static override examples = [ '<%= config.bin %> <%= command.id %> --wizard', '<%= config.bin %> <%= command.id %> ./plugin.json', + '<%= config.bin %> <%= command.id %> ./plugin.json --collection', ] static override flags = { wizard: Flags.boolean({ description: 'Wizard mode', default: false }), - collection: Flags.boolean({ description: 'Is this a collection\'s plugin', default: false }), + collection: Flags.boolean({ + description: 'Treat the address as a collection (auto-detected if omitted)', + default: false, + }), } static override args = { @@ -30,28 +35,27 @@ export default class CorePluginsAdd extends BaseCommand { json: Args.file({ description: 'path to a plugin data JSON file', required: false }), } - - public async run(): Promise { const { args, flags } = await this.parse(CorePluginsAdd) - // Auto-detect collection ID if this is an asset operation + const resolveSpinner = ora('Resolving asset or collection...').start() + let isCollection: boolean let collectionId: string | undefined - if (!flags.collection) { - try { - const asset = await fetchAsset(this.context.umi, publicKey(args.id)) - - if (asset.updateAuthority.type === 'Collection') { - collectionId = asset.updateAuthority.address - } - } catch (error) { - throw new Error('Unable to fetch asset') - } + try { + const resolved = await resolveCoreAccount(this.context.umi, args.id, { + forceCollection: flags.collection, + }) + isCollection = resolved.isCollection + collectionId = resolved.collectionId + resolveSpinner.succeed(`Resolved as ${isCollection ? 'collection' : 'asset'}`) + } catch (error) { + resolveSpinner.fail(error instanceof Error ? error.message : 'Failed to resolve address') + throw error } if (flags.wizard) { const selectedPlugins = await pluginSelector({ - filter: flags.collection ? PluginFilterType.Collection : PluginFilterType.Asset, + filter: isCollection ? PluginFilterType.Collection : PluginFilterType.Asset, type: 'list', managedBy: PluginFilterType.Authority }) as Plugin[] @@ -64,7 +68,7 @@ export default class CorePluginsAdd extends BaseCommand { const pluginsArray = Object.values(wizardPluginData) as (AddPluginArgsPlugin | AddCollectionPluginArgsPlugin)[] return await this.addPluginsBatch(args.id, pluginsArray, { - isCollection: flags.collection, + isCollection, collectionId }) } @@ -81,7 +85,7 @@ export default class CorePluginsAdd extends BaseCommand { } return await this.addPluginsBatch(args.id, jsonData as (AddPluginArgsPlugin | AddCollectionPluginArgsPlugin)[], { - isCollection: flags.collection, + isCollection, collectionId }) } @@ -91,7 +95,7 @@ export default class CorePluginsAdd extends BaseCommand { private async addPluginsBatch(asset: string, pluginsData: (AddPluginArgsPlugin | AddCollectionPluginArgsPlugin)[], options: { isCollection: boolean, collectionId?: string }): Promise { - const { umi, explorer } = this.context + const { umi } = this.context const { isCollection, collectionId } = options let transaction = transactionBuilder() diff --git a/src/commands/core/plugins/update.ts b/src/commands/core/plugins/update.ts index dd86830..6c7736c 100644 --- a/src/commands/core/plugins/update.ts +++ b/src/commands/core/plugins/update.ts @@ -1,11 +1,12 @@ import { Args, Flags } from '@oclif/core' -import { updateCollectionPlugin, updatePlugin, UpdatePluginArgsPlugin, UpdateCollectionPluginArgsPlugin, fetchAsset, fetchCollection } from '@metaplex-foundation/mpl-core' +import { updateCollectionPlugin, updatePlugin, UpdatePluginArgsPlugin, UpdateCollectionPluginArgsPlugin } from '@metaplex-foundation/mpl-core' import { publicKey, transactionBuilder } from '@metaplex-foundation/umi' import { readFileSync } from 'fs' import ora from 'ora' import { BaseCommand } from '../../../BaseCommand.js' import { generateCoreExplorerUrl } from '../../../explorers.js' +import resolveCoreAccount from '../../../lib/core/fetch/resolveCoreAccount.js' import { Plugin } from '../../../lib/types/pluginData.js' import umiSendAllTransactionsAndConfirm from '../../../lib/umi/sendAllTransactionsAndConfirm.js' import { txSignatureToString } from '../../../lib/util.js' @@ -18,11 +19,15 @@ export default class CorePluginsUpdate extends BaseCommand <%= command.id %> --wizard', '<%= config.bin %> <%= command.id %> ./plugin.json', + '<%= config.bin %> <%= command.id %> ./plugin.json --collection', ] static override flags = { wizard: Flags.boolean({ description: 'Wizard mode', default: false }), - collection: Flags.boolean({ description: 'Is this a collection\'s plugin', default: false }), + collection: Flags.boolean({ + description: 'Treat the address as a collection (auto-detected if omitted)', + default: false, + }), } static override args = { @@ -33,37 +38,24 @@ export default class CorePluginsUpdate extends BaseCommand { const { args, flags } = await this.parse(CorePluginsUpdate) - // Fetch current asset or collection to validate it exists - const fetchSpinner = ora('Fetching current state...').start() + const resolveSpinner = ora('Resolving asset or collection...').start() + let isCollection: boolean + let collectionId: string | undefined try { - if (flags.collection) { - await fetchCollection(this.context.umi, publicKey(args.id)) - } else { - await fetchAsset(this.context.umi, publicKey(args.id)) - } - fetchSpinner.succeed('Successfully fetched current state') + const resolved = await resolveCoreAccount(this.context.umi, args.id, { + forceCollection: flags.collection, + }) + isCollection = resolved.isCollection + collectionId = resolved.collectionId + resolveSpinner.succeed(`Resolved as ${isCollection ? 'collection' : 'asset'}`) } catch (error) { - fetchSpinner.fail(`Failed to fetch ${flags.collection ? 'collection' : 'asset'}: ${error instanceof Error ? error.message : 'Unknown error'}`) + resolveSpinner.fail(error instanceof Error ? error.message : 'Failed to resolve address') throw error } - // Auto-detect collection ID if this is an asset operation - let collectionId: string | undefined - if (!flags.collection) { - try { - const asset = await fetchAsset(this.context.umi, publicKey(args.id)) - - if (asset.updateAuthority.type === 'Collection') { - collectionId = asset.updateAuthority.address - } - } catch (error) { - throw new Error('Unable to fetch asset') - } - } - if (flags.wizard) { const selectedPlugins = await pluginSelector({ - filter: flags.collection ? PluginFilterType.Collection : PluginFilterType.Asset, + filter: isCollection ? PluginFilterType.Collection : PluginFilterType.Asset, type: 'list', managedBy: PluginFilterType.Authority }) as Plugin[] @@ -76,7 +68,7 @@ export default class CorePluginsUpdate extends BaseCommand { - const { umi, explorer } = this.context + const { umi } = this.context const { isCollection, collectionId } = options // Build a single transaction with all plugin instructions @@ -160,4 +152,4 @@ Core Explorer: ${generateCoreExplorerUrl(this.context.chain, assetOrCollection)} throw error } } -} \ No newline at end of file +} diff --git a/src/lib/core/fetch/resolveCoreAccount.ts b/src/lib/core/fetch/resolveCoreAccount.ts new file mode 100644 index 0000000..052ce90 --- /dev/null +++ b/src/lib/core/fetch/resolveCoreAccount.ts @@ -0,0 +1,63 @@ +import { + collectionAddress, + safeFetchAssetV1, + safeFetchCollectionV1, +} from '@metaplex-foundation/mpl-core' +import { publicKey, Umi } from '@metaplex-foundation/umi' + +export type ResolvedCoreAccount = { + id: string + isCollection: boolean + /** Parent collection address when the account is an asset belonging to a collection */ + collectionId?: string +} + +export type ResolveCoreAccountOptions = { + /** + * When true, only accept a Core Collection at this address. + * Mirrors an explicit `--collection` flag override. + */ + forceCollection?: boolean +} + +/** + * Resolve whether an address is a Core Asset or Collection. + * + * When `forceCollection` is set, only Collection is accepted. + * Otherwise tries Asset first, then falls back to Collection — matching the + * auto-detect pattern used by `genesis bucket fetch` when `--type` is omitted. + */ +export async function resolveCoreAccount( + umi: Umi, + id: string, + options: ResolveCoreAccountOptions = {}, +): Promise { + const address = publicKey(id) + + if (options.forceCollection) { + const collection = await safeFetchCollectionV1(umi, address).catch(() => null) + if (!collection) { + throw new Error(`Unable to fetch collection at address: ${id}`) + } + return { id, isCollection: true } + } + + const asset = await safeFetchAssetV1(umi, address).catch(() => null) + if (asset) { + const parentCollection = collectionAddress(asset) + return { + id, + isCollection: false, + collectionId: parentCollection ? parentCollection.toString() : undefined, + } + } + + const collection = await safeFetchCollectionV1(umi, address).catch(() => null) + if (collection) { + return { id, isCollection: true } + } + + throw new Error(`Address ${id} is neither a Core Asset nor a Core Collection`) +} + +export default resolveCoreAccount diff --git a/test/commands/core/core.plugins.test.ts b/test/commands/core/core.plugins.test.ts index a45c641..dc04dd9 100644 --- a/test/commands/core/core.plugins.test.ts +++ b/test/commands/core/core.plugins.test.ts @@ -40,6 +40,26 @@ describe('core plugin commands', () => { expect(cleanAddStderr).to.contain('Successfully added') }) + it('adds a plugin to a collection without --collection by auto-detecting', async function() { + this.timeout(30000) + const { collectionId } = await createCoreCollection() + + const addInput = [ + 'core', + 'plugins', + 'add', + collectionId, + 'test-files/plugins.json', + ] + + const { stderr: addStderr, code: addCode } = await runCli(addInput) + const cleanAddStderr = stripAnsi(addStderr) + + expect(addCode).to.equal(0) + expect(cleanAddStderr).to.contain('Resolved as collection') + expect(cleanAddStderr).to.contain('Successfully added') + }) + it('updates a plugin on a collection using JSON file', async function() { this.timeout(45000) // 45 seconds timeout // First create a collection and add a plugin @@ -76,6 +96,34 @@ describe('core plugin commands', () => { expect(cleanUpdateStderr).to.contain('Successfully updated') }) + it('updates a plugin on a collection without --collection by auto-detecting', async function() { + this.timeout(45000) + const { collectionId } = await createCoreCollection() + + const { code: addCode } = await runCli([ + 'core', + 'plugins', + 'add', + collectionId, + 'test-files/plugins.json', + ]) + expect(addCode).to.equal(0) + + const { stderr: updateStderr, code: updateCode } = await runCli([ + 'core', + 'plugins', + 'update', + collectionId, + 'test-files/plugins-updated.json', + ]) + + const cleanUpdateStderr = stripAnsi(updateStderr) + + expect(updateCode).to.equal(0) + expect(cleanUpdateStderr).to.contain('Resolved as collection') + expect(cleanUpdateStderr).to.contain('Successfully updated') + }) + it('adds a plugin to an asset using JSON file', async function() { this.timeout(45000) // 45 seconds timeout // Create a collection and asset From f72cb606a2982513a3ff4e40ffddebbb5815eb1b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Jul 2026 07:22:01 +0000 Subject: [PATCH 2/4] Raise default compute unit limit to avoid Genesis CU flakes Genesis account creation can exceed Solana's 200k default compute budget under Node 24 CI. Prepend setComputeUnitLimit (400k) in the shared send path so heavier Metaplex transactions stop failing flakily. Co-authored-by: MarkSackerberg --- src/lib/umi/sendOptions.ts | 2 ++ src/lib/umi/sendTransaction.ts | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/lib/umi/sendOptions.ts b/src/lib/umi/sendOptions.ts index 67adee1..0465549 100644 --- a/src/lib/umi/sendOptions.ts +++ b/src/lib/umi/sendOptions.ts @@ -7,6 +7,8 @@ export enum ConfirmationStrategy { export interface UmiSendOptions { priorityFee?: number | undefined + /** Override the default compute unit limit (400_000). */ + computeUnitLimit?: number | undefined commitment?: Commitment | undefined skipPreflight?: boolean | undefined confirmationStrategy?: ConfirmationStrategy diff --git a/src/lib/umi/sendTransaction.ts b/src/lib/umi/sendTransaction.ts index 6ebb047..2957a62 100644 --- a/src/lib/umi/sendTransaction.ts +++ b/src/lib/umi/sendTransaction.ts @@ -1,5 +1,5 @@ -import { setComputeUnitPrice } from '@metaplex-foundation/mpl-toolbox' -import { BlockhashWithExpiryBlockHeight, Signer, TransactionBuilder, TransactionSignature, Umi } from '@metaplex-foundation/umi' +import { setComputeUnitLimit, setComputeUnitPrice } from '@metaplex-foundation/mpl-toolbox' +import { BlockhashWithExpiryBlockHeight, TransactionBuilder, TransactionSignature, Umi } from '@metaplex-foundation/umi' import { getAssetSigner } from './assetSignerPlugin.js' import { UmiSendOptions } from './sendOptions.js' @@ -9,6 +9,9 @@ export interface UmiTransactionResponse { err: string | null } +/** Default above Solana's 200k so heavier Metaplex txs (e.g. Genesis create) don't flake. */ +const DEFAULT_COMPUTE_UNIT_LIMIT = 400_000 + const umiSendTransaction = async ( umi: Umi, tx: TransactionBuilder, @@ -30,10 +33,17 @@ const umiSendTransaction = async ( let transaction = tx.setBlockhash(blockhash) + // Compute budget instructions must come first in the transaction. + transaction = transaction.prepend( + setComputeUnitLimit(umi, { + units: sendOptions?.computeUnitLimit ?? DEFAULT_COMPUTE_UNIT_LIMIT, + }), + ) + if (sendOptions?.priorityFee) { - transaction = transaction.add( + transaction = transaction.prepend( setComputeUnitPrice(umi, { - microLamports: 100000, + microLamports: sendOptions.priorityFee, }), ) } From 87288dd76f76d37c9a00bac6b3bef4f7dfebe335 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Jul 2026 07:34:43 +0000 Subject: [PATCH 3/4] Limit Genesis create CU budget without growing other txs A global compute-unit limit pushed near-full transactions (e.g. add presale bucket) over Solana's size cap. Scope the 400k CU limit to Genesis initialize only, where create was flaking on the 200k default. Co-authored-by: MarkSackerberg --- src/commands/genesis/create.ts | 8 +++++++- src/lib/genesis/operations.ts | 8 +++++++- src/lib/umi/sendOptions.ts | 2 -- src/lib/umi/sendTransaction.ts | 18 ++++-------------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/commands/genesis/create.ts b/src/commands/genesis/create.ts index 93534b3..f482e6a 100644 --- a/src/commands/genesis/create.ts +++ b/src/commands/genesis/create.ts @@ -3,6 +3,7 @@ import { findGenesisAccountV2Pda, WRAPPED_SOL_MINT, } from '@metaplex-foundation/genesis' +import { setComputeUnitLimit } from '@metaplex-foundation/mpl-toolbox' import { generateSigner, publicKey } from '@metaplex-foundation/umi' import { Flags } from '@oclif/core' import { confirm } from '@inquirer/prompts' @@ -14,6 +15,9 @@ import { txSignatureToString } from '../../lib/util.js' import umiSendAndConfirmTransaction from '../../lib/umi/sendAndConfirm.js' import { runApiWizard, runManualWizard, WizardContext, WizardLogger } from '../../lib/genesis/wizard.js' +/** Genesis initialize + token metadata can exceed Solana's 200k default CU budget. */ +const GENESIS_CREATE_COMPUTE_UNIT_LIMIT = 400_000 + // Funding modes for Genesis const FUNDING_MODE = { NewMint: 0, // Create a new mint (most common) @@ -156,7 +160,9 @@ Use --wizard for an interactive guided setup.` uri: flags.uri, decimals: flags.decimals, genesisIndex: flags.genesisIndex, - }) + }).prepend( + setComputeUnitLimit(this.context.umi, { units: GENESIS_CREATE_COMPUTE_UNIT_LIMIT }), + ) const result = await umiSendAndConfirmTransaction(this.context.umi, transaction) diff --git a/src/lib/genesis/operations.ts b/src/lib/genesis/operations.ts index c5aa218..33696d9 100644 --- a/src/lib/genesis/operations.ts +++ b/src/lib/genesis/operations.ts @@ -12,6 +12,7 @@ import { findUnlockedBucketV2Pda, WRAPPED_SOL_MINT, } from '@metaplex-foundation/genesis' +import { setComputeUnitLimit } from '@metaplex-foundation/mpl-toolbox' import { Umi, Signer, @@ -26,6 +27,9 @@ import { import umiSendAndConfirmTransaction from '../umi/sendAndConfirm.js' import { txSignatureToString } from '../util.js' +/** Genesis initialize + token metadata can exceed Solana's 200k default CU budget. */ +const GENESIS_CREATE_COMPUTE_UNIT_LIMIT = 400_000 + /* ------------------------------------------------------------------ */ /* Types */ /* ------------------------------------------------------------------ */ @@ -143,7 +147,9 @@ export async function createGenesisAccount( uri: params.uri, decimals: params.decimals, genesisIndex, - }) + }).prepend( + setComputeUnitLimit(umi, { units: GENESIS_CREATE_COMPUTE_UNIT_LIMIT }), + ) const result = await umiSendAndConfirmTransaction(umi, transaction) diff --git a/src/lib/umi/sendOptions.ts b/src/lib/umi/sendOptions.ts index 0465549..67adee1 100644 --- a/src/lib/umi/sendOptions.ts +++ b/src/lib/umi/sendOptions.ts @@ -7,8 +7,6 @@ export enum ConfirmationStrategy { export interface UmiSendOptions { priorityFee?: number | undefined - /** Override the default compute unit limit (400_000). */ - computeUnitLimit?: number | undefined commitment?: Commitment | undefined skipPreflight?: boolean | undefined confirmationStrategy?: ConfirmationStrategy diff --git a/src/lib/umi/sendTransaction.ts b/src/lib/umi/sendTransaction.ts index 2957a62..6ebb047 100644 --- a/src/lib/umi/sendTransaction.ts +++ b/src/lib/umi/sendTransaction.ts @@ -1,5 +1,5 @@ -import { setComputeUnitLimit, setComputeUnitPrice } from '@metaplex-foundation/mpl-toolbox' -import { BlockhashWithExpiryBlockHeight, TransactionBuilder, TransactionSignature, Umi } from '@metaplex-foundation/umi' +import { setComputeUnitPrice } from '@metaplex-foundation/mpl-toolbox' +import { BlockhashWithExpiryBlockHeight, Signer, TransactionBuilder, TransactionSignature, Umi } from '@metaplex-foundation/umi' import { getAssetSigner } from './assetSignerPlugin.js' import { UmiSendOptions } from './sendOptions.js' @@ -9,9 +9,6 @@ export interface UmiTransactionResponse { err: string | null } -/** Default above Solana's 200k so heavier Metaplex txs (e.g. Genesis create) don't flake. */ -const DEFAULT_COMPUTE_UNIT_LIMIT = 400_000 - const umiSendTransaction = async ( umi: Umi, tx: TransactionBuilder, @@ -33,17 +30,10 @@ const umiSendTransaction = async ( let transaction = tx.setBlockhash(blockhash) - // Compute budget instructions must come first in the transaction. - transaction = transaction.prepend( - setComputeUnitLimit(umi, { - units: sendOptions?.computeUnitLimit ?? DEFAULT_COMPUTE_UNIT_LIMIT, - }), - ) - if (sendOptions?.priorityFee) { - transaction = transaction.prepend( + transaction = transaction.add( setComputeUnitPrice(umi, { - microLamports: sendOptions.priorityFee, + microLamports: 100000, }), ) } From 16c28371582382fe8b6fb8c2f8290777fa8643e1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Jul 2026 14:10:40 +0000 Subject: [PATCH 4/4] Harden Core account resolution and share Genesis CU constant Resolve asset vs collection via a single getAccount + Key check so RPC failures are not swallowed as missing accounts. Export the Genesis create compute-unit limit from operations and reuse it in the command. Co-authored-by: MarkSackerberg --- src/commands/genesis/create.ts | 4 +-- src/lib/core/fetch/resolveCoreAccount.ts | 33 +++++++++++++++++------- src/lib/genesis/operations.ts | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/commands/genesis/create.ts b/src/commands/genesis/create.ts index f482e6a..5dbc831 100644 --- a/src/commands/genesis/create.ts +++ b/src/commands/genesis/create.ts @@ -11,13 +11,11 @@ import ora from 'ora' import { TransactionCommand } from '../../TransactionCommand.js' import { generateExplorerUrl } from '../../explorers.js' +import { GENESIS_CREATE_COMPUTE_UNIT_LIMIT } from '../../lib/genesis/operations.js' import { txSignatureToString } from '../../lib/util.js' import umiSendAndConfirmTransaction from '../../lib/umi/sendAndConfirm.js' import { runApiWizard, runManualWizard, WizardContext, WizardLogger } from '../../lib/genesis/wizard.js' -/** Genesis initialize + token metadata can exceed Solana's 200k default CU budget. */ -const GENESIS_CREATE_COMPUTE_UNIT_LIMIT = 400_000 - // Funding modes for Genesis const FUNDING_MODE = { NewMint: 0, // Create a new mint (most common) diff --git a/src/lib/core/fetch/resolveCoreAccount.ts b/src/lib/core/fetch/resolveCoreAccount.ts index 052ce90..eae7286 100644 --- a/src/lib/core/fetch/resolveCoreAccount.ts +++ b/src/lib/core/fetch/resolveCoreAccount.ts @@ -1,7 +1,8 @@ import { collectionAddress, - safeFetchAssetV1, - safeFetchCollectionV1, + deserializeAssetV1, + deserializeCollectionV1, + Key, } from '@metaplex-foundation/mpl-core' import { publicKey, Umi } from '@metaplex-foundation/umi' @@ -23,9 +24,11 @@ export type ResolveCoreAccountOptions = { /** * Resolve whether an address is a Core Asset or Collection. * + * Fetches the account once so RPC/transport failures propagate. Distinguishes + * Asset vs Collection via the on-chain account key instead of catching + * deserialize errors (which would also hide network failures). + * * When `forceCollection` is set, only Collection is accepted. - * Otherwise tries Asset first, then falls back to Collection — matching the - * auto-detect pattern used by `genesis bucket fetch` when `--type` is omitted. */ export async function resolveCoreAccount( umi: Umi, @@ -33,17 +36,27 @@ export async function resolveCoreAccount( options: ResolveCoreAccountOptions = {}, ): Promise { const address = publicKey(id) + const account = await umi.rpc.getAccount(address) + + if (!account.exists) { + if (options.forceCollection) { + throw new Error(`Unable to fetch collection at address: ${id}`) + } + throw new Error(`Address ${id} is neither a Core Asset nor a Core Collection`) + } + + const accountKey = account.data[0] if (options.forceCollection) { - const collection = await safeFetchCollectionV1(umi, address).catch(() => null) - if (!collection) { + if (accountKey !== Key.CollectionV1) { throw new Error(`Unable to fetch collection at address: ${id}`) } + deserializeCollectionV1(account) return { id, isCollection: true } } - const asset = await safeFetchAssetV1(umi, address).catch(() => null) - if (asset) { + if (accountKey === Key.AssetV1) { + const asset = deserializeAssetV1(account) const parentCollection = collectionAddress(asset) return { id, @@ -52,8 +65,8 @@ export async function resolveCoreAccount( } } - const collection = await safeFetchCollectionV1(umi, address).catch(() => null) - if (collection) { + if (accountKey === Key.CollectionV1) { + deserializeCollectionV1(account) return { id, isCollection: true } } diff --git a/src/lib/genesis/operations.ts b/src/lib/genesis/operations.ts index 33696d9..abf1983 100644 --- a/src/lib/genesis/operations.ts +++ b/src/lib/genesis/operations.ts @@ -28,7 +28,7 @@ import umiSendAndConfirmTransaction from '../umi/sendAndConfirm.js' import { txSignatureToString } from '../util.js' /** Genesis initialize + token metadata can exceed Solana's 200k default CU budget. */ -const GENESIS_CREATE_COMPUTE_UNIT_LIMIT = 400_000 +export const GENESIS_CREATE_COMPUTE_UNIT_LIMIT = 400_000 /* ------------------------------------------------------------------ */ /* Types */