Skip to content

Commit

Permalink
Integrate the new Burn and Verify instructions (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva authored May 26, 2023
1 parent 1b2c86c commit 989a885
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 100 deletions.
5 changes: 5 additions & 0 deletions .changeset/ninety-ladybugs-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@metaplex-foundation/js': patch
---

Integrate the new Burn and Verify instructions
115 changes: 100 additions & 15 deletions packages/js/src/plugins/nftModule/operations/deleteNft.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { createBurnNftInstruction } from '@metaplex-foundation/mpl-token-metadata';
import { PublicKey } from '@solana/web3.js';
import { createBurnInstruction } from '@metaplex-foundation/mpl-token-metadata';
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
import { Metaplex } from '@/Metaplex';
import {
TokenMetadataAuthorityHolder,
TokenMetadataAuthorityTokenDelegate,
getSignerFromTokenMetadataAuthority,
parseTokenMetadataAuthorization,
} from '../Authorization';
import { TransactionBuilder, TransactionBuilderOptions } from '@/utils';
import {
Operation,
OperationHandler,
OperationScope,
Signer,
SplTokenAmount,
token,
useOperation,
} from '@/types';
import { TransactionBuilder, TransactionBuilderOptions } from '@/utils';
import { Metaplex } from '@/Metaplex';

// -----------------
// Operation
Expand Down Expand Up @@ -50,12 +58,51 @@ export type DeleteNftInput = {
mintAddress: PublicKey;

/**
* The owner of the NFT as a Signer.
* An authority allowed to burn the asset.
*
* Note that Metadata authorities are
* not supported for this instruction.
*
* If a `Signer` is provided directly,
* it will be used as an Holder authority.
*
* @see {@link TokenMetadataAuthority}
* @defaultValue `metaplex.identity()`
*/
authority?:
| Signer
| TokenMetadataAuthorityTokenDelegate
| TokenMetadataAuthorityHolder;

/**
* Alias of `authority` for backwards compatibility.
*
* @deprecated Use `authority` instead.
* @see {@link DeleteNftInput.authority}
*/
owner?: Signer;

/**
* The mint of the parent edition when the asset is a printed edition.
*
* @defaultValue Defaults to not providing a parent edition to the program.
*/
parentEditionMint?: PublicKey;

/**
* The token account of the parent edition when the asset is a printed edition.
*
* @defaultValue Defaults to not providing a parent edition to the program.
*/
parentEditionToken?: PublicKey;

/**
* The edition marker of the asset if it is a printed edition.
*
* @defaultValue Defaults to not providing the edition marker to the program.
*/
editionMarker?: PublicKey;

/**
* The explicit token account linking the provided mint and owner
* accounts, if that account is not their associated token account.
Expand All @@ -74,6 +121,13 @@ export type DeleteNftInput = {
* Size Collection NFT.
*/
collection?: PublicKey;

/**
* The amount of tokens to burn.
*
* @defaultValue `token(1)`
*/
amount?: SplTokenAmount;
};

/**
Expand Down Expand Up @@ -136,14 +190,22 @@ export const deleteNftBuilder = (
const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
const {
mintAddress,
owner = metaplex.identity(),
ownerTokenAccount,
collection,
parentEditionMint,
parentEditionToken,
editionMarker,
amount = token(1),
} = params;

const authority =
params.authority ?? params.owner ?? (metaplex.identity() as Signer);

const systemProgram = metaplex.programs().getSystem(programs);
const tokenProgram = metaplex.programs().getToken(programs);
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);

const owner = getSignerFromTokenMetadataAuthority(authority).publicKey;
const metadata = metaplex.nfts().pdas().metadata({
mint: mintAddress,
programs,
Expand All @@ -156,28 +218,51 @@ export const deleteNftBuilder = (
ownerTokenAccount ??
metaplex.tokens().pdas().associatedTokenAccount({
mint: mintAddress,
owner: owner.publicKey,
owner,
programs,
});

// Auth.
const auth = parseTokenMetadataAuthorization(metaplex, {
mint: mintAddress,
authority:
'__kind' in authority
? authority
: { __kind: 'holder', owner: authority, token: tokenAddress },
programs,
});

return TransactionBuilder.make()
.setFeePayer(payer)
.add({
instruction: createBurnNftInstruction(
instruction: createBurnInstruction(
{
metadata,
owner: owner.publicKey,
mint: mintAddress,
tokenAccount: tokenAddress,
masterEditionAccount: edition,
splTokenProgram: tokenProgram.address,
authority: auth.accounts.authority,
collectionMetadata: collection
? metaplex.nfts().pdas().metadata({ mint: collection, programs })
: undefined,
metadata,
edition,
mint: mintAddress,
token: auth.accounts.token!,
masterEdition: parentEditionMint
? metaplex.nfts().pdas().metadata({
mint: parentEditionMint,
programs,
})
: undefined,
masterEditionMint: parentEditionMint,
masterEditionToken: parentEditionToken,
editionMarker,
tokenRecord: auth.accounts.delegateRecord,
systemProgram: systemProgram.address,
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
splTokenProgram: tokenProgram.address,
},
{ burnArgs: { __kind: 'V1', amount: amount.basisPoints } },
tokenMetadataProgram.address
),
signers: [owner],
signers: auth.signers,
key: params.instructionKey ?? 'deleteNft',
});
};
135 changes: 95 additions & 40 deletions packages/js/src/plugins/nftModule/operations/unverifyNftCollection.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
VerificationArgs,
createUnverifyCollectionInstruction,
createUnverifyInstruction,
createUnverifySizedCollectionItemInstruction,
} from '@metaplex-foundation/mpl-token-metadata';
import { PublicKey } from '@solana/web3.js';
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
import { Metaplex } from '@/Metaplex';
import {
Expand Down Expand Up @@ -74,12 +76,28 @@ export type UnverifyNftCollectionInput = {

/**
* Whether or not the provided `collectionAuthority` is a delegated
* collection authority, i.e. it was approved by the update authority
* using `metaplex.nfts().approveCollectionAuthority()`.
* collection authority, i.e. it was approved by the update authority.
*
* - `false` means the collection authority is the update authority of the collection.
* - `legacyDelegate` means the collection authority is a delegate that was approved
* using the legacy `metaplex.nfts().approveCollectionAuthority()` operation.
* - `metadataDelegate` means the collection authority is a delegate that was approved
* using the new `metaplex.nfts().delegate()` operation.
* - `true` is equivalent to `legacyDelegate` for backwards compatibility.
*
* @defaultValue `false`
*/
isDelegated?: boolean;
isDelegated?: boolean | 'legacyDelegate' | 'metadataDelegate';

/**
* The update authority of the Collection NFT.
*
* This is used to compute the metadata delegate record when
* `isDelegated` is equal to `"metadataDelegate"`.
*
* @defaultValue `metaplex.identity().publicKey`
*/
collectionUpdateAuthority?: PublicKey;
};

/**
Expand Down Expand Up @@ -151,55 +169,92 @@ export const unverifyNftCollectionBuilder = (
isSizedCollection = true,
isDelegated = false,
collectionAuthority = metaplex.identity(),
collectionUpdateAuthority = metaplex.identity().publicKey,
} = params;

// Programs.
const systemProgram = metaplex.programs().getSystem(programs);
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);

const accounts = {
metadata: metaplex.nfts().pdas().metadata({
mint: mintAddress,
programs,
}),
collectionAuthority: collectionAuthority.publicKey,
payer: payer.publicKey,
collectionMint: collectionMintAddress,
collection: metaplex.nfts().pdas().metadata({
mint: collectionMintAddress,
programs,
}),
collectionMasterEditionAccount: metaplex.nfts().pdas().masterEdition({
mint: collectionMintAddress,
programs,
}),
collectionAuthorityRecord: isDelegated
? metaplex.nfts().pdas().collectionAuthorityRecord({
// Accounts.
const metadata = metaplex.nfts().pdas().metadata({
mint: mintAddress,
programs,
});
const collectionMetadata = metaplex.nfts().pdas().metadata({
mint: collectionMintAddress,
programs,
});
const collectionEdition = metaplex.nfts().pdas().masterEdition({
mint: collectionMintAddress,
programs,
});

if (isDelegated === 'legacyDelegate' || isDelegated === true) {
const accounts = {
metadata,
collectionAuthority: collectionAuthority.publicKey,
payer: payer.publicKey,
collectionMint: collectionMintAddress,
collection: collectionMetadata,
collectionMasterEditionAccount: collectionEdition,
collectionAuthorityRecord: metaplex
.nfts()
.pdas()
.collectionAuthorityRecord({
mint: collectionMintAddress,
collectionAuthority: collectionAuthority.publicKey,
programs,
})
: undefined,
};
}),
};

const instruction = isSizedCollection
? createUnverifySizedCollectionItemInstruction(
accounts,
tokenMetadataProgram.address
)
: createUnverifyCollectionInstruction(
accounts,
tokenMetadataProgram.address
);
const instruction = isSizedCollection
? createUnverifySizedCollectionItemInstruction(
accounts,
tokenMetadataProgram.address
)
: createUnverifyCollectionInstruction(
accounts,
tokenMetadataProgram.address
);

return (
TransactionBuilder.make()
return TransactionBuilder.make()
.setFeePayer(payer)

// Unverify the collection.
.add({
instruction,
signers: [payer, collectionAuthority],
key: params.instructionKey ?? 'unverifyCollection',
})
);
});
}

const delegateRecord =
isDelegated === 'metadataDelegate'
? metaplex.nfts().pdas().metadataDelegateRecord({
mint: collectionMintAddress,
type: 'CollectionV1',
updateAuthority: collectionUpdateAuthority,
delegate: collectionAuthority.publicKey,
programs,
})
: undefined;

return TransactionBuilder.make()
.setFeePayer(payer)
.add({
instruction: createUnverifyInstruction(
{
authority: collectionAuthority.publicKey,
delegateRecord,
metadata,
collectionMint: collectionMintAddress,
collectionMetadata,
systemProgram: systemProgram.address,
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
},
{ verificationArgs: VerificationArgs.CollectionV1 },
tokenMetadataProgram.address
),
signers: [collectionAuthority],
key: params.instructionKey ?? 'unverifyCollection',
});
};
15 changes: 11 additions & 4 deletions packages/js/src/plugins/nftModule/operations/unverifyNftCreator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createRemoveCreatorVerificationInstruction } from '@metaplex-foundation/mpl-token-metadata';
import { PublicKey } from '@solana/web3.js';
import {
VerificationArgs,
createUnverifyInstruction,
} from '@metaplex-foundation/mpl-token-metadata';
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
import { Metaplex } from '@/Metaplex';
import {
Expand Down Expand Up @@ -124,6 +127,7 @@ export const unverifyNftCreatorBuilder = (
const { mintAddress, creator = metaplex.identity() } = params;

// Programs.
const systemProgram = metaplex.programs().getSystem(programs);
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);

return (
Expand All @@ -132,14 +136,17 @@ export const unverifyNftCreatorBuilder = (

// Verify the creator.
.add({
instruction: createRemoveCreatorVerificationInstruction(
instruction: createUnverifyInstruction(
{
authority: creator.publicKey,
metadata: metaplex.nfts().pdas().metadata({
mint: mintAddress,
programs,
}),
creator: creator.publicKey,
systemProgram: systemProgram.address,
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
},
{ verificationArgs: VerificationArgs.CreatorV1 },
tokenMetadataProgram.address
),
signers: [creator],
Expand Down
Loading

0 comments on commit 989a885

Please sign in to comment.