Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Token-2022 support #17

Merged
merged 70 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
b4272ae
Add Token-2022 program
febo Jul 20, 2023
c09b4db
Add asserts for Token-2022
febo Jul 20, 2023
03cf3fe
Make token program account optional
febo Jul 20, 2023
cb25c79
Update asserts for Token-2022
febo Jul 20, 2023
7cfad35
Add Token-2022 types
febo Jul 20, 2023
644f954
Support multiple token programs
febo Jul 20, 2023
fae4ba0
wip: update tests to support multiple token programs
febo Jul 20, 2023
2a34bb6
Update generated clients
febo Jul 20, 2023
29087ab
Refactor unpack helper
febo Jul 23, 2023
fd6313d
Fix tests for Token-2022
febo Jul 23, 2023
a5ddc7a
Fix tests for Token-2022
febo Jul 23, 2023
cad0af3
Update edition helper
febo Jul 23, 2023
45e4e37
Add Token-2022 support to burn instruction
febo Jul 23, 2023
a92cfd4
Use transfer checked
febo Jul 24, 2023
40421f0
Remove spl-token dependency
febo Jul 24, 2023
5098c4a
Update mpl-utils dependency
febo Jul 29, 2023
d0a815c
Merge 'main' into febo/token-2022
febo Sep 7, 2023
d46f355
Update generated code
febo Sep 7, 2023
6f60362
Merge branch 'main' into febo/token-2022
febo Sep 8, 2023
dab89a1
Update generated clients
febo Sep 8, 2023
93d2cbf
Merge branch 'main' into febo/token-2022
febo Sep 8, 2023
af54f16
Add missing option
febo Sep 8, 2023
794e825
Add missing spl token program
febo Sep 8, 2023
be7991c
Update spl-token program resolver condition
febo Sep 8, 2023
b79b95d
Mint extensions validation
febo Sep 8, 2023
2116609
Add creation and validation for spl-token-2022 mints
febo Sep 11, 2023
23b6b77
Add metadata pointer error
febo Sep 11, 2023
216876c
Update spl-token-2022 version
febo Sep 11, 2023
97b973f
Regenerate clients
febo Sep 11, 2023
47a9ea2
Merge branch 'main' into febo/token-2022
febo Sep 11, 2023
58ec37f
Update tests
febo Sep 11, 2023
1b77381
Update generated code
febo Sep 11, 2023
f96aadf
Move validation to a sparate module
febo Sep 12, 2023
e8b2503
Add mint and token accounts validation
febo Sep 12, 2023
0dc5f0d
Add token program test
febo Sep 12, 2023
37c0897
Add spl-token-2022 tests
febo Sep 12, 2023
314c6d8
Update generated code
febo Sep 12, 2023
85d8d64
Add token account validation on transfer
febo Sep 12, 2023
2b328e9
Restrict token program
febo Oct 14, 2023
69e345e
Merge branch 'main' into febo/token-2022
febo Oct 14, 2023
535ba30
Fix warnings
febo Oct 14, 2023
c4d3d59
Validate token owner
febo Oct 20, 2023
636f237
Validate account owner in legacy instructions
febo Oct 20, 2023
b3b745a
Update clients
febo Oct 20, 2023
24b6b5c
Fix error type
febo Oct 20, 2023
7139b87
Check close authority on destination account
febo Nov 18, 2023
eeb8432
Check immutable owner extension on token account
febo Nov 18, 2023
a674a40
Refactor collection check
febo Nov 19, 2023
7644e2d
Merge branch 'main' into febo/token-2022
febo Nov 19, 2023
f56fed1
Update kinobi version
febo Nov 19, 2023
c118e65
Add missing token extension
febo Nov 21, 2023
a648103
Add custom error type
febo Nov 21, 2023
5914b03
Add token account tests
febo Nov 21, 2023
6d37ce0
Add ATA non-transferable test
febo Nov 22, 2023
9a6623d
Remove redundant check
febo Nov 22, 2023
8f6f29c
Add close authority check
febo Nov 22, 2023
53bab2f
Fix close authority check
febo Nov 27, 2023
ebf861e
Switch error type
febo Nov 27, 2023
e57914d
Validate token close authority on print
febo Dec 3, 2023
c2b0dd2
Fix clippy
febo Dec 3, 2023
87d8f99
Merge branch 'main' into febo/token-2022
febo Dec 3, 2023
50827e2
Add Token-2022 JS test
febo Dec 3, 2023
3b4a1b4
Add owner assert
febo Dec 3, 2023
a9bcd23
Revert to optional token owner (#70)
febo Dec 8, 2023
3d85a0e
Fix optional token owner
febo Dec 9, 2023
4913061
Fix account type
febo Dec 20, 2023
92123bd
Improve token program test
febo Dec 21, 2023
24cc370
Fix typos
febo Dec 21, 2023
d751b53
Remove large slot offset
febo Dec 21, 2023
16b6bd5
Not applicable comments
febo Dec 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions clients/js/src/generated/errors/mplTokenMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2855,6 +2855,74 @@ nameToErrorMap.set(
CannotChangeUpdateAuthorityWithDelegateError
);

/** InvalidMintExtensionType: Invalid mint extension type */
export class InvalidMintExtensionTypeError extends ProgramError {
override readonly name: string = 'InvalidMintExtensionType';

readonly code: number = 0xc2; // 194

constructor(program: Program, cause?: Error) {
super('Invalid mint extension type', program, cause);
}
}
codeToErrorMap.set(0xc2, InvalidMintExtensionTypeError);
nameToErrorMap.set('InvalidMintExtensionType', InvalidMintExtensionTypeError);

/** InvalidMintCloseAuthority: Invalid mint close authority */
export class InvalidMintCloseAuthorityError extends ProgramError {
override readonly name: string = 'InvalidMintCloseAuthority';

readonly code: number = 0xc3; // 195

constructor(program: Program, cause?: Error) {
super('Invalid mint close authority', program, cause);
}
}
codeToErrorMap.set(0xc3, InvalidMintCloseAuthorityError);
nameToErrorMap.set('InvalidMintCloseAuthority', InvalidMintCloseAuthorityError);

/** InvalidMetadataPointer: Invalid metadata pointer */
export class InvalidMetadataPointerError extends ProgramError {
override readonly name: string = 'InvalidMetadataPointer';

readonly code: number = 0xc4; // 196

constructor(program: Program, cause?: Error) {
super('Invalid metadata pointer', program, cause);
}
}
codeToErrorMap.set(0xc4, InvalidMetadataPointerError);
nameToErrorMap.set('InvalidMetadataPointer', InvalidMetadataPointerError);

/** InvalidTokenExtensionType: Invalid token extension type */
export class InvalidTokenExtensionTypeError extends ProgramError {
override readonly name: string = 'InvalidTokenExtensionType';

readonly code: number = 0xc5; // 197

constructor(program: Program, cause?: Error) {
super('Invalid token extension type', program, cause);
}
}
codeToErrorMap.set(0xc5, InvalidTokenExtensionTypeError);
nameToErrorMap.set('InvalidTokenExtensionType', InvalidTokenExtensionTypeError);

/** MissingImmutableOwnerExtension: Missing immutable owner extension */
export class MissingImmutableOwnerExtensionError extends ProgramError {
override readonly name: string = 'MissingImmutableOwnerExtension';

readonly code: number = 0xc6; // 198

constructor(program: Program, cause?: Error) {
super('Missing immutable owner extension', program, cause);
}
}
codeToErrorMap.set(0xc6, MissingImmutableOwnerExtensionError);
nameToErrorMap.set(
'MissingImmutableOwnerExtension',
MissingImmutableOwnerExtensionError
);

/**
* Attempts to resolve a custom program error from the provided error code.
* @category Errors
Expand Down
21 changes: 16 additions & 5 deletions clients/js/src/generated/instructions/createV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
resolveCreators,
resolveDecimals,
resolveIsNonFungible,
resolveIsNonFungibleOrIsMintSigner,
resolvePrintSupply,
} from '../../hooked';
import { findMasterEditionPda, findMetadataPda } from '../accounts';
Expand Down Expand Up @@ -297,11 +298,21 @@ export function createV1(
);
}
if (!resolvedAccounts.splTokenProgram.value) {
resolvedAccounts.splTokenProgram.value = context.programs.getPublicKey(
'splToken',
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
);
resolvedAccounts.splTokenProgram.isWritable = false;
if (
resolveIsNonFungibleOrIsMintSigner(
context,
resolvedAccounts,
resolvedArgs,
programId,
false
)
) {
resolvedAccounts.splTokenProgram.value = context.programs.getPublicKey(
'splToken',
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
);
resolvedAccounts.splTokenProgram.isWritable = false;
}
}
if (!resolvedArgs.isCollection) {
resolvedArgs.isCollection = false;
Expand Down
10 changes: 10 additions & 0 deletions clients/js/src/hooked/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ACCOUNT_HEADER_SIZE,
Context,
Option,
isSigner,
none,
some,
} from '@metaplex-foundation/umi';
Expand Down Expand Up @@ -90,3 +91,12 @@ export const resolveOptionalTokenOwner = (
accounts.token.value
? { value: null }
: { value: context.identity.publicKey };

export const resolveIsNonFungibleOrIsMintSigner = (
context: any,
accounts: ResolvedAccountsWithIndices,
args: { tokenStandard?: TokenStandard },
...rest: any[]
): boolean =>
isNonFungible(expectSome(args.tokenStandard)) ||
isSigner(expectSome(accounts.mint.value));
8 changes: 7 additions & 1 deletion clients/js/test/_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {
generateSigner,
percentAmount,
publicKey,
PublicKey,
Signer,
transactionBuilder,
Expand Down Expand Up @@ -50,6 +51,10 @@ export const FUNGIBLE_TOKEN_STANDARDS: TokenStandardKeys[] = [
'Fungible',
];

export const SPL_TOKEN_2022_PROGRAM_ID: PublicKey = publicKey(
'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
);

export const createUmi = async () =>
(await baseCreateUmi()).use(mplTokenMetadata());

Expand Down Expand Up @@ -92,7 +97,7 @@ export const createDigitalAssetWithToken = async (
authority: input.authority,
mint: mint.publicKey,
token: input.token,
tokenOwner: input.tokenOwner,
tokenOwner: input.tokenOwner ?? umi.identity.publicKey,
amount: input.amount ?? 1,
tokenStandard: input.tokenStandard ?? TokenStandard.NonFungible,
})
Expand Down Expand Up @@ -127,6 +132,7 @@ export const createDigitalAssetWithVerifiedCreators = async (
mintV1(umi, {
authority: input.authority,
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 1,
tokenStandard: TokenStandard.NonFungible,
})
Expand Down
2 changes: 2 additions & 0 deletions clients/js/test/createHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ test('it can create a new NonFungible', async (t) => {
// When we create a new NonFungible at this address.
await createNft(umi, {
mint,
tokenOwner: umi.identity.publicKey,
name: 'My NFT',
uri: 'https://example.com/my-nft.json',
sellerFeeBasisPoints: percentAmount(5.5),
Expand Down Expand Up @@ -86,6 +87,7 @@ test('it can create a new ProgrammableNonFungible', async (t) => {
// When we create a new ProgrammableNonFungible at this address.
await createProgrammableNft(umi, {
mint,
tokenOwner: umi.identity.publicKey,
name: 'My Programmable NFT',
uri: 'https://example.com/my-programmable-nft.json',
sellerFeeBasisPoints: percentAmount(5.5),
Expand Down
67 changes: 66 additions & 1 deletion clients/js/test/createV1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
programmableConfig,
TokenStandard,
} from '../src';
import { createUmi } from './_setup';
import { createUmi, SPL_TOKEN_2022_PROGRAM_ID } from './_setup';

test('it can create a new NonFungible', async (t) => {
// Given a new mint Signer.
Expand Down Expand Up @@ -351,3 +351,68 @@ test('an explicit payer can be used for storage fees', async (t) => {
const payerBalance = await umi.rpc.getBalance(payer.publicKey);
t.deepEqual(payerBalance, subtractAmounts(sol(10), totalFees));
});

test('it can create a new ProgrammableNonFungible with Token-2022', async (t) => {
// Given a new mint Signer.
const umi = await createUmi();
const mint = generateSigner(umi);

// When we create a new ProgrammableNonFungible at this address.
await createV1(umi, {
mint,
name: 'My Programmable NFT',
uri: 'https://example.com/my-programmable-nft.json',
sellerFeeBasisPoints: percentAmount(5.5),
splTokenProgram: SPL_TOKEN_2022_PROGRAM_ID,
tokenStandard: TokenStandard.ProgrammableNonFungible,
}).sendAndConfirm(umi);

// Then a Mint account was created with zero supply.
const mintAccount = await fetchMint(umi, mint.publicKey);
const masterEdition = findMasterEditionPda(umi, { mint: mint.publicKey });
t.like(mintAccount, <Mint>{
publicKey: publicKey(mint),
supply: 0n,
decimals: 0,
mintAuthority: some(publicKey(masterEdition)),
freezeAuthority: some(publicKey(masterEdition)),
});

// And a Metadata account was created.
const metadata = findMetadataPda(umi, { mint: mint.publicKey });
const metadataAccount = await fetchMetadata(umi, metadata);
t.like(metadataAccount, <Metadata>{
publicKey: publicKey(metadata),
updateAuthority: publicKey(umi.identity),
mint: publicKey(mint),
tokenStandard: some(TokenStandard.ProgrammableNonFungible),
name: 'My Programmable NFT',
uri: 'https://example.com/my-programmable-nft.json',
sellerFeeBasisPoints: 550,
primarySaleHappened: false,
isMutable: true,
creators: some([
{ address: publicKey(umi.identity), verified: true, share: 100 },
]),
collection: none(),
uses: none(),
collectionDetails: none(),
programmableConfig: some(programmableConfig('V1', { ruleSet: none() })),
});

// And a MasterEdition account was created.
const masterEditionAccount = await fetchMasterEdition(umi, masterEdition);
t.like(masterEditionAccount, <MasterEdition>{
publicKey: publicKey(masterEdition),
supply: 0n,
maxSupply: some(0n),
});

// And the SPL Token-2022 Program is the owner of the mint account.
const account = await umi.rpc.getAccount(mint.publicKey);
t.true(account.exists);

if (account.exists) {
t.is(account.owner, SPL_TOKEN_2022_PROGRAM_ID);
}
});
1 change: 1 addition & 0 deletions clients/js/test/digitalAssetWithToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ test('it can fetch all DigitalAssetWithToken by owner and mint', async (t) => {
mintV1(umi, {
mint: mintA1.publicKey,
token: regularToken.publicKey,
tokenOwner: ownerA,
tokenStandard: TokenStandard.FungibleAsset,
amount: 15,
})
Expand Down
6 changes: 6 additions & 0 deletions clients/js/test/mintV1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ test('it can mint only one token after a NonFungible is created', async (t) => {
// When we mint one token.
await mintV1(umi, {
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 1,
tokenStandard: TokenStandard.NonFungible,
}).sendAndConfirm(umi);
Expand All @@ -45,6 +46,7 @@ test('it can mint only one token after a NonFungible is created', async (t) => {
// But when we try to mint another token.
const promise = mintV1(umi, {
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 1,
tokenStandard: TokenStandard.NonFungible,
}).sendAndConfirm(umi);
Expand All @@ -68,6 +70,7 @@ test('it can mint only one token after a ProgrammableNonFungible is created', as
// When we mint one token.
await mintV1(umi, {
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 1,
tokenStandard: TokenStandard.ProgrammableNonFungible,
}).sendAndConfirm(umi);
Expand All @@ -85,6 +88,7 @@ test('it can mint only one token after a ProgrammableNonFungible is created', as
// But when we try to mint another token.
const promise = mintV1(umi, {
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 1,
tokenStandard: TokenStandard.ProgrammableNonFungible,
}).sendAndConfirm(umi);
Expand All @@ -108,6 +112,7 @@ test('it can mint multiple tokens after a Fungible is created', async (t) => {
// When we mint 42 token.
await mintV1(umi, {
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 42,
tokenStandard: TokenStandard.Fungible,
}).sendAndConfirm(umi);
Expand Down Expand Up @@ -138,6 +143,7 @@ test('it can mint multiple tokens after a FungibleAsset is created', async (t) =
// When we mint 42 token.
await mintV1(umi, {
mint: mint.publicKey,
tokenOwner: umi.identity.publicKey,
amount: 42,
tokenStandard: TokenStandard.FungibleAsset,
}).sendAndConfirm(umi);
Expand Down
Loading
Loading