diff --git a/clients/js/src/generated/types/index.ts b/clients/js/src/generated/types/index.ts index 1482421..48a3af8 100644 --- a/clients/js/src/generated/types/index.ts +++ b/clients/js/src/generated/types/index.ts @@ -6,4 +6,4 @@ * @see https://github.com/metaplex-foundation/kinobi */ -export * from './path'; +export * from './internalPath'; diff --git a/clients/js/src/generated/types/internalPath.ts b/clients/js/src/generated/types/internalPath.ts new file mode 100644 index 0000000..7a4d10a --- /dev/null +++ b/clients/js/src/generated/types/internalPath.ts @@ -0,0 +1,24 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; + +export enum InternalPath { + NoRerollMetadata, +} + +export type InternalPathArgs = InternalPath; + +export function getInternalPathSerializer(): Serializer< + InternalPathArgs, + InternalPath +> { + return scalarEnum(InternalPath, { + description: 'InternalPath', + }) as Serializer; +} diff --git a/clients/js/src/generated/types/path.ts b/clients/js/src/generated/types/path.ts deleted file mode 100644 index 26607ec..0000000 --- a/clients/js/src/generated/types/path.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * This code was AUTOGENERATED using the kinobi library. - * Please DO NOT EDIT THIS FILE, instead use visitors - * to add features, then rerun kinobi to update it. - * - * @see https://github.com/metaplex-foundation/kinobi - */ - -import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; - -export enum Path { - RerollMetadata, -} - -export type PathArgs = Path; - -export function getPathSerializer(): Serializer { - return scalarEnum(Path, { description: 'Path' }) as Serializer< - PathArgs, - Path - >; -} diff --git a/clients/js/src/index.ts b/clients/js/src/index.ts index 7dda878..854930e 100644 --- a/clients/js/src/index.ts +++ b/clients/js/src/index.ts @@ -1,2 +1,3 @@ export * from './generated'; export * from './plugin'; +export * from './path'; diff --git a/clients/js/src/path.ts b/clients/js/src/path.ts new file mode 100644 index 0000000..4fa1257 --- /dev/null +++ b/clients/js/src/path.ts @@ -0,0 +1,27 @@ +import { InternalPath } from './generated'; + +enum CustomPath { + RerollMetadata = 16, +} + +export const Path = { + ...InternalPath, + ...CustomPath, +} as const; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type Path = InternalPath | CustomPath; + +export function buildPath(features: Path[]) { + let path = 0; + + // eslint-disable-next-line no-restricted-syntax + for (const feature of features) { + if (feature !== Path.RerollMetadata) { + // eslint-disable-next-line no-bitwise + path |= 1 << feature; + } + } + + return path; +} diff --git a/clients/js/test/v1/capture.test.ts b/clients/js/test/v1/capture.test.ts index c5ff5a2..23ac95b 100644 --- a/clients/js/test/v1/capture.test.ts +++ b/clients/js/test/v1/capture.test.ts @@ -16,6 +16,7 @@ import { transfer, } from '@metaplex-foundation/mpl-core'; import { + buildPath, captureV1, EscrowV1, fetchEscrowV1, @@ -76,7 +77,7 @@ test('it can swap tokens for an asset with reroll', async (t) => { min: 0, amount: 5, feeAmount: 1, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), solFeeAmount: 1000000n, }).sendAndConfirm(umi); @@ -94,7 +95,7 @@ test('it can swap tokens for an asset with reroll', async (t) => { amount: 5n, feeAmount: 1n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), bump: escrow[1], solFeeAmount: 1_000_000n, }); @@ -154,6 +155,134 @@ test('it can swap tokens for an asset with reroll', async (t) => { t.regex(assetAfter.uri, uriRegex); }); +test('it can swap tokens for an asset without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: umi.identity.publicKey, + amount: 1000, + }).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + // Transfer the assets to the escrow. + // eslint-disable-next-line no-restricted-syntax + for (const asset of assets) { + // eslint-disable-next-line no-await-in-loop + await transfer(umi, { + asset, + collection, + newOwner: escrow, + }).sendAndConfirm(umi); + } + + await initEscrowV1(umi, { + escrow, + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmount: 1, + path: buildPath([Path.NoRerollMetadata]), + solFeeAmount: 1000000n, + }).sendAndConfirm(umi); + + const escrowData = await fetchEscrowV1(umi, escrow); + + t.like(escrowData, { + publicKey: publicKey(escrow), + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmount: 1n, + count: 1n, + path: buildPath([Path.NoRerollMetadata]), + bump: escrow[1], + solFeeAmount: 1_000_000n, + }); + + const userTokenBefore = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenBefore.token.amount, 1000n); + try { + await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.fail('Escrow token account should not exist'); + } catch (e) { + t.is(e.name, 'AccountNotFoundError'); + } + + const assetBefore = await fetchAsset(umi, assets[0].publicKey); + t.is(assetBefore.owner, publicKey(escrow)); + + await captureV1(umi, { + owner: umi.identity, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: escrowData.feeLocation, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 5n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 994n); + const feeTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + escrowData.feeLocation + ); + t.deepEqual(feeTokenAfter.token.amount, 1n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, umi.identity.publicKey); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); + test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) => { // Given a Umi instance using the project's plugin. const umi = await createUmi(); @@ -205,7 +334,7 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) min: 0, amount: 5, feeAmount: 1, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), solFeeAmount: 1000000n, }).sendAndConfirm(umi); @@ -232,7 +361,7 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) amount: 5n, feeAmount: 1n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), bump: escrow[1], solFeeAmount: 1_000_000n, }); @@ -272,3 +401,121 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) const uriRegex = new RegExp(`${escrowData.uri}\\d+\\.json`); t.regex(assetAfter.uri, uriRegex); }); + +test('it can swap tokens for an asset as UpdateDelegate without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: umi.identity.publicKey, + amount: 1000, + }).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + // Transfer the assets to the escrow. + // eslint-disable-next-line no-restricted-syntax + for (const asset of assets) { + // eslint-disable-next-line no-await-in-loop + await transfer(umi, { + asset, + collection, + newOwner: escrow, + }).sendAndConfirm(umi); + } + + await initEscrowV1(umi, { + escrow, + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmount: 1, + path: buildPath([Path.NoRerollMetadata]), + solFeeAmount: 1000000n, + }).sendAndConfirm(umi); + + await addCollectionPlugin(umi, { + collection: collection.publicKey, + plugin: { + type: 'UpdateDelegate', + additionalDelegates: [], + authority: { type: 'Address', address: publicKey(escrow) }, + }, + }).sendAndConfirm(umi); + + const escrowData = await fetchEscrowV1(umi, escrow); + + t.like(escrowData, { + publicKey: publicKey(escrow), + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmount: 1n, + count: 1n, + path: buildPath([Path.NoRerollMetadata]), + bump: escrow[1], + solFeeAmount: 1_000_000n, + }); + + await captureV1(umi, { + owner: umi.identity, + authority: escrow, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: escrowData.feeLocation, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 5n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 994n); + const feeTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + escrowData.feeLocation + ); + t.deepEqual(feeTokenAfter.token.amount, 1n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, umi.identity.publicKey); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); diff --git a/clients/js/test/v1/release.test.ts b/clients/js/test/v1/release.test.ts index cf7c77a..e9d3bd6 100644 --- a/clients/js/test/v1/release.test.ts +++ b/clients/js/test/v1/release.test.ts @@ -12,6 +12,7 @@ import { } from '@metaplex-foundation/umi/serializers'; import { addCollectionPlugin, fetchAsset } from '@metaplex-foundation/mpl-core'; import { + buildPath, EscrowV1, fetchEscrowV1, initEscrowV1, @@ -61,7 +62,7 @@ test('it can swap an asset for tokens with reroll', async (t) => { min: 0, amount: 5, feeAmount: 1, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), solFeeAmount: 1000000n, }).sendAndConfirm(umi); @@ -79,7 +80,7 @@ test('it can swap an asset for tokens with reroll', async (t) => { amount: 5n, feeAmount: 1n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), bump: escrow[1], solFeeAmount: 1_000_000n, }); @@ -131,6 +132,116 @@ test('it can swap an asset for tokens with reroll', async (t) => { t.is(assetAfter.uri, `${escrowData.uri}captured.json`); }); +test('it can swap an asset for tokens without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: escrow, + amount: 1000, + }).sendAndConfirm(umi); + + await initEscrowV1(umi, { + escrow, + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmount: 1, + path: buildPath([Path.NoRerollMetadata]), + solFeeAmount: 1000000n, + }).sendAndConfirm(umi); + + const escrowData = await fetchEscrowV1(umi, escrow); + + t.like(escrowData, { + publicKey: publicKey(escrow), + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmount: 1n, + count: 1n, + path: buildPath([Path.NoRerollMetadata]), + bump: escrow[1], + solFeeAmount: 1_000_000n, + }); + + const escrowTokenBefore = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenBefore.token.amount, 1000n); + try { + await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.fail('User token account should not exist'); + } catch (e) { + t.is(e.name, 'AccountNotFoundError'); + } + + t.is(assets[0].owner, umi.identity.publicKey); + + await releaseV1(umi, { + owner: umi.identity, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: escrowData.feeLocation, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 995n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 5n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, publicKey(escrow)); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); + test('it can swap an asset for tokens as UpdateDelegate with reroll', async (t) => { // Given a Umi instance using the project's plugin. const umi = await createUmi(); @@ -171,7 +282,7 @@ test('it can swap an asset for tokens as UpdateDelegate with reroll', async (t) min: 0, amount: 5, feeAmount: 1, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), solFeeAmount: 1000000n, }).sendAndConfirm(umi); @@ -198,7 +309,7 @@ test('it can swap an asset for tokens as UpdateDelegate with reroll', async (t) amount: 5n, feeAmount: 1n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), bump: escrow[1], solFeeAmount: 1_000_000n, }); @@ -250,3 +361,123 @@ test('it can swap an asset for tokens as UpdateDelegate with reroll', async (t) // Confirm that an asset in the escrow has the correct URI t.is(assetAfter.uri, `${escrowData.uri}captured.json`); }); + +test('it can swap an asset for tokens as UpdateDelegate without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: escrow, + amount: 1000, + }).sendAndConfirm(umi); + + await initEscrowV1(umi, { + escrow, + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmount: 1, + path: buildPath([Path.NoRerollMetadata]), + solFeeAmount: 1000000n, + }).sendAndConfirm(umi); + + await addCollectionPlugin(umi, { + collection: collection.publicKey, + plugin: { + type: 'UpdateDelegate', + additionalDelegates: [], + authority: { type: 'Address', address: publicKey(escrow) }, + }, + }).sendAndConfirm(umi); + + const escrowData = await fetchEscrowV1(umi, escrow); + + t.like(escrowData, { + publicKey: publicKey(escrow), + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmount: 1n, + count: 1n, + path: buildPath([Path.NoRerollMetadata]), + bump: escrow[1], + solFeeAmount: 1_000_000n, + }); + + const escrowTokenBefore = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenBefore.token.amount, 1000n); + try { + await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.fail('User token account should not exist'); + } catch (e) { + t.is(e.name, 'AccountNotFoundError'); + } + + t.is(assets[0].owner, umi.identity.publicKey); + + await releaseV1(umi, { + owner: umi.identity, + authority: escrow, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: escrowData.feeLocation, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 995n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 5n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, publicKey(escrow)); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); diff --git a/clients/js/test/v2/captureV2.test.ts b/clients/js/test/v2/captureV2.test.ts index a89b67d..03095a0 100644 --- a/clients/js/test/v2/captureV2.test.ts +++ b/clients/js/test/v2/captureV2.test.ts @@ -16,6 +16,7 @@ import { transfer, } from '@metaplex-foundation/mpl-core'; import { + buildPath, captureV2, EscrowV2, fetchEscrowV2, @@ -92,7 +93,7 @@ test('it can swap tokens for an asset with reroll', async (t) => { feeAmountRelease: 1, solFeeAmountCapture: 890_880n, solFeeAmountRelease: 100_000n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), }).sendAndConfirm(umi); const recipeData = await fetchRecipeV1(umi, recipe); @@ -112,7 +113,7 @@ test('it can swap tokens for an asset with reroll', async (t) => { solFeeAmountCapture: 890_880n, solFeeAmountRelease: 100_000n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), bump: recipe[1], }); @@ -173,6 +174,151 @@ test('it can swap tokens for an asset with reroll', async (t) => { t.regex(assetAfter.uri, uriRegex); }); +test('it can swap tokens for an asset without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: umi.identity.publicKey, + amount: 1000, + }).sendAndConfirm(umi); + + await initEscrowV2(umi, {}).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(umi.identity.publicKey), + ]); + + t.like(await fetchEscrowV2(umi, escrow), { + authority: umi.identity.publicKey, + bump: escrow[1], + }); + + // Transfer the assets to the escrow. + // eslint-disable-next-line no-restricted-syntax + for (const asset of assets) { + // eslint-disable-next-line no-await-in-loop + await transfer(umi, { + asset, + collection, + newOwner: escrow, + }).sendAndConfirm(umi); + } + + const recipe = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('recipe'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + await initRecipeV1(umi, { + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmountCapture: 1, + feeAmountRelease: 1, + solFeeAmountCapture: 890_880n, + solFeeAmountRelease: 100_000n, + path: buildPath([Path.NoRerollMetadata]), + }).sendAndConfirm(umi); + + const recipeData = await fetchRecipeV1(umi, recipe); + t.like(recipeData, { + publicKey: publicKey(recipe), + collection: collection.publicKey, + authority: umi.identity.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmountCapture: 1n, + feeAmountRelease: 1n, + solFeeAmountCapture: 890_880n, + solFeeAmountRelease: 100_000n, + count: 1n, + path: buildPath([Path.NoRerollMetadata]), + bump: recipe[1], + }); + + const userTokenBefore = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenBefore.token.amount, 1000n); + try { + await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.fail('Escrow token account should not exist'); + } catch (e) { + t.is(e.name, 'AccountNotFoundError'); + } + + const assetBefore = await fetchAsset(umi, assets[0].publicKey); + t.is(assetBefore.owner, publicKey(escrow)); + + await captureV2(umi, { + owner: umi.identity, + authority: umi.identity, + recipe, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: feeLocation.publicKey, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 5n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 994n); + const feeTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + feeLocation.publicKey + ); + t.deepEqual(feeTokenAfter.token.amount, 1n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, umi.identity.publicKey); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); + test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) => { // Given a Umi instance using the project's plugin. const umi = await createUmi(); @@ -238,7 +384,7 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) feeAmountRelease: 1, solFeeAmountCapture: 890_880n, solFeeAmountRelease: 100_000n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), }).sendAndConfirm(umi); await addCollectionPlugin(umi, { @@ -267,7 +413,7 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) solFeeAmountCapture: 890_880n, solFeeAmountRelease: 100_000n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), bump: recipe[1], }); @@ -307,3 +453,137 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) const uriRegex = new RegExp(`${recipeData.uri}\\d+\\.json`); t.regex(assetAfter.uri, uriRegex); }); + +test('it can swap tokens for an asset as UpdateDelegate without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: umi.identity.publicKey, + amount: 1000, + }).sendAndConfirm(umi); + + await initEscrowV2(umi, {}).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(umi.identity.publicKey), + ]); + + t.like(await fetchEscrowV2(umi, escrow), { + authority: umi.identity.publicKey, + bump: escrow[1], + }); + + // Transfer the assets to the escrow. + // eslint-disable-next-line no-restricted-syntax + for (const asset of assets) { + // eslint-disable-next-line no-await-in-loop + await transfer(umi, { + asset, + collection, + newOwner: escrow, + }).sendAndConfirm(umi); + } + + const recipe = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('recipe'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + await initRecipeV1(umi, { + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmountCapture: 1, + feeAmountRelease: 1, + solFeeAmountCapture: 890_880n, + solFeeAmountRelease: 100_000n, + path: buildPath([Path.NoRerollMetadata]), + }).sendAndConfirm(umi); + + await addCollectionPlugin(umi, { + collection: collection.publicKey, + plugin: { + type: 'UpdateDelegate', + additionalDelegates: [], + authority: { type: 'Address', address: publicKey(recipe) }, + }, + }).sendAndConfirm(umi); + + const recipeData = await fetchRecipeV1(umi, recipe); + t.like(recipeData, { + publicKey: publicKey(recipe), + collection: collection.publicKey, + authority: umi.identity.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmountCapture: 1n, + feeAmountRelease: 1n, + solFeeAmountCapture: 890_880n, + solFeeAmountRelease: 100_000n, + count: 1n, + path: buildPath([Path.NoRerollMetadata]), + bump: recipe[1], + }); + + await captureV2(umi, { + owner: umi.identity, + authority: recipe, + recipe, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: feeLocation.publicKey, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 5n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 994n); + const feeTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + feeLocation.publicKey + ); + t.deepEqual(feeTokenAfter.token.amount, 1n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, umi.identity.publicKey); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); diff --git a/clients/js/test/v2/releaseV2.test.ts b/clients/js/test/v2/releaseV2.test.ts index 0f23f5d..a3db482 100644 --- a/clients/js/test/v2/releaseV2.test.ts +++ b/clients/js/test/v2/releaseV2.test.ts @@ -12,6 +12,7 @@ import { } from '@metaplex-foundation/umi/serializers'; import { addCollectionPlugin, fetchAsset } from '@metaplex-foundation/mpl-core'; import { + buildPath, EscrowV2, fetchEscrowV2, fetchRecipeV1, @@ -23,7 +24,7 @@ import { } from '../../src'; import { createCoreCollection, createUmi } from '../_setup'; -test('it can swap an asset for tokens', async (t) => { +test('it can swap an asset for tokens with reroll', async (t) => { // Given a Umi instance using the project's plugin. const umi = await createUmi(); const feeLocation = generateSigner(umi); @@ -72,7 +73,133 @@ test('it can swap an asset for tokens', async (t) => { feeAmountRelease: 0, solFeeAmountCapture: 100_000n, solFeeAmountRelease: 890_880n, - path: Path.RerollMetadata, + path: buildPath([Path.RerollMetadata]), + }).sendAndConfirm(umi); + + const recipe = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('recipe'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + const recipeData = await fetchRecipeV1(umi, recipe); + t.like(recipeData, { + publicKey: publicKey(recipe), + collection: collection.publicKey, + authority: umi.identity.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmountCapture: 1n, + feeAmountRelease: 0n, + solFeeAmountCapture: 100_000n, + solFeeAmountRelease: 890_880n, + count: 1n, + path: buildPath([Path.RerollMetadata]), + bump: recipe[1], + }); + + const escrowTokenBefore = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenBefore.token.amount, 1000n); + try { + await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.fail('User token account should not exist'); + } catch (e) { + t.is(e.name, 'AccountNotFoundError'); + } + + t.is(assets[0].owner, umi.identity.publicKey); + + await releaseV2(umi, { + owner: umi.identity, + recipe, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: feeLocation.publicKey, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 995n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 5n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, publicKey(escrow)); + + // Confirm that an asset in the escrow has the correct URI + t.is(assetAfter.uri, `${recipeData.uri}captured.json`); +}); + +test('it can swap an asset for tokens without reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + await initEscrowV2(umi, {}).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(umi.identity.publicKey), + ]); + + t.like(await fetchEscrowV2(umi, escrow), { + authority: umi.identity.publicKey, + bump: escrow[1], + }); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: escrow, + amount: 1000, + }).sendAndConfirm(umi); + + await initRecipeV1(umi, { + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmountCapture: 1, + feeAmountRelease: 0, + solFeeAmountCapture: 100_000n, + solFeeAmountRelease: 890_880n, + path: buildPath([Path.NoRerollMetadata]), }).sendAndConfirm(umi); const recipe = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ @@ -96,7 +223,142 @@ test('it can swap an asset for tokens', async (t) => { solFeeAmountCapture: 100_000n, solFeeAmountRelease: 890_880n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.NoRerollMetadata]), + bump: recipe[1], + }); + + const escrowTokenBefore = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenBefore.token.amount, 1000n); + try { + await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.fail('User token account should not exist'); + } catch (e) { + t.is(e.name, 'AccountNotFoundError'); + } + + t.is(assets[0].owner, umi.identity.publicKey); + + await releaseV2(umi, { + owner: umi.identity, + recipe, + escrow, + asset: assets[0].publicKey, + collection: collection.publicKey, + feeProjectAccount: feeLocation.publicKey, + token: tokenMint.publicKey, + }).sendAndConfirm(umi); + + const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + publicKey(escrow) + ); + t.deepEqual(escrowTokenAfter.token.amount, 995n); + const userTokenAfter = await fetchDigitalAssetWithAssociatedToken( + umi, + tokenMint.publicKey, + umi.identity.publicKey + ); + t.deepEqual(userTokenAfter.token.amount, 5n); + const assetAfter = await fetchAsset(umi, assets[0].publicKey); + t.is(assetAfter.owner, publicKey(escrow)); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); +}); + +test('it can swap an asset for tokens as UpdateDelegate with reroll', async (t) => { + // Given a Umi instance using the project's plugin. + const umi = await createUmi(); + const feeLocation = generateSigner(umi); + const { assets, collection } = await createCoreCollection(umi); + const tokenMint = generateSigner(umi); + await createFungible(umi, { + name: 'Test Token', + uri: 'www.fungible.com', + sellerFeeBasisPoints: { + basisPoints: 0n, + identifier: '%', + decimals: 2, + }, + mint: tokenMint, + }).sendAndConfirm(umi); + + await initEscrowV2(umi, {}).sendAndConfirm(umi); + + const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('escrow'), + publicKeySerializer().serialize(umi.identity.publicKey), + ]); + + t.like(await fetchEscrowV2(umi, escrow), { + authority: umi.identity.publicKey, + bump: escrow[1], + }); + + await mintV1(umi, { + mint: tokenMint.publicKey, + tokenStandard: TokenStandard.Fungible, + tokenOwner: escrow, + amount: 1000, + }).sendAndConfirm(umi); + + await initRecipeV1(umi, { + collection: collection.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9, + min: 0, + amount: 5, + feeAmountCapture: 1, + feeAmountRelease: 0, + solFeeAmountCapture: 100_000n, + solFeeAmountRelease: 890_880n, + path: buildPath([Path.RerollMetadata]), + }).sendAndConfirm(umi); + + const recipe = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ + string({ size: 'variable' }).serialize('recipe'), + publicKeySerializer().serialize(collection.publicKey), + ]); + + await addCollectionPlugin(umi, { + collection: collection.publicKey, + plugin: { + type: 'UpdateDelegate', + additionalDelegates: [], + authority: { type: 'Address', address: publicKey(recipe) }, + }, + }).sendAndConfirm(umi); + + const recipeData = await fetchRecipeV1(umi, recipe); + t.like(recipeData, { + publicKey: publicKey(recipe), + collection: collection.publicKey, + authority: umi.identity.publicKey, + token: tokenMint.publicKey, + feeLocation: feeLocation.publicKey, + name: 'Test Escrow', + uri: 'www.test.com/', + max: 9n, + min: 0n, + amount: 5n, + feeAmountCapture: 1n, + feeAmountRelease: 0n, + solFeeAmountCapture: 100_000n, + solFeeAmountRelease: 890_880n, + count: 1n, + path: buildPath([Path.RerollMetadata]), bump: recipe[1], }); @@ -121,6 +383,7 @@ test('it can swap an asset for tokens', async (t) => { await releaseV2(umi, { owner: umi.identity, + authority: recipe, recipe, escrow, asset: assets[0].publicKey, @@ -143,9 +406,12 @@ test('it can swap an asset for tokens', async (t) => { t.deepEqual(userTokenAfter.token.amount, 5n); const assetAfter = await fetchAsset(umi, assets[0].publicKey); t.is(assetAfter.owner, publicKey(escrow)); + + // Confirm that an asset in the escrow has the correct URI + t.is(assetAfter.uri, `${recipeData.uri}captured.json`); }); -test('it can swap an asset for tokens as UpdateDelegate', async (t) => { +test('it can swap an asset for tokens as UpdateDelegate without reroll', async (t) => { // Given a Umi instance using the project's plugin. const umi = await createUmi(); const feeLocation = generateSigner(umi); @@ -194,7 +460,7 @@ test('it can swap an asset for tokens as UpdateDelegate', async (t) => { feeAmountRelease: 0, solFeeAmountCapture: 100_000n, solFeeAmountRelease: 890_880n, - path: Path.RerollMetadata, + path: buildPath([Path.NoRerollMetadata]), }).sendAndConfirm(umi); const recipe = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [ @@ -227,7 +493,7 @@ test('it can swap an asset for tokens as UpdateDelegate', async (t) => { solFeeAmountCapture: 100_000n, solFeeAmountRelease: 890_880n, count: 1n, - path: Path.RerollMetadata, + path: buildPath([Path.NoRerollMetadata]), bump: recipe[1], }); @@ -275,4 +541,7 @@ test('it can swap an asset for tokens as UpdateDelegate', async (t) => { t.deepEqual(userTokenAfter.token.amount, 5n); const assetAfter = await fetchAsset(umi, assets[0].publicKey); t.is(assetAfter.owner, publicKey(escrow)); + + // Make sure the URI has not changed. + t.is(assetAfter.uri, 'https://example.com/asset'); }); diff --git a/clients/rust/src/generated/types/path.rs b/clients/rust/src/generated/types/internal_path.rs similarity index 94% rename from clients/rust/src/generated/types/path.rs rename to clients/rust/src/generated/types/internal_path.rs index a170110..8368d4d 100644 --- a/clients/rust/src/generated/types/path.rs +++ b/clients/rust/src/generated/types/internal_path.rs @@ -15,6 +15,6 @@ use num_derive::FromPrimitive; #[cfg_attr(not(feature = "anchor"), derive(BorshSerialize, BorshDeserialize))] #[cfg_attr(feature = "anchor", derive(AnchorSerialize, AnchorDeserialize))] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash, FromPrimitive)] -pub enum Path { - RerollMetadata, +pub enum InternalPath { + NoRerollMetadata, } diff --git a/clients/rust/src/generated/types/mod.rs b/clients/rust/src/generated/types/mod.rs index dd978f4..ea1047f 100644 --- a/clients/rust/src/generated/types/mod.rs +++ b/clients/rust/src/generated/types/mod.rs @@ -5,6 +5,6 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -pub(crate) mod r#path; +pub(crate) mod r#internal_path; -pub use self::r#path::*; +pub use self::r#internal_path::*; diff --git a/configs/kinobi.cjs b/configs/kinobi.cjs index eaa713b..765f506 100644 --- a/configs/kinobi.cjs +++ b/configs/kinobi.cjs @@ -17,6 +17,14 @@ kinobi.update( }) ); +kinobi.update( + k.updateDefinedTypesVisitor({ + Path: { + name: "InternalPath", + }, + }) +); + // Update Accounts. kinobi.update( k.updateAccountsVisitor({ diff --git a/idls/mpl_hybrid.json b/idls/mpl_hybrid.json index 868c807..6c89a8f 100644 --- a/idls/mpl_hybrid.json +++ b/idls/mpl_hybrid.json @@ -1327,7 +1327,7 @@ "kind": "enum", "variants": [ { - "name": "RerollMetadata" + "name": "NoRerollMetadata" } ] } diff --git a/programs/mpl-hybrid/src/instructions/capture.rs b/programs/mpl-hybrid/src/instructions/capture.rs index a2e206b..8d9ce54 100644 --- a/programs/mpl-hybrid/src/instructions/capture.rs +++ b/programs/mpl-hybrid/src/instructions/capture.rs @@ -165,7 +165,7 @@ pub fn handler_capture_v1(ctx: Context) -> Result<()> { } //If the path has bit 0 set, we need to update the metadata onchain - if Path::RerollMetadata.check(escrow.path) { + if !Path::NoRerollMetadata.check(escrow.path) { let clock = Clock::get()?; // seed for the random number is a combination of the slot_hash - timestamp let recent_slothashes = &ctx.accounts.recent_blockhashes; diff --git a/programs/mpl-hybrid/src/instructions/capture_v2.rs b/programs/mpl-hybrid/src/instructions/capture_v2.rs index 32b1e2f..0a842e5 100644 --- a/programs/mpl-hybrid/src/instructions/capture_v2.rs +++ b/programs/mpl-hybrid/src/instructions/capture_v2.rs @@ -180,7 +180,7 @@ pub fn handler_capture_v2(ctx: Context) -> Result<()> { } //If the path has bit 0 set, we need to update the metadata onchain - if Path::RerollMetadata.check(recipe.path) { + if !Path::NoRerollMetadata.check(recipe.path) { let clock = Clock::get()?; // seed for the random number is a combination of the slot_hash - timestamp let recent_slothashes = &ctx.accounts.recent_blockhashes; diff --git a/programs/mpl-hybrid/src/instructions/release.rs b/programs/mpl-hybrid/src/instructions/release.rs index 5a9641c..be0fe42 100644 --- a/programs/mpl-hybrid/src/instructions/release.rs +++ b/programs/mpl-hybrid/src/instructions/release.rs @@ -168,7 +168,7 @@ pub fn handler_release_v1(ctx: Context) -> Result<()> { } //If the path has bit 0 set, we need to update the metadata onchain - if Path::RerollMetadata.check(escrow.path) { + if !Path::NoRerollMetadata.check(escrow.path) { //construct the captured uri let mut uri = escrow.uri.clone(); let name = "Captured".to_string(); diff --git a/programs/mpl-hybrid/src/instructions/release_v2.rs b/programs/mpl-hybrid/src/instructions/release_v2.rs index bd2e2cf..24b638c 100644 --- a/programs/mpl-hybrid/src/instructions/release_v2.rs +++ b/programs/mpl-hybrid/src/instructions/release_v2.rs @@ -183,7 +183,7 @@ pub fn handler_release_v2(ctx: Context) -> Result<()> { } //If the path has bit 0 set, we need to update the metadata onchain - if Path::RerollMetadata.check(recipe.path) { + if !Path::NoRerollMetadata.check(recipe.path) { //construct the captured uri let mut uri = recipe.uri.clone(); let name = "Captured".to_string(); diff --git a/programs/mpl-hybrid/src/state/path.rs b/programs/mpl-hybrid/src/state/path.rs index 70ad63c..bccbaff 100644 --- a/programs/mpl-hybrid/src/state/path.rs +++ b/programs/mpl-hybrid/src/state/path.rs @@ -2,11 +2,11 @@ use anchor_lang::prelude::*; #[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)] pub enum Path { - RerollMetadata, + NoRerollMetadata, } impl Path { pub fn check(self, bits: u16) -> bool { - bits & (1 << (self as u16)) == 0 + bits & (1 << (self as u16)) != 0 } }