Skip to content
This repository was archived by the owner on Mar 26, 2025. It is now read-only.

Commit 989a885

Browse files
authoredMay 26, 2023
Integrate the new Burn and Verify instructions (#519)

9 files changed

+418
-100
lines changed
 

‎.changeset/ninety-ladybugs-obey.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@metaplex-foundation/js': patch
3+
---
4+
5+
Integrate the new Burn and Verify instructions

‎packages/js/src/plugins/nftModule/operations/deleteNft.ts

+100-15
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
import { createBurnNftInstruction } from '@metaplex-foundation/mpl-token-metadata';
2-
import { PublicKey } from '@solana/web3.js';
1+
import { createBurnInstruction } from '@metaplex-foundation/mpl-token-metadata';
2+
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
33
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
4-
import { Metaplex } from '@/Metaplex';
4+
import {
5+
TokenMetadataAuthorityHolder,
6+
TokenMetadataAuthorityTokenDelegate,
7+
getSignerFromTokenMetadataAuthority,
8+
parseTokenMetadataAuthorization,
9+
} from '../Authorization';
10+
import { TransactionBuilder, TransactionBuilderOptions } from '@/utils';
511
import {
612
Operation,
713
OperationHandler,
814
OperationScope,
915
Signer,
16+
SplTokenAmount,
17+
token,
1018
useOperation,
1119
} from '@/types';
12-
import { TransactionBuilder, TransactionBuilderOptions } from '@/utils';
20+
import { Metaplex } from '@/Metaplex';
1321

1422
// -----------------
1523
// Operation
@@ -50,12 +58,51 @@ export type DeleteNftInput = {
5058
mintAddress: PublicKey;
5159

5260
/**
53-
* The owner of the NFT as a Signer.
61+
* An authority allowed to burn the asset.
5462
*
63+
* Note that Metadata authorities are
64+
* not supported for this instruction.
65+
*
66+
* If a `Signer` is provided directly,
67+
* it will be used as an Holder authority.
68+
*
69+
* @see {@link TokenMetadataAuthority}
5570
* @defaultValue `metaplex.identity()`
5671
*/
72+
authority?:
73+
| Signer
74+
| TokenMetadataAuthorityTokenDelegate
75+
| TokenMetadataAuthorityHolder;
76+
77+
/**
78+
* Alias of `authority` for backwards compatibility.
79+
*
80+
* @deprecated Use `authority` instead.
81+
* @see {@link DeleteNftInput.authority}
82+
*/
5783
owner?: Signer;
5884

85+
/**
86+
* The mint of the parent edition when the asset is a printed edition.
87+
*
88+
* @defaultValue Defaults to not providing a parent edition to the program.
89+
*/
90+
parentEditionMint?: PublicKey;
91+
92+
/**
93+
* The token account of the parent edition when the asset is a printed edition.
94+
*
95+
* @defaultValue Defaults to not providing a parent edition to the program.
96+
*/
97+
parentEditionToken?: PublicKey;
98+
99+
/**
100+
* The edition marker of the asset if it is a printed edition.
101+
*
102+
* @defaultValue Defaults to not providing the edition marker to the program.
103+
*/
104+
editionMarker?: PublicKey;
105+
59106
/**
60107
* The explicit token account linking the provided mint and owner
61108
* accounts, if that account is not their associated token account.
@@ -74,6 +121,13 @@ export type DeleteNftInput = {
74121
* Size Collection NFT.
75122
*/
76123
collection?: PublicKey;
124+
125+
/**
126+
* The amount of tokens to burn.
127+
*
128+
* @defaultValue `token(1)`
129+
*/
130+
amount?: SplTokenAmount;
77131
};
78132

79133
/**
@@ -136,14 +190,22 @@ export const deleteNftBuilder = (
136190
const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
137191
const {
138192
mintAddress,
139-
owner = metaplex.identity(),
140193
ownerTokenAccount,
141194
collection,
195+
parentEditionMint,
196+
parentEditionToken,
197+
editionMarker,
198+
amount = token(1),
142199
} = params;
143200

201+
const authority =
202+
params.authority ?? params.owner ?? (metaplex.identity() as Signer);
203+
204+
const systemProgram = metaplex.programs().getSystem(programs);
144205
const tokenProgram = metaplex.programs().getToken(programs);
145206
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
146207

208+
const owner = getSignerFromTokenMetadataAuthority(authority).publicKey;
147209
const metadata = metaplex.nfts().pdas().metadata({
148210
mint: mintAddress,
149211
programs,
@@ -156,28 +218,51 @@ export const deleteNftBuilder = (
156218
ownerTokenAccount ??
157219
metaplex.tokens().pdas().associatedTokenAccount({
158220
mint: mintAddress,
159-
owner: owner.publicKey,
221+
owner,
160222
programs,
161223
});
162224

225+
// Auth.
226+
const auth = parseTokenMetadataAuthorization(metaplex, {
227+
mint: mintAddress,
228+
authority:
229+
'__kind' in authority
230+
? authority
231+
: { __kind: 'holder', owner: authority, token: tokenAddress },
232+
programs,
233+
});
234+
163235
return TransactionBuilder.make()
164236
.setFeePayer(payer)
165237
.add({
166-
instruction: createBurnNftInstruction(
238+
instruction: createBurnInstruction(
167239
{
168-
metadata,
169-
owner: owner.publicKey,
170-
mint: mintAddress,
171-
tokenAccount: tokenAddress,
172-
masterEditionAccount: edition,
173-
splTokenProgram: tokenProgram.address,
240+
authority: auth.accounts.authority,
174241
collectionMetadata: collection
175242
? metaplex.nfts().pdas().metadata({ mint: collection, programs })
176243
: undefined,
244+
metadata,
245+
edition,
246+
mint: mintAddress,
247+
token: auth.accounts.token!,
248+
masterEdition: parentEditionMint
249+
? metaplex.nfts().pdas().metadata({
250+
mint: parentEditionMint,
251+
programs,
252+
})
253+
: undefined,
254+
masterEditionMint: parentEditionMint,
255+
masterEditionToken: parentEditionToken,
256+
editionMarker,
257+
tokenRecord: auth.accounts.delegateRecord,
258+
systemProgram: systemProgram.address,
259+
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
260+
splTokenProgram: tokenProgram.address,
177261
},
262+
{ burnArgs: { __kind: 'V1', amount: amount.basisPoints } },
178263
tokenMetadataProgram.address
179264
),
180-
signers: [owner],
265+
signers: auth.signers,
181266
key: params.instructionKey ?? 'deleteNft',
182267
});
183268
};

‎packages/js/src/plugins/nftModule/operations/unverifyNftCollection.ts

+95-40
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {
2+
VerificationArgs,
23
createUnverifyCollectionInstruction,
4+
createUnverifyInstruction,
35
createUnverifySizedCollectionItemInstruction,
46
} from '@metaplex-foundation/mpl-token-metadata';
5-
import { PublicKey } from '@solana/web3.js';
7+
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
68
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
79
import { Metaplex } from '@/Metaplex';
810
import {
@@ -74,12 +76,28 @@ export type UnverifyNftCollectionInput = {
7476

7577
/**
7678
* Whether or not the provided `collectionAuthority` is a delegated
77-
* collection authority, i.e. it was approved by the update authority
78-
* using `metaplex.nfts().approveCollectionAuthority()`.
79+
* collection authority, i.e. it was approved by the update authority.
80+
*
81+
* - `false` means the collection authority is the update authority of the collection.
82+
* - `legacyDelegate` means the collection authority is a delegate that was approved
83+
* using the legacy `metaplex.nfts().approveCollectionAuthority()` operation.
84+
* - `metadataDelegate` means the collection authority is a delegate that was approved
85+
* using the new `metaplex.nfts().delegate()` operation.
86+
* - `true` is equivalent to `legacyDelegate` for backwards compatibility.
7987
*
8088
* @defaultValue `false`
8189
*/
82-
isDelegated?: boolean;
90+
isDelegated?: boolean | 'legacyDelegate' | 'metadataDelegate';
91+
92+
/**
93+
* The update authority of the Collection NFT.
94+
*
95+
* This is used to compute the metadata delegate record when
96+
* `isDelegated` is equal to `"metadataDelegate"`.
97+
*
98+
* @defaultValue `metaplex.identity().publicKey`
99+
*/
100+
collectionUpdateAuthority?: PublicKey;
83101
};
84102

85103
/**
@@ -151,55 +169,92 @@ export const unverifyNftCollectionBuilder = (
151169
isSizedCollection = true,
152170
isDelegated = false,
153171
collectionAuthority = metaplex.identity(),
172+
collectionUpdateAuthority = metaplex.identity().publicKey,
154173
} = params;
155174

156175
// Programs.
176+
const systemProgram = metaplex.programs().getSystem(programs);
157177
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
158178

159-
const accounts = {
160-
metadata: metaplex.nfts().pdas().metadata({
161-
mint: mintAddress,
162-
programs,
163-
}),
164-
collectionAuthority: collectionAuthority.publicKey,
165-
payer: payer.publicKey,
166-
collectionMint: collectionMintAddress,
167-
collection: metaplex.nfts().pdas().metadata({
168-
mint: collectionMintAddress,
169-
programs,
170-
}),
171-
collectionMasterEditionAccount: metaplex.nfts().pdas().masterEdition({
172-
mint: collectionMintAddress,
173-
programs,
174-
}),
175-
collectionAuthorityRecord: isDelegated
176-
? metaplex.nfts().pdas().collectionAuthorityRecord({
179+
// Accounts.
180+
const metadata = metaplex.nfts().pdas().metadata({
181+
mint: mintAddress,
182+
programs,
183+
});
184+
const collectionMetadata = metaplex.nfts().pdas().metadata({
185+
mint: collectionMintAddress,
186+
programs,
187+
});
188+
const collectionEdition = metaplex.nfts().pdas().masterEdition({
189+
mint: collectionMintAddress,
190+
programs,
191+
});
192+
193+
if (isDelegated === 'legacyDelegate' || isDelegated === true) {
194+
const accounts = {
195+
metadata,
196+
collectionAuthority: collectionAuthority.publicKey,
197+
payer: payer.publicKey,
198+
collectionMint: collectionMintAddress,
199+
collection: collectionMetadata,
200+
collectionMasterEditionAccount: collectionEdition,
201+
collectionAuthorityRecord: metaplex
202+
.nfts()
203+
.pdas()
204+
.collectionAuthorityRecord({
177205
mint: collectionMintAddress,
178206
collectionAuthority: collectionAuthority.publicKey,
179207
programs,
180-
})
181-
: undefined,
182-
};
208+
}),
209+
};
183210

184-
const instruction = isSizedCollection
185-
? createUnverifySizedCollectionItemInstruction(
186-
accounts,
187-
tokenMetadataProgram.address
188-
)
189-
: createUnverifyCollectionInstruction(
190-
accounts,
191-
tokenMetadataProgram.address
192-
);
211+
const instruction = isSizedCollection
212+
? createUnverifySizedCollectionItemInstruction(
213+
accounts,
214+
tokenMetadataProgram.address
215+
)
216+
: createUnverifyCollectionInstruction(
217+
accounts,
218+
tokenMetadataProgram.address
219+
);
193220

194-
return (
195-
TransactionBuilder.make()
221+
return TransactionBuilder.make()
196222
.setFeePayer(payer)
197-
198-
// Unverify the collection.
199223
.add({
200224
instruction,
201225
signers: [payer, collectionAuthority],
202226
key: params.instructionKey ?? 'unverifyCollection',
203-
})
204-
);
227+
});
228+
}
229+
230+
const delegateRecord =
231+
isDelegated === 'metadataDelegate'
232+
? metaplex.nfts().pdas().metadataDelegateRecord({
233+
mint: collectionMintAddress,
234+
type: 'CollectionV1',
235+
updateAuthority: collectionUpdateAuthority,
236+
delegate: collectionAuthority.publicKey,
237+
programs,
238+
})
239+
: undefined;
240+
241+
return TransactionBuilder.make()
242+
.setFeePayer(payer)
243+
.add({
244+
instruction: createUnverifyInstruction(
245+
{
246+
authority: collectionAuthority.publicKey,
247+
delegateRecord,
248+
metadata,
249+
collectionMint: collectionMintAddress,
250+
collectionMetadata,
251+
systemProgram: systemProgram.address,
252+
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
253+
},
254+
{ verificationArgs: VerificationArgs.CollectionV1 },
255+
tokenMetadataProgram.address
256+
),
257+
signers: [collectionAuthority],
258+
key: params.instructionKey ?? 'unverifyCollection',
259+
});
205260
};

‎packages/js/src/plugins/nftModule/operations/unverifyNftCreator.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { createRemoveCreatorVerificationInstruction } from '@metaplex-foundation/mpl-token-metadata';
2-
import { PublicKey } from '@solana/web3.js';
1+
import {
2+
VerificationArgs,
3+
createUnverifyInstruction,
4+
} from '@metaplex-foundation/mpl-token-metadata';
5+
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
36
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
47
import { Metaplex } from '@/Metaplex';
58
import {
@@ -124,6 +127,7 @@ export const unverifyNftCreatorBuilder = (
124127
const { mintAddress, creator = metaplex.identity() } = params;
125128

126129
// Programs.
130+
const systemProgram = metaplex.programs().getSystem(programs);
127131
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
128132

129133
return (
@@ -132,14 +136,17 @@ export const unverifyNftCreatorBuilder = (
132136

133137
// Verify the creator.
134138
.add({
135-
instruction: createRemoveCreatorVerificationInstruction(
139+
instruction: createUnverifyInstruction(
136140
{
141+
authority: creator.publicKey,
137142
metadata: metaplex.nfts().pdas().metadata({
138143
mint: mintAddress,
139144
programs,
140145
}),
141-
creator: creator.publicKey,
146+
systemProgram: systemProgram.address,
147+
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
142148
},
149+
{ verificationArgs: VerificationArgs.CreatorV1 },
143150
tokenMetadataProgram.address
144151
),
145152
signers: [creator],

‎packages/js/src/plugins/nftModule/operations/verifyNftCollection.ts

+90-36
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {
2+
VerificationArgs,
23
createVerifyCollectionInstruction,
4+
createVerifyInstruction,
35
createVerifySizedCollectionItemInstruction,
46
} from '@metaplex-foundation/mpl-token-metadata';
5-
import { PublicKey } from '@solana/web3.js';
7+
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
68
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
79
import { Metaplex } from '@/Metaplex';
810
import {
@@ -74,12 +76,28 @@ export type VerifyNftCollectionInput = {
7476

7577
/**
7678
* Whether or not the provided `collectionAuthority` is a delegated
77-
* collection authority, i.e. it was approved by the update authority
78-
* using `metaplex.nfts().approveCollectionAuthority()`.
79+
* collection authority, i.e. it was approved by the update authority.
80+
*
81+
* - `false` means the collection authority is the update authority of the collection.
82+
* - `legacyDelegate` means the collection authority is a delegate that was approved
83+
* using the legacy `metaplex.nfts().approveCollectionAuthority()` operation.
84+
* - `metadataDelegate` means the collection authority is a delegate that was approved
85+
* using the new `metaplex.nfts().delegate()` operation.
86+
* - `true` is equivalent to `legacyDelegate` for backwards compatibility.
7987
*
8088
* @defaultValue `false`
8189
*/
82-
isDelegated?: boolean;
90+
isDelegated?: boolean | 'legacyDelegate' | 'metadataDelegate';
91+
92+
/**
93+
* The update authority of the Collection NFT.
94+
*
95+
* This is used to compute the metadata delegate record when
96+
* `isDelegated` is equal to `"metadataDelegate"`.
97+
*
98+
* @defaultValue `metaplex.identity().publicKey`
99+
*/
100+
collectionUpdateAuthority?: PublicKey;
83101
};
84102

85103
/**
@@ -151,37 +169,45 @@ export const verifyNftCollectionBuilder = (
151169
isSizedCollection = true,
152170
isDelegated = false,
153171
collectionAuthority = metaplex.identity(),
172+
collectionUpdateAuthority = metaplex.identity().publicKey,
154173
} = params;
155174

156175
// Programs.
176+
const systemProgram = metaplex.programs().getSystem(programs);
157177
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
158178

159-
const accounts = {
160-
metadata: metaplex.nfts().pdas().metadata({
161-
mint: mintAddress,
162-
programs,
163-
}),
164-
collectionAuthority: collectionAuthority.publicKey,
165-
payer: payer.publicKey,
166-
collectionMint: collectionMintAddress,
167-
collection: metaplex.nfts().pdas().metadata({
168-
mint: collectionMintAddress,
169-
programs,
170-
}),
171-
collectionMasterEditionAccount: metaplex.nfts().pdas().masterEdition({
172-
mint: collectionMintAddress,
173-
programs,
174-
}),
175-
};
176-
177-
const instruction = isSizedCollection
178-
? createVerifySizedCollectionItemInstruction(
179-
accounts,
180-
tokenMetadataProgram.address
181-
)
182-
: createVerifyCollectionInstruction(accounts, tokenMetadataProgram.address);
179+
// Accounts.
180+
const metadata = metaplex.nfts().pdas().metadata({
181+
mint: mintAddress,
182+
programs,
183+
});
184+
const collectionMetadata = metaplex.nfts().pdas().metadata({
185+
mint: collectionMintAddress,
186+
programs,
187+
});
188+
const collectionEdition = metaplex.nfts().pdas().masterEdition({
189+
mint: collectionMintAddress,
190+
programs,
191+
});
183192

184-
if (isDelegated) {
193+
if (isDelegated === 'legacyDelegate' || isDelegated === true) {
194+
const accounts = {
195+
metadata,
196+
collectionAuthority: collectionAuthority.publicKey,
197+
payer: payer.publicKey,
198+
collectionMint: collectionMintAddress,
199+
collection: collectionMetadata,
200+
collectionMasterEditionAccount: collectionEdition,
201+
};
202+
const instruction = isSizedCollection
203+
? createVerifySizedCollectionItemInstruction(
204+
accounts,
205+
tokenMetadataProgram.address
206+
)
207+
: createVerifyCollectionInstruction(
208+
accounts,
209+
tokenMetadataProgram.address
210+
);
185211
instruction.keys.push({
186212
pubkey: metaplex.nfts().pdas().collectionAuthorityRecord({
187213
mint: collectionMintAddress,
@@ -191,17 +217,45 @@ export const verifyNftCollectionBuilder = (
191217
isWritable: false,
192218
isSigner: false,
193219
});
194-
}
195220

196-
return (
197-
TransactionBuilder.make()
221+
return TransactionBuilder.make()
198222
.setFeePayer(payer)
199-
200-
// Verify the collection.
201223
.add({
202224
instruction,
203225
signers: [payer, collectionAuthority],
204226
key: params.instructionKey ?? 'verifyCollection',
205-
})
206-
);
227+
});
228+
}
229+
230+
const delegateRecord =
231+
isDelegated === 'metadataDelegate'
232+
? metaplex.nfts().pdas().metadataDelegateRecord({
233+
mint: collectionMintAddress,
234+
type: 'CollectionV1',
235+
updateAuthority: collectionUpdateAuthority,
236+
delegate: collectionAuthority.publicKey,
237+
programs,
238+
})
239+
: undefined;
240+
241+
return TransactionBuilder.make()
242+
.setFeePayer(payer)
243+
.add({
244+
instruction: createVerifyInstruction(
245+
{
246+
authority: collectionAuthority.publicKey,
247+
delegateRecord,
248+
metadata,
249+
collectionMint: collectionMintAddress,
250+
collectionMetadata,
251+
collectionMasterEdition: collectionEdition,
252+
systemProgram: systemProgram.address,
253+
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
254+
},
255+
{ verificationArgs: VerificationArgs.CollectionV1 },
256+
tokenMetadataProgram.address
257+
),
258+
signers: [collectionAuthority],
259+
key: params.instructionKey ?? 'verifyCollection',
260+
});
207261
};

‎packages/js/src/plugins/nftModule/operations/verifyNftCreator.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { createSignMetadataInstruction } from '@metaplex-foundation/mpl-token-metadata';
2-
import { PublicKey } from '@solana/web3.js';
1+
import {
2+
VerificationArgs,
3+
createVerifyInstruction,
4+
} from '@metaplex-foundation/mpl-token-metadata';
5+
import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
36
import { SendAndConfirmTransactionResponse } from '../../rpcModule';
47
import { Metaplex } from '@/Metaplex';
58
import {
@@ -124,6 +127,7 @@ export const verifyNftCreatorBuilder = (
124127
const { mintAddress, creator = metaplex.identity() } = params;
125128

126129
// Programs.
130+
const systemProgram = metaplex.programs().getSystem(programs);
127131
const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
128132

129133
return (
@@ -132,14 +136,17 @@ export const verifyNftCreatorBuilder = (
132136

133137
// Verify the creator.
134138
.add({
135-
instruction: createSignMetadataInstruction(
139+
instruction: createVerifyInstruction(
136140
{
141+
authority: creator.publicKey,
137142
metadata: metaplex.nfts().pdas().metadata({
138143
mint: mintAddress,
139144
programs,
140145
}),
141-
creator: creator.publicKey,
146+
systemProgram: systemProgram.address,
147+
sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY,
142148
},
149+
{ verificationArgs: VerificationArgs.CreatorV1 },
143150
tokenMetadataProgram.address
144151
),
145152
signers: [creator],

‎packages/js/test/plugins/nftModule/deleteNft.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ test('[nftModule] the update authority of an NFT cannot delete it', async (t: Te
9393
});
9494

9595
// Then we expect an error.
96-
await assertThrows(t, promise, /InvalidOwner: Invalid Owner/);
96+
await assertThrows(t, promise, /Invalid authority type/);
9797

9898
// And the NFT accounts still exist.
9999
const accounts = await mx

‎packages/js/test/plugins/nftModule/unverifyNftCollection.test.ts

+53
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,56 @@ test('[nftModule] it can unverify the legacy collection of an NFT item', async (
8989
},
9090
} as unknown as Specifications<Nft>);
9191
});
92+
93+
test('[nftModule] it can unverify the collection of an NFT item as a metadata delegate', async (t: Test) => {
94+
// Given a Metaplex instance.
95+
const mx = await metaplex();
96+
97+
// And an existing NFT with an verified collection.
98+
const collectionAuthority = Keypair.generate();
99+
const collection = await createCollectionNft(mx, {
100+
updateAuthority: collectionAuthority,
101+
});
102+
const nft = await createNft(mx, {
103+
collection: collection.address,
104+
collectionAuthority,
105+
});
106+
t.true(nft.collection, 'nft has a collection');
107+
t.true(nft.collection?.verified, 'nft collection is verified');
108+
await assertRefreshedCollectionHasSize(t, mx, collection, 1);
109+
110+
// And a metadata delegate approved by the collection's update authority.
111+
const collectionDelegate = Keypair.generate();
112+
await mx.nfts().delegate({
113+
nftOrSft: collection,
114+
authority: collectionAuthority,
115+
delegate: {
116+
type: 'CollectionV1',
117+
delegate: collectionDelegate.publicKey,
118+
updateAuthority: collection.updateAuthorityAddress,
119+
},
120+
});
121+
122+
// When the metadata delegate unverifies the collection.
123+
await mx.nfts().unverifyCollection({
124+
mintAddress: nft.address,
125+
collectionMintAddress: nft.collection!.address,
126+
collectionAuthority: collectionDelegate,
127+
collectionUpdateAuthority: collectionAuthority.publicKey,
128+
isDelegated: 'metadataDelegate',
129+
});
130+
131+
// Then the NFT collection should be unverified.
132+
const updatedNft = await mx.nfts().refresh(nft);
133+
spok(t, updatedNft, {
134+
$topic: 'Updated Nft',
135+
model: 'nft',
136+
collection: {
137+
address: spokSamePubkey(collection.address),
138+
verified: false,
139+
},
140+
} as unknown as Specifications<Nft>);
141+
142+
// And the collection should have the updated size.
143+
await assertRefreshedCollectionHasSize(t, mx, collection, 0);
144+
});

‎packages/js/test/plugins/nftModule/verifyNftCollection.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,55 @@ test('[nftModule] it can verify the legacy collection of an NFT item', async (t:
8686
},
8787
} as unknown as Specifications<Nft>);
8888
});
89+
90+
test('[nftModule] it can verify the collection of an NFT item as a metadata delegate', async (t: Test) => {
91+
// Given a Metaplex instance.
92+
const mx = await metaplex();
93+
94+
// And an existing NFT with an unverified collection.
95+
const collectionAuthority = Keypair.generate();
96+
const collection = await createCollectionNft(mx, {
97+
updateAuthority: collectionAuthority,
98+
});
99+
const nft = await createNft(mx, {
100+
collection: collection.address,
101+
});
102+
t.true(nft.collection, 'nft has a collection');
103+
t.false(nft.collection?.verified, 'nft collection is not verified');
104+
await assertRefreshedCollectionHasSize(t, mx, collection, 0);
105+
106+
// And a metadata delegate approved by the collection's update authority.
107+
const collectionDelegate = Keypair.generate();
108+
await mx.nfts().delegate({
109+
nftOrSft: collection,
110+
authority: collectionAuthority,
111+
delegate: {
112+
type: 'CollectionV1',
113+
delegate: collectionDelegate.publicKey,
114+
updateAuthority: collection.updateAuthorityAddress,
115+
},
116+
});
117+
118+
// When the metadata delegate verifies the collection.
119+
await mx.nfts().verifyCollection({
120+
mintAddress: nft.address,
121+
collectionMintAddress: nft.collection!.address,
122+
collectionAuthority: collectionDelegate,
123+
collectionUpdateAuthority: collectionAuthority.publicKey,
124+
isDelegated: 'metadataDelegate',
125+
});
126+
127+
// Then the NFT collection should be verified.
128+
const updatedNft = await mx.nfts().refresh(nft);
129+
spok(t, updatedNft, {
130+
$topic: 'Updated Nft',
131+
model: 'nft',
132+
collection: {
133+
address: spokSamePubkey(collection.address),
134+
verified: true,
135+
},
136+
} as unknown as Specifications<Nft>);
137+
138+
// And the collection should have the updated size.
139+
await assertRefreshedCollectionHasSize(t, mx, collection, 1);
140+
});

0 commit comments

Comments
 (0)
This repository has been archived.