diff --git a/.gitignore b/.gitignore index 0b16cba..36fd4ac 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ dist .bin JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa.json jsonz56LVLsegzJHJE5ysRC1qdeakiGFtB5ys8EWyJC.json +inscr9svLET8FFoaFi3yPt8xF7fKsHLQWC5a4jRMFpb.json +1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo.json diff --git a/README.md b/README.md index 1765021..9ec2a6a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A simple program for inscribing bytes on chain. This project contains the following programs: -- [Mpl Inscription](./programs/mpl-inscription/README.md) `JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa` +- [Mpl Inscription](./programs/mpl-inscription/README.md) `1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo` You will need a Rust version compatible with BPF to compile the program, currently we recommend using Rust 1.68.0. diff --git a/clients/js/src/generated/accounts/inscriptionMetadata.ts b/clients/js/src/generated/accounts/inscriptionMetadata.ts index 862d1f5..844b559 100644 --- a/clients/js/src/generated/accounts/inscriptionMetadata.ts +++ b/clients/js/src/generated/accounts/inscriptionMetadata.ts @@ -31,21 +31,32 @@ import { u64, u8, } from '@metaplex-foundation/umi/serializers'; -import { Key, KeyArgs, getKeySerializer } from '../types'; +import { + InscriptionState, + InscriptionStateArgs, + Key, + KeyArgs, + getInscriptionStateSerializer, + getKeySerializer, +} from '../types'; export type InscriptionMetadata = Account; export type InscriptionMetadataAccountData = { key: Key; bump: number; + state: InscriptionState; inscriptionNumber: Option; + inscriptionBump: Option; updateAuthorities: Array; }; export type InscriptionMetadataAccountDataArgs = { key: KeyArgs; bump: number; + state: InscriptionStateArgs; inscriptionNumber: OptionOrNullable; + inscriptionBump: OptionOrNullable; updateAuthorities: Array; }; @@ -57,7 +68,9 @@ export function getInscriptionMetadataAccountDataSerializer(): Serializer< [ ['key', getKeySerializer()], ['bump', u8()], + ['state', getInscriptionStateSerializer()], ['inscriptionNumber', option(u64())], + ['inscriptionBump', option(u8())], ['updateAuthorities', array(publicKeySerializer())], ], { description: 'InscriptionMetadataAccountData' } @@ -139,18 +152,22 @@ export function getInscriptionMetadataGpaBuilder( ) { const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); return gpaBuilder(context, programId) .registerFields<{ key: KeyArgs; bump: number; + state: InscriptionStateArgs; inscriptionNumber: OptionOrNullable; + inscriptionBump: OptionOrNullable; updateAuthorities: Array; }>({ key: [0, getKeySerializer()], bump: [1, u8()], - inscriptionNumber: [2, option(u64())], + state: [2, getInscriptionStateSerializer()], + inscriptionNumber: [3, option(u64())], + inscriptionBump: [null, option(u8())], updateAuthorities: [null, array(publicKeySerializer())], }) .deserializeUsing((account) => @@ -167,7 +184,7 @@ export function findInscriptionMetadataPda( ): Pda { const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); return context.eddsa.findPda(programId, [ string({ size: 'variable' }).serialize('Inscription'), diff --git a/clients/js/src/generated/errors/mplInscription.ts b/clients/js/src/generated/errors/mplInscription.ts index 79ae97b..3fbc4d5 100644 --- a/clients/js/src/generated/errors/mplInscription.ts +++ b/clients/js/src/generated/errors/mplInscription.ts @@ -93,11 +93,24 @@ export class BorshSerializeErrorError extends ProgramError { codeToErrorMap.set(0x5, BorshSerializeErrorError); nameToErrorMap.set('BorshSerializeError', BorshSerializeErrorError); +/** BorshDeserializeError: Borsh failed to deserialize this account. */ +export class BorshDeserializeErrorError extends ProgramError { + readonly name: string = 'BorshDeserializeError'; + + readonly code: number = 0x6; // 6 + + constructor(program: Program, cause?: Error) { + super('Borsh failed to deserialize this account.', program, cause); + } +} +codeToErrorMap.set(0x6, BorshDeserializeErrorError); +nameToErrorMap.set('BorshDeserializeError', BorshDeserializeErrorError); + /** InvalidAuthority: The payer does not have authority to perform this action. */ export class InvalidAuthorityError extends ProgramError { readonly name: string = 'InvalidAuthority'; - readonly code: number = 0x6; // 6 + readonly code: number = 0x7; // 7 constructor(program: Program, cause?: Error) { super( @@ -107,22 +120,74 @@ export class InvalidAuthorityError extends ProgramError { ); } } -codeToErrorMap.set(0x6, InvalidAuthorityError); +codeToErrorMap.set(0x7, InvalidAuthorityError); nameToErrorMap.set('InvalidAuthority', InvalidAuthorityError); /** NumericalOverflow: Numerical Overflow */ export class NumericalOverflowError extends ProgramError { readonly name: string = 'NumericalOverflow'; - readonly code: number = 0x7; // 7 + readonly code: number = 0x8; // 8 constructor(program: Program, cause?: Error) { super('Numerical Overflow', program, cause); } } -codeToErrorMap.set(0x7, NumericalOverflowError); +codeToErrorMap.set(0x8, NumericalOverflowError); nameToErrorMap.set('NumericalOverflow', NumericalOverflowError); +/** IncorrectOwner: Incorrect Owner */ +export class IncorrectOwnerError extends ProgramError { + readonly name: string = 'IncorrectOwner'; + + readonly code: number = 0x9; // 9 + + constructor(program: Program, cause?: Error) { + super('Incorrect Owner', program, cause); + } +} +codeToErrorMap.set(0x9, IncorrectOwnerError); +nameToErrorMap.set('IncorrectOwner', IncorrectOwnerError); + +/** MintMismatch: Mint Mismatch between Metadata and Mint Accounts. */ +export class MintMismatchError extends ProgramError { + readonly name: string = 'MintMismatch'; + + readonly code: number = 0xa; // 10 + + constructor(program: Program, cause?: Error) { + super('Mint Mismatch between Metadata and Mint Accounts.', program, cause); + } +} +codeToErrorMap.set(0xa, MintMismatchError); +nameToErrorMap.set('MintMismatch', MintMismatchError); + +/** InvalidTokenStandard: Must be a NonFungible Token */ +export class InvalidTokenStandardError extends ProgramError { + readonly name: string = 'InvalidTokenStandard'; + + readonly code: number = 0xb; // 11 + + constructor(program: Program, cause?: Error) { + super('Must be a NonFungible Token', program, cause); + } +} +codeToErrorMap.set(0xb, InvalidTokenStandardError); +nameToErrorMap.set('InvalidTokenStandard', InvalidTokenStandardError); + +/** NotEnoughTokens: Not enough tokens in the provided token account. */ +export class NotEnoughTokensError extends ProgramError { + readonly name: string = 'NotEnoughTokens'; + + readonly code: number = 0xc; // 12 + + constructor(program: Program, cause?: Error) { + super('Not enough tokens in the provided token account.', program, cause); + } +} +codeToErrorMap.set(0xc, NotEnoughTokensError); +nameToErrorMap.set('NotEnoughTokens', NotEnoughTokensError); + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/clients/js/src/generated/instructions/addAuthority.ts b/clients/js/src/generated/instructions/addAuthority.ts index b77ba70..3b5e15b 100644 --- a/clients/js/src/generated/instructions/addAuthority.ts +++ b/clients/js/src/generated/instructions/addAuthority.ts @@ -61,7 +61,7 @@ export function getAddAuthorityInstructionDataSerializer(): Serializer< ], { description: 'AddAuthorityInstructionData' } ), - (value) => ({ ...value, discriminator: 4 }) + (value) => ({ ...value, discriminator: 5 }) ) as Serializer; } @@ -76,7 +76,7 @@ export function addAuthority( // Program ID. const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); // Accounts. diff --git a/clients/js/src/generated/instructions/clearData.ts b/clients/js/src/generated/instructions/clearData.ts index fbba7e1..ed9e788 100644 --- a/clients/js/src/generated/instructions/clearData.ts +++ b/clients/js/src/generated/instructions/clearData.ts @@ -55,7 +55,7 @@ export function getClearDataInstructionDataSerializer(): Serializer< struct([['discriminator', u8()]], { description: 'ClearDataInstructionData', }), - (value) => ({ ...value, discriminator: 3 }) + (value) => ({ ...value, discriminator: 4 }) ) as Serializer; } @@ -67,7 +67,7 @@ export function clearData( // Program ID. const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); // Accounts. diff --git a/clients/js/src/generated/instructions/close.ts b/clients/js/src/generated/instructions/close.ts index cd770a2..d9b2821 100644 --- a/clients/js/src/generated/instructions/close.ts +++ b/clients/js/src/generated/instructions/close.ts @@ -51,7 +51,7 @@ export function getCloseInstructionDataSerializer(): Serializer< struct([['discriminator', u8()]], { description: 'CloseInstructionData', }), - (value) => ({ ...value, discriminator: 1 }) + (value) => ({ ...value, discriminator: 2 }) ) as Serializer; } @@ -63,7 +63,7 @@ export function close( // Program ID. const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); // Accounts. diff --git a/clients/js/src/generated/instructions/index.ts b/clients/js/src/generated/instructions/index.ts index 3ebb36b..0e02cd4 100644 --- a/clients/js/src/generated/instructions/index.ts +++ b/clients/js/src/generated/instructions/index.ts @@ -10,5 +10,6 @@ export * from './addAuthority'; export * from './clearData'; export * from './close'; export * from './initialize'; +export * from './initializeFromMint'; export * from './removeAuthority'; export * from './writeData'; diff --git a/clients/js/src/generated/instructions/initialize.ts b/clients/js/src/generated/instructions/initialize.ts index fcc99a0..26ec8c9 100644 --- a/clients/js/src/generated/instructions/initialize.ts +++ b/clients/js/src/generated/instructions/initialize.ts @@ -67,7 +67,7 @@ export function initialize( // Program ID. const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); // Accounts. diff --git a/clients/js/src/generated/instructions/initializeFromMint.ts b/clients/js/src/generated/instructions/initializeFromMint.ts new file mode 100644 index 0000000..f062a88 --- /dev/null +++ b/clients/js/src/generated/instructions/initializeFromMint.ts @@ -0,0 +1,150 @@ +/** + * 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 { + Context, + Pda, + PublicKey, + Signer, + TransactionBuilder, + transactionBuilder, +} from '@metaplex-foundation/umi'; +import { + Serializer, + mapSerializer, + struct, + u8, +} from '@metaplex-foundation/umi/serializers'; +import { + ResolvedAccount, + ResolvedAccountsWithIndices, + getAccountMetasAndSigners, +} from '../shared'; + +// Accounts. +export type InitializeFromMintInstructionAccounts = { + /** The account to store the metadata in. */ + inscriptionAccount: PublicKey | Pda; + /** The account to store the inscription account's metadata in. */ + metadataAccount: PublicKey | Pda; + /** The mint that will be used to derive the PDA. */ + mintAccount: PublicKey | Pda; + /** The metadata for the mint. */ + tokenMetadataAccount: PublicKey | Pda; + /** The token account for the mint. */ + tokenAccount: PublicKey | Pda; + /** The account that will pay for the transaction and rent. */ + payer?: Signer; + /** System program */ + systemProgram?: PublicKey | Pda; +}; + +// Data. +export type InitializeFromMintInstructionData = { discriminator: number }; + +export type InitializeFromMintInstructionDataArgs = {}; + +export function getInitializeFromMintInstructionDataSerializer(): Serializer< + InitializeFromMintInstructionDataArgs, + InitializeFromMintInstructionData +> { + return mapSerializer< + InitializeFromMintInstructionDataArgs, + any, + InitializeFromMintInstructionData + >( + struct([['discriminator', u8()]], { + description: 'InitializeFromMintInstructionData', + }), + (value) => ({ ...value, discriminator: 1 }) + ) as Serializer< + InitializeFromMintInstructionDataArgs, + InitializeFromMintInstructionData + >; +} + +// Instruction. +export function initializeFromMint( + context: Pick, + input: InitializeFromMintInstructionAccounts +): TransactionBuilder { + // Program ID. + const programId = context.programs.getPublicKey( + 'mplInscription', + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' + ); + + // Accounts. + const resolvedAccounts: ResolvedAccountsWithIndices = { + inscriptionAccount: { + index: 0, + isWritable: true, + value: input.inscriptionAccount ?? null, + }, + metadataAccount: { + index: 1, + isWritable: true, + value: input.metadataAccount ?? null, + }, + mintAccount: { + index: 2, + isWritable: false, + value: input.mintAccount ?? null, + }, + tokenMetadataAccount: { + index: 3, + isWritable: false, + value: input.tokenMetadataAccount ?? null, + }, + tokenAccount: { + index: 4, + isWritable: false, + value: input.tokenAccount ?? null, + }, + payer: { index: 5, isWritable: true, value: input.payer ?? null }, + systemProgram: { + index: 6, + isWritable: false, + value: input.systemProgram ?? null, + }, + }; + + // Default values. + if (!resolvedAccounts.payer.value) { + resolvedAccounts.payer.value = context.payer; + } + if (!resolvedAccounts.systemProgram.value) { + resolvedAccounts.systemProgram.value = context.programs.getPublicKey( + 'splSystem', + '11111111111111111111111111111111' + ); + resolvedAccounts.systemProgram.isWritable = false; + } + + // Accounts in order. + const orderedAccounts: ResolvedAccount[] = Object.values( + resolvedAccounts + ).sort((a, b) => a.index - b.index); + + // Keys and Signers. + const [keys, signers] = getAccountMetasAndSigners( + orderedAccounts, + 'programId', + programId + ); + + // Data. + const data = getInitializeFromMintInstructionDataSerializer().serialize({}); + + // Bytes Created On Chain. + const bytesCreatedOnChain = 0; + + return transactionBuilder([ + { instruction: { keys, programId, data }, signers, bytesCreatedOnChain }, + ]); +} diff --git a/clients/js/src/generated/instructions/removeAuthority.ts b/clients/js/src/generated/instructions/removeAuthority.ts index e78a6f5..a005593 100644 --- a/clients/js/src/generated/instructions/removeAuthority.ts +++ b/clients/js/src/generated/instructions/removeAuthority.ts @@ -53,7 +53,7 @@ export function getRemoveAuthorityInstructionDataSerializer(): Serializer< struct([['discriminator', u8()]], { description: 'RemoveAuthorityInstructionData', }), - (value) => ({ ...value, discriminator: 5 }) + (value) => ({ ...value, discriminator: 6 }) ) as Serializer< RemoveAuthorityInstructionDataArgs, RemoveAuthorityInstructionData @@ -68,7 +68,7 @@ export function removeAuthority( // Program ID. const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); // Accounts. diff --git a/clients/js/src/generated/instructions/writeData.ts b/clients/js/src/generated/instructions/writeData.ts index 1914257..688be14 100644 --- a/clients/js/src/generated/instructions/writeData.ts +++ b/clients/js/src/generated/instructions/writeData.ts @@ -64,7 +64,7 @@ export function getWriteDataInstructionDataSerializer(): Serializer< ], { description: 'WriteDataInstructionData' } ), - (value) => ({ ...value, discriminator: 2 }) + (value) => ({ ...value, discriminator: 3 }) ) as Serializer; } @@ -79,7 +79,7 @@ export function writeData( // Program ID. const programId = context.programs.getPublicKey( 'mplInscription', - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' ); // Accounts. diff --git a/clients/js/src/generated/programs/mplInscription.ts b/clients/js/src/generated/programs/mplInscription.ts index 21cc0ac..85ec02d 100644 --- a/clients/js/src/generated/programs/mplInscription.ts +++ b/clients/js/src/generated/programs/mplInscription.ts @@ -18,7 +18,7 @@ import { } from '../errors'; export const MPL_INSCRIPTION_PROGRAM_ID = - 'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa' as PublicKey<'JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa'>; + '1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo' as PublicKey<'1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo'>; export function createMplInscriptionProgram(): Program { return { diff --git a/clients/js/src/generated/types/dataType.ts b/clients/js/src/generated/types/dataType.ts new file mode 100644 index 0000000..3e46d2a --- /dev/null +++ b/clients/js/src/generated/types/dataType.ts @@ -0,0 +1,23 @@ +/** + * 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 DataType { + Binary, + Json, + Png, +} + +export type DataTypeArgs = DataType; + +export function getDataTypeSerializer(): Serializer { + return scalarEnum(DataType, { + description: 'DataType', + }) as Serializer; +} diff --git a/clients/js/src/generated/types/index.ts b/clients/js/src/generated/types/index.ts index 118e6a3..7917e8b 100644 --- a/clients/js/src/generated/types/index.ts +++ b/clients/js/src/generated/types/index.ts @@ -6,4 +6,6 @@ * @see https://github.com/metaplex-foundation/kinobi */ +export * from './dataType'; +export * from './inscriptionState'; export * from './key'; diff --git a/clients/js/src/generated/types/inscriptionState.ts b/clients/js/src/generated/types/inscriptionState.ts new file mode 100644 index 0000000..20a18f6 --- /dev/null +++ b/clients/js/src/generated/types/inscriptionState.ts @@ -0,0 +1,26 @@ +/** + * 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 InscriptionState { + Raw, + Validated, + Curated, +} + +export type InscriptionStateArgs = InscriptionState; + +export function getInscriptionStateSerializer(): Serializer< + InscriptionStateArgs, + InscriptionState +> { + return scalarEnum(InscriptionState, { + description: 'InscriptionState', + }) as Serializer; +} diff --git a/clients/js/src/generated/types/key.ts b/clients/js/src/generated/types/key.ts index eb7b3cd..df4c05c 100644 --- a/clients/js/src/generated/types/key.ts +++ b/clients/js/src/generated/types/key.ts @@ -11,6 +11,7 @@ import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; export enum Key { Uninitialized, InscriptionMetadataAccount, + MintInscriptionMetadataAccount, } export type KeyArgs = Key; diff --git a/clients/rust/src/generated/accounts/inscription_metadata.rs b/clients/rust/src/generated/accounts/inscription_metadata.rs index 6989921..bf459d1 100644 --- a/clients/rust/src/generated/accounts/inscription_metadata.rs +++ b/clients/rust/src/generated/accounts/inscription_metadata.rs @@ -5,6 +5,7 @@ //! [https://github.com/metaplex-foundation/kinobi] //! +use crate::generated::types::InscriptionState; use crate::generated::types::Key; use borsh::BorshDeserialize; use borsh::BorshSerialize; @@ -15,7 +16,9 @@ use solana_program::pubkey::Pubkey; pub struct InscriptionMetadata { pub key: Key, pub bump: u8, + pub state: InscriptionState, pub inscription_number: Option, + pub inscription_bump: Option, pub update_authorities: Vec, } diff --git a/clients/rust/src/generated/errors/mpl_inscription.rs b/clients/rust/src/generated/errors/mpl_inscription.rs index fd4256c..6967bdd 100644 --- a/clients/rust/src/generated/errors/mpl_inscription.rs +++ b/clients/rust/src/generated/errors/mpl_inscription.rs @@ -28,12 +28,27 @@ pub enum MplInscriptionError { /// 5 (0x5) - Borsh failed to serialize this account. #[error("Borsh failed to serialize this account.")] BorshSerializeError, - /// 6 (0x6) - The payer does not have authority to perform this action. + /// 6 (0x6) - Borsh failed to deserialize this account. + #[error("Borsh failed to deserialize this account.")] + BorshDeserializeError, + /// 7 (0x7) - The payer does not have authority to perform this action. #[error("The payer does not have authority to perform this action.")] InvalidAuthority, - /// 7 (0x7) - Numerical Overflow + /// 8 (0x8) - Numerical Overflow #[error("Numerical Overflow")] NumericalOverflow, + /// 9 (0x9) - Incorrect Owner + #[error("Incorrect Owner")] + IncorrectOwner, + /// 10 (0xA) - Mint Mismatch between Metadata and Mint Accounts. + #[error("Mint Mismatch between Metadata and Mint Accounts.")] + MintMismatch, + /// 11 (0xB) - Must be a NonFungible Token + #[error("Must be a NonFungible Token")] + InvalidTokenStandard, + /// 12 (0xC) - Not enough tokens in the provided token account. + #[error("Not enough tokens in the provided token account.")] + NotEnoughTokens, } impl solana_program::program_error::PrintProgramError for MplInscriptionError { diff --git a/clients/rust/src/generated/instructions/add_authority.rs b/clients/rust/src/generated/instructions/add_authority.rs index e0466fa..5053e07 100644 --- a/clients/rust/src/generated/instructions/add_authority.rs +++ b/clients/rust/src/generated/instructions/add_authority.rs @@ -64,7 +64,7 @@ struct AddAuthorityInstructionData { impl AddAuthorityInstructionData { fn new() -> Self { - Self { discriminator: 4 } + Self { discriminator: 5 } } } diff --git a/clients/rust/src/generated/instructions/clear_data.rs b/clients/rust/src/generated/instructions/clear_data.rs index cc7d048..a322e72 100644 --- a/clients/rust/src/generated/instructions/clear_data.rs +++ b/clients/rust/src/generated/instructions/clear_data.rs @@ -63,7 +63,7 @@ struct ClearDataInstructionData { impl ClearDataInstructionData { fn new() -> Self { - Self { discriminator: 3 } + Self { discriminator: 4 } } } diff --git a/clients/rust/src/generated/instructions/close.rs b/clients/rust/src/generated/instructions/close.rs index 387faec..2286580 100644 --- a/clients/rust/src/generated/instructions/close.rs +++ b/clients/rust/src/generated/instructions/close.rs @@ -63,7 +63,7 @@ struct CloseInstructionData { impl CloseInstructionData { fn new() -> Self { - Self { discriminator: 1 } + Self { discriminator: 2 } } } diff --git a/clients/rust/src/generated/instructions/initialize_from_mint.rs b/clients/rust/src/generated/instructions/initialize_from_mint.rs new file mode 100644 index 0000000..21d6664 --- /dev/null +++ b/clients/rust/src/generated/instructions/initialize_from_mint.rs @@ -0,0 +1,530 @@ +//! 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. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +/// Accounts. +pub struct InitializeFromMint { + /// The account to store the metadata in. + pub inscription_account: solana_program::pubkey::Pubkey, + /// The account to store the inscription account's metadata in. + pub metadata_account: solana_program::pubkey::Pubkey, + /// The mint that will be used to derive the PDA. + pub mint_account: solana_program::pubkey::Pubkey, + /// The metadata for the mint. + pub token_metadata_account: solana_program::pubkey::Pubkey, + /// The token account for the mint. + pub token_account: solana_program::pubkey::Pubkey, + /// The account that will pay for the transaction and rent. + pub payer: solana_program::pubkey::Pubkey, + /// System program + pub system_program: solana_program::pubkey::Pubkey, +} + +impl InitializeFromMint { + pub fn instruction(&self) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(&[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(7 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new( + self.inscription_account, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.metadata_account, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.mint_account, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.token_metadata_account, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.token_account, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.payer, true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let data = InitializeFromMintInstructionData::new() + .try_to_vec() + .unwrap(); + + solana_program::instruction::Instruction { + program_id: crate::MPL_INSCRIPTION_ID, + accounts, + data, + } + } +} + +#[derive(BorshDeserialize, BorshSerialize)] +struct InitializeFromMintInstructionData { + discriminator: u8, +} + +impl InitializeFromMintInstructionData { + fn new() -> Self { + Self { discriminator: 1 } + } +} + +/// Instruction builder. +#[derive(Default)] +pub struct InitializeFromMintBuilder { + inscription_account: Option, + metadata_account: Option, + mint_account: Option, + token_metadata_account: Option, + token_account: Option, + payer: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +impl InitializeFromMintBuilder { + pub fn new() -> Self { + Self::default() + } + /// The account to store the metadata in. + #[inline(always)] + pub fn inscription_account( + &mut self, + inscription_account: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.inscription_account = Some(inscription_account); + self + } + /// The account to store the inscription account's metadata in. + #[inline(always)] + pub fn metadata_account( + &mut self, + metadata_account: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.metadata_account = Some(metadata_account); + self + } + /// The mint that will be used to derive the PDA. + #[inline(always)] + pub fn mint_account(&mut self, mint_account: solana_program::pubkey::Pubkey) -> &mut Self { + self.mint_account = Some(mint_account); + self + } + /// The metadata for the mint. + #[inline(always)] + pub fn token_metadata_account( + &mut self, + token_metadata_account: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.token_metadata_account = Some(token_metadata_account); + self + } + /// The token account for the mint. + #[inline(always)] + pub fn token_account(&mut self, token_account: solana_program::pubkey::Pubkey) -> &mut Self { + self.token_account = Some(token_account); + self + } + /// The account that will pay for the transaction and rent. + #[inline(always)] + pub fn payer(&mut self, payer: solana_program::pubkey::Pubkey) -> &mut Self { + self.payer = Some(payer); + self + } + /// `[optional account, default to '11111111111111111111111111111111']` + /// System program + #[inline(always)] + pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self { + self.system_program = Some(system_program); + self + } + /// Add an aditional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: solana_program::instruction::AccountMeta, + ) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_program::instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = InitializeFromMint { + inscription_account: self + .inscription_account + .expect("inscription_account is not set"), + metadata_account: self.metadata_account.expect("metadata_account is not set"), + mint_account: self.mint_account.expect("mint_account is not set"), + token_metadata_account: self + .token_metadata_account + .expect("token_metadata_account is not set"), + token_account: self.token_account.expect("token_account is not set"), + payer: self.payer.expect("payer is not set"), + system_program: self + .system_program + .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), + }; + + accounts.instruction_with_remaining_accounts(&self.__remaining_accounts) + } +} + +/// `initialize_from_mint` CPI accounts. +pub struct InitializeFromMintCpiAccounts<'a, 'b> { + /// The account to store the metadata in. + pub inscription_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The account to store the inscription account's metadata in. + pub metadata_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The mint that will be used to derive the PDA. + pub mint_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The metadata for the mint. + pub token_metadata_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The token account for the mint. + pub token_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The account that will pay for the transaction and rent. + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + /// System program + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `initialize_from_mint` CPI instruction. +pub struct InitializeFromMintCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_program::account_info::AccountInfo<'a>, + /// The account to store the metadata in. + pub inscription_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The account to store the inscription account's metadata in. + pub metadata_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The mint that will be used to derive the PDA. + pub mint_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The metadata for the mint. + pub token_metadata_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The token account for the mint. + pub token_account: &'b solana_program::account_info::AccountInfo<'a>, + /// The account that will pay for the transaction and rent. + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + /// System program + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +impl<'a, 'b> InitializeFromMintCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: InitializeFromMintCpiAccounts<'a, 'b>, + ) -> Self { + Self { + __program: program, + inscription_account: accounts.inscription_account, + metadata_account: accounts.metadata_account, + mint_account: accounts.mint_account, + token_metadata_account: accounts.token_metadata_account, + token_account: accounts.token_account, + payer: accounts.payer, + system_program: accounts.system_program, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + let mut accounts = Vec::with_capacity(7 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.inscription_account.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.metadata_account.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.mint_account.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.token_metadata_account.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.token_account.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.payer.key, + true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.system_program.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_program::instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let data = InitializeFromMintInstructionData::new() + .try_to_vec() + .unwrap(); + + let instruction = solana_program::instruction::Instruction { + program_id: crate::MPL_INSCRIPTION_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(7 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.inscription_account.clone()); + account_infos.push(self.metadata_account.clone()); + account_infos.push(self.mint_account.clone()); + account_infos.push(self.token_metadata_account.clone()); + account_infos.push(self.token_account.clone()); + account_infos.push(self.payer.clone()); + account_infos.push(self.system_program.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_program::program::invoke(&instruction, &account_infos) + } else { + solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// `initialize_from_mint` CPI instruction builder. +pub struct InitializeFromMintCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> InitializeFromMintCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(InitializeFromMintCpiBuilderInstruction { + __program: program, + inscription_account: None, + metadata_account: None, + mint_account: None, + token_metadata_account: None, + token_account: None, + payer: None, + system_program: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// The account to store the metadata in. + #[inline(always)] + pub fn inscription_account( + &mut self, + inscription_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.inscription_account = Some(inscription_account); + self + } + /// The account to store the inscription account's metadata in. + #[inline(always)] + pub fn metadata_account( + &mut self, + metadata_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.metadata_account = Some(metadata_account); + self + } + /// The mint that will be used to derive the PDA. + #[inline(always)] + pub fn mint_account( + &mut self, + mint_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.mint_account = Some(mint_account); + self + } + /// The metadata for the mint. + #[inline(always)] + pub fn token_metadata_account( + &mut self, + token_metadata_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_metadata_account = Some(token_metadata_account); + self + } + /// The token account for the mint. + #[inline(always)] + pub fn token_account( + &mut self, + token_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_account = Some(token_account); + self + } + /// The account that will pay for the transaction and rent. + #[inline(always)] + pub fn payer(&mut self, payer: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + self + } + /// System program + #[inline(always)] + pub fn system_program( + &mut self, + system_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.system_program = Some(system_program); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_program::account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + let instruction = InitializeFromMintCpi { + __program: self.instruction.__program, + + inscription_account: self + .instruction + .inscription_account + .expect("inscription_account is not set"), + + metadata_account: self + .instruction + .metadata_account + .expect("metadata_account is not set"), + + mint_account: self + .instruction + .mint_account + .expect("mint_account is not set"), + + token_metadata_account: self + .instruction + .token_metadata_account + .expect("token_metadata_account is not set"), + + token_account: self + .instruction + .token_account + .expect("token_account is not set"), + + payer: self.instruction.payer.expect("payer is not set"), + + system_program: self + .instruction + .system_program + .expect("system_program is not set"), + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +struct InitializeFromMintCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + inscription_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + metadata_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + mint_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + token_metadata_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )>, +} diff --git a/clients/rust/src/generated/instructions/mod.rs b/clients/rust/src/generated/instructions/mod.rs index 875fd1e..baa0b07 100644 --- a/clients/rust/src/generated/instructions/mod.rs +++ b/clients/rust/src/generated/instructions/mod.rs @@ -9,6 +9,7 @@ pub(crate) mod add_authority; pub(crate) mod clear_data; pub(crate) mod close; pub(crate) mod initialize; +pub(crate) mod initialize_from_mint; pub(crate) mod remove_authority; pub(crate) mod write_data; @@ -16,5 +17,6 @@ pub use self::add_authority::*; pub use self::clear_data::*; pub use self::close::*; pub use self::initialize::*; +pub use self::initialize_from_mint::*; pub use self::remove_authority::*; pub use self::write_data::*; diff --git a/clients/rust/src/generated/instructions/remove_authority.rs b/clients/rust/src/generated/instructions/remove_authority.rs index 99b4367..097e469 100644 --- a/clients/rust/src/generated/instructions/remove_authority.rs +++ b/clients/rust/src/generated/instructions/remove_authority.rs @@ -58,7 +58,7 @@ struct RemoveAuthorityInstructionData { impl RemoveAuthorityInstructionData { fn new() -> Self { - Self { discriminator: 5 } + Self { discriminator: 6 } } } diff --git a/clients/rust/src/generated/instructions/write_data.rs b/clients/rust/src/generated/instructions/write_data.rs index edbe54f..49b17db 100644 --- a/clients/rust/src/generated/instructions/write_data.rs +++ b/clients/rust/src/generated/instructions/write_data.rs @@ -69,7 +69,7 @@ struct WriteDataInstructionData { impl WriteDataInstructionData { fn new() -> Self { - Self { discriminator: 2 } + Self { discriminator: 3 } } } diff --git a/clients/rust/src/generated/programs.rs b/clients/rust/src/generated/programs.rs index eaf252f..0420e61 100644 --- a/clients/rust/src/generated/programs.rs +++ b/clients/rust/src/generated/programs.rs @@ -8,4 +8,4 @@ use solana_program::{pubkey, pubkey::Pubkey}; /// `mpl_inscription` program ID. -pub const MPL_INSCRIPTION_ID: Pubkey = pubkey!("JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa"); +pub const MPL_INSCRIPTION_ID: Pubkey = pubkey!("1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo"); diff --git a/clients/rust/src/generated/types/data_type.rs b/clients/rust/src/generated/types/data_type.rs new file mode 100644 index 0000000..d0be2d7 --- /dev/null +++ b/clients/rust/src/generated/types/data_type.rs @@ -0,0 +1,17 @@ +//! 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. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum DataType { + Binary, + Json, + Png, +} diff --git a/clients/rust/src/generated/types/inscription_state.rs b/clients/rust/src/generated/types/inscription_state.rs new file mode 100644 index 0000000..e79ba1a --- /dev/null +++ b/clients/rust/src/generated/types/inscription_state.rs @@ -0,0 +1,17 @@ +//! 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. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum InscriptionState { + Raw, + Validated, + Curated, +} diff --git a/clients/rust/src/generated/types/key.rs b/clients/rust/src/generated/types/key.rs index 5f07f1f..c2ebffe 100644 --- a/clients/rust/src/generated/types/key.rs +++ b/clients/rust/src/generated/types/key.rs @@ -13,4 +13,5 @@ use borsh::BorshSerialize; pub enum Key { Uninitialized, InscriptionMetadataAccount, + MintInscriptionMetadataAccount, } diff --git a/clients/rust/src/generated/types/mod.rs b/clients/rust/src/generated/types/mod.rs index 32cd979..bf0252b 100644 --- a/clients/rust/src/generated/types/mod.rs +++ b/clients/rust/src/generated/types/mod.rs @@ -5,6 +5,10 @@ //! [https://github.com/metaplex-foundation/kinobi] //! +pub(crate) mod data_type; +pub(crate) mod inscription_state; pub(crate) mod key; +pub use self::data_type::*; +pub use self::inscription_state::*; pub use self::key::*; diff --git a/configs/shank.cjs b/configs/shank.cjs index 8d483de..7b4cc33 100644 --- a/configs/shank.cjs +++ b/configs/shank.cjs @@ -8,7 +8,7 @@ const programDir = path.join(__dirname, "..", "programs"); generateIdl({ generator: "shank", programName: "mpl_inscription", - programId: "JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa", + programId: "1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo", idlDir, binaryInstallDir, programDir: path.join(programDir, "mpl-inscription"), diff --git a/configs/validator.cjs b/configs/validator.cjs index 473d0f1..a6a5198 100755 --- a/configs/validator.cjs +++ b/configs/validator.cjs @@ -12,7 +12,7 @@ module.exports = { programs: [ { label: "Mpl Json", - programId: "JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa", + programId: "1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo", deployPath: getProgram("mpl_inscription.so"), }, // Below are external programs that should be included in the local validator. diff --git a/idls/mpl_inscription.json b/idls/mpl_inscription.json index 86d3370..42da5c1 100644 --- a/idls/mpl_inscription.json +++ b/idls/mpl_inscription.json @@ -45,7 +45,7 @@ } }, { - "name": "Close", + "name": "InitializeFromMint", "accounts": [ { "name": "inscriptionAccount", @@ -63,6 +63,30 @@ "The account to store the inscription account's metadata in." ] }, + { + "name": "mintAccount", + "isMut": false, + "isSigner": false, + "docs": [ + "The mint that will be used to derive the PDA." + ] + }, + { + "name": "tokenMetadataAccount", + "isMut": false, + "isSigner": false, + "docs": [ + "The metadata for the mint." + ] + }, + { + "name": "tokenAccount", + "isMut": false, + "isSigner": false, + "docs": [ + "The token account for the mint." + ] + }, { "name": "payer", "isMut": true, @@ -86,6 +110,48 @@ "value": 1 } }, + { + "name": "Close", + "accounts": [ + { + "name": "inscriptionAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "The account to store the metadata in." + ] + }, + { + "name": "metadataAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "The account to store the inscription account's metadata in." + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "The account that will pay for the transaction and rent." + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 2 + } + }, { "name": "WriteData", "accounts": [ @@ -132,7 +198,7 @@ ], "discriminant": { "type": "u8", - "value": 2 + "value": 3 } }, { @@ -174,7 +240,7 @@ "args": [], "discriminant": { "type": "u8", - "value": 3 + "value": 4 } }, { @@ -215,7 +281,7 @@ ], "discriminant": { "type": "u8", - "value": 4 + "value": 5 } }, { @@ -249,7 +315,7 @@ "args": [], "discriminant": { "type": "u8", - "value": 5 + "value": 6 } } ], @@ -269,12 +335,24 @@ "name": "bump", "type": "u8" }, + { + "name": "state", + "type": { + "defined": "InscriptionState" + } + }, { "name": "inscriptionNumber", "type": { "option": "u64" } }, + { + "name": "inscriptionBump", + "type": { + "option": "u8" + } + }, { "name": "updateAuthorities", "type": { @@ -320,6 +398,43 @@ }, { "name": "InscriptionMetadataAccount" + }, + { + "name": "MintInscriptionMetadataAccount" + } + ] + } + }, + { + "name": "InscriptionState", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Raw" + }, + { + "name": "Validated" + }, + { + "name": "Curated" + } + ] + } + }, + { + "name": "DataType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Binary" + }, + { + "name": "Json" + }, + { + "name": "Png" } ] } @@ -358,18 +473,43 @@ }, { "code": 6, + "name": "BorshDeserializeError", + "msg": "Borsh failed to deserialize this account." + }, + { + "code": 7, "name": "InvalidAuthority", "msg": "The payer does not have authority to perform this action." }, { - "code": 7, + "code": 8, "name": "NumericalOverflow", "msg": "Numerical Overflow" + }, + { + "code": 9, + "name": "IncorrectOwner", + "msg": "Incorrect Owner" + }, + { + "code": 10, + "name": "MintMismatch", + "msg": "Mint Mismatch between Metadata and Mint Accounts." + }, + { + "code": 11, + "name": "InvalidTokenStandard", + "msg": "Must be a NonFungible Token" + }, + { + "code": 12, + "name": "NotEnoughTokens", + "msg": "Not enough tokens in the provided token account." } ], "metadata": { "origin": "shank", - "address": "JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa", + "address": "1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo", "binaryVersion": "0.3.0", "libVersion": "0.3.0" } diff --git a/programs/mpl-inscription/Cargo.lock b/programs/mpl-inscription/Cargo.lock index efccf97..10a31f9 100644 --- a/programs/mpl-inscription/Cargo.lock +++ b/programs/mpl-inscription/Cargo.lock @@ -1039,12 +1039,27 @@ name = "mpl-inscription" version = "0.1.0" dependencies = [ "borsh 0.10.3", + "mpl-token-metadata", "mpl-utils", "num-derive", "num-traits", "serde_json", "shank", "solana-program", + "spl-token 4.0.0", + "thiserror", +] + +[[package]] +name = "mpl-token-metadata" +version = "3.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8ee05284d79b367ae8966d558e1a305a781fc80c9df51f37775169117ba64f" +dependencies = [ + "borsh 0.10.3", + "num-derive", + "num-traits", + "solana-program", "thiserror", ] @@ -1853,6 +1868,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-token" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum 0.6.1", + "solana-program", + "thiserror", +] + [[package]] name = "spl-token-2022" version = "0.6.1" @@ -1867,7 +1897,7 @@ dependencies = [ "solana-program", "solana-zk-token-sdk", "spl-memo", - "spl-token", + "spl-token 3.5.0", "thiserror", ] diff --git a/programs/mpl-inscription/Cargo.toml b/programs/mpl-inscription/Cargo.toml index 09281be..e4bee33 100644 --- a/programs/mpl-inscription/Cargo.toml +++ b/programs/mpl-inscription/Cargo.toml @@ -17,4 +17,6 @@ num-traits = "^0.2" solana-program = "~1.16" thiserror = "^1.0" mpl-utils = "0.3.1" -serde_json = { version = "1.0.108", features = ["std"]} \ No newline at end of file +serde_json = { version = "1.0.108", features = ["std"]} +mpl-token-metadata = "3.2.3" +spl-token = { version = "4.0.0", features = ["no-entrypoint"] } \ No newline at end of file diff --git a/programs/mpl-inscription/src/error.rs b/programs/mpl-inscription/src/error.rs index 3e3f971..df5966a 100644 --- a/programs/mpl-inscription/src/error.rs +++ b/programs/mpl-inscription/src/error.rs @@ -32,13 +32,33 @@ pub enum MplInscriptionError { #[error("Borsh failed to serialize this account.")] BorshSerializeError, - /// 6 - The payer does not have authority to perform this action. + /// 6 - Borsh failed to deserialize this account. + #[error("Borsh failed to deserialize this account.")] + BorshDeserializeError, + + /// 7 - The payer does not have authority to perform this action. #[error("The payer does not have authority to perform this action.")] InvalidAuthority, - /// 7 - Numerical Overflow + /// 8 - Numerical Overflow #[error("Numerical Overflow")] NumericalOverflow, + + /// 9 - Incorrect Owner + #[error("Incorrect Owner")] + IncorrectOwner, + + /// 10 - Mint Mismatch + #[error("Mint Mismatch between Metadata and Mint Accounts.")] + MintMismatch, + + /// 11 - Invalid Token Standard + #[error("Must be a NonFungible Token")] + InvalidTokenStandard, + + /// 12 - Not enough tokens + #[error("Not enough tokens in the provided token account.")] + NotEnoughTokens, } impl PrintProgramError for MplInscriptionError { diff --git a/programs/mpl-inscription/src/instruction/mod.rs b/programs/mpl-inscription/src/instruction/mod.rs index 620f692..0820cf6 100644 --- a/programs/mpl-inscription/src/instruction/mod.rs +++ b/programs/mpl-inscription/src/instruction/mod.rs @@ -12,6 +12,16 @@ pub enum MplInscriptionInstruction { #[account(3, name="system_program", desc = "System program")] Initialize, + /// Initialize the Inscription and Metadata accounts as a Mint PDA + #[account(0, writable, name="inscription_account", desc = "The account to store the metadata in.")] + #[account(1, writable, name="metadata_account", desc = "The account to store the inscription account's metadata in.")] + #[account(2, name="mint_account", desc="The mint that will be used to derive the PDA.")] + #[account(3, name="token_metadata_account", desc="The metadata for the mint.")] + #[account(4, name="token_account", desc="The token account for the mint.")] + #[account(5, writable, signer, name="payer", desc="The account that will pay for the transaction and rent.")] + #[account(6, name="system_program", desc = "System program")] + InitializeFromMint, + /// Close the Inscription and Metadata accounts #[account(0, writable, name="inscription_account", desc = "The account to store the metadata in.")] #[account(1, writable, name="metadata_account", desc = "The account to store the inscription account's metadata in.")] diff --git a/programs/mpl-inscription/src/lib.rs b/programs/mpl-inscription/src/lib.rs index 8a21875..49e2424 100644 --- a/programs/mpl-inscription/src/lib.rs +++ b/programs/mpl-inscription/src/lib.rs @@ -4,4 +4,4 @@ pub(crate) mod instruction; pub(crate) mod processor; pub(crate) mod state; -solana_program::declare_id!("JSoNoHBzUEFnjpZtcNcNzv5KLzo4tD5v4Z1pT9G4jJa"); +solana_program::declare_id!("1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo"); diff --git a/programs/mpl-inscription/src/processor/initialize.rs b/programs/mpl-inscription/src/processor/initialize.rs index f350bfb..0e8e00e 100644 --- a/programs/mpl-inscription/src/processor/initialize.rs +++ b/programs/mpl-inscription/src/processor/initialize.rs @@ -8,7 +8,7 @@ use solana_program::{ use crate::{ error::MplInscriptionError, instruction::accounts::InitializeAccounts, - state::{InscriptionMetadata, Key, INITIAL_SIZE, PREFIX}, + state::{InscriptionMetadata, INITIAL_SIZE, PREFIX}, }; pub(crate) fn process_initialize<'a>(accounts: &'a [AccountInfo<'a>]) -> ProgramResult { @@ -66,10 +66,9 @@ pub(crate) fn process_initialize<'a>(accounts: &'a [AccountInfo<'a>]) -> Program // Initialize the inscription metadata. let inscription_metadata = InscriptionMetadata { - key: Key::InscriptionMetadataAccount, bump, update_authorities: vec![*ctx.accounts.payer.key], - inscription_number: None, + ..InscriptionMetadata::default() }; let serialized_metadata = &inscription_metadata.try_to_vec()?; diff --git a/programs/mpl-inscription/src/processor/initialize_from_mint.rs b/programs/mpl-inscription/src/processor/initialize_from_mint.rs new file mode 100644 index 0000000..0c47288 --- /dev/null +++ b/programs/mpl-inscription/src/processor/initialize_from_mint.rs @@ -0,0 +1,176 @@ +use borsh::BorshSerialize; +use mpl_token_metadata::{accounts::Metadata, types::TokenStandard}; +use mpl_utils::{ + assert_derivation, assert_initialized, assert_owned_by, assert_signer, + create_or_allocate_account_raw, +}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_memory::sol_memcpy, + system_program, +}; + +use crate::{ + error::MplInscriptionError, + instruction::accounts::InitializeFromMintAccounts, + state::{InscriptionMetadata, Key, INITIAL_SIZE, PREFIX}, +}; + +pub(crate) fn process_initialize_from_mint<'a>(accounts: &'a [AccountInfo<'a>]) -> ProgramResult { + let ctx = &InitializeFromMintAccounts::context(accounts)?; + + // Check that the account isn't already initialized. + if (ctx.accounts.inscription_account.owner != &system_program::ID) + || !ctx.accounts.inscription_account.data_is_empty() + { + return Err(MplInscriptionError::AlreadyInitialized.into()); + } + + // Check that the account isn't already initialized. + if (ctx.accounts.metadata_account.owner != &system_program::ID) + || !ctx.accounts.metadata_account.data_is_empty() + { + return Err(MplInscriptionError::AlreadyInitialized.into()); + } + + // Do the standard Token Metadata checks. + assert_owned_by( + ctx.accounts.token_metadata_account, + &mpl_token_metadata::ID, + MplInscriptionError::IncorrectOwner, + )?; + + assert_owned_by( + ctx.accounts.mint_account, + &spl_token::ID, + MplInscriptionError::IncorrectOwner, + )?; + + assert_owned_by( + ctx.accounts.token_account, + &spl_token::ID, + MplInscriptionError::IncorrectOwner, + )?; + + let token_metadata_data = ctx.accounts.token_metadata_account.try_borrow_data()?; + let token_metadata: Metadata = Metadata::safe_deserialize(&token_metadata_data)?; + + if token_metadata.mint != *ctx.accounts.mint_account.key { + return Err(MplInscriptionError::MintMismatch.into()); + } + + let token_standard = token_metadata + .token_standard + .ok_or(MplInscriptionError::InvalidTokenStandard)?; + if !matches!( + token_standard, + TokenStandard::NonFungible + | TokenStandard::NonFungibleEdition + | TokenStandard::ProgrammableNonFungible + | TokenStandard::ProgrammableNonFungibleEdition + ) { + return Err(MplInscriptionError::InvalidTokenStandard.into()); + } + + let token_account: spl_token::state::Account = assert_initialized( + ctx.accounts.token_account, + MplInscriptionError::BorshDeserializeError, + )?; + + if token_account.mint != *ctx.accounts.mint_account.key { + return Err(MplInscriptionError::MintMismatch.into()); + } + + if token_account.amount < 1 { + return Err(MplInscriptionError::NotEnoughTokens.into()); + } + + if token_account.mint != token_metadata.mint { + return Err(MplInscriptionError::MintMismatch.into()); + } + + // Verify that the derived address is correct for the metadata account. + let inscription_bump = assert_derivation( + &crate::ID, + ctx.accounts.inscription_account, + &[ + PREFIX.as_bytes(), + crate::ID.as_ref(), + ctx.accounts.mint_account.key.as_ref(), + ], + MplInscriptionError::MetadataDerivedKeyInvalid, + )?; + + // Verify that the derived address is correct for the metadata account. + let bump = assert_derivation( + &crate::ID, + ctx.accounts.metadata_account, + &[ + PREFIX.as_bytes(), + crate::ID.as_ref(), + ctx.accounts.inscription_account.key.as_ref(), + ], + MplInscriptionError::MetadataDerivedKeyInvalid, + )?; + + // The payer and authority must sign. + assert_signer(ctx.accounts.payer)?; + + if ctx.accounts.system_program.key != &system_program::ID { + return Err(MplInscriptionError::InvalidSystemProgram.into()); + } + + // Initialize the inscription account. + create_or_allocate_account_raw( + crate::ID, + ctx.accounts.inscription_account, + ctx.accounts.system_program, + ctx.accounts.payer, + INITIAL_SIZE, + &[ + PREFIX.as_bytes(), + crate::ID.as_ref(), + ctx.accounts.mint_account.key.as_ref(), + &[inscription_bump], + ], + )?; + + let mut update_authorities = vec![token_metadata.update_authority]; + if token_metadata.update_authority != *ctx.accounts.payer.key { + update_authorities.push(*ctx.accounts.payer.key); + } + + // Initialize the inscription metadata. + let inscription_metadata = InscriptionMetadata { + key: Key::MintInscriptionMetadataAccount, + bump, + inscription_bump: Some(inscription_bump), + update_authorities, + ..InscriptionMetadata::default() + }; + + let serialized_metadata = &inscription_metadata.try_to_vec()?; + + // Initialize the inscription metadata account. + create_or_allocate_account_raw( + crate::ID, + ctx.accounts.metadata_account, + ctx.accounts.system_program, + ctx.accounts.payer, + serialized_metadata.len(), + &[ + PREFIX.as_bytes(), + crate::ID.as_ref(), + ctx.accounts.inscription_account.key.as_ref(), + &[bump], + ], + )?; + + // Write the inscription metadata to the metadata account. + sol_memcpy( + &mut ctx.accounts.metadata_account.try_borrow_mut_data()?, + serialized_metadata, + serialized_metadata.len(), + ); + + Ok(()) +} diff --git a/programs/mpl-inscription/src/processor/mod.rs b/programs/mpl-inscription/src/processor/mod.rs index 9959b17..2f65542 100644 --- a/programs/mpl-inscription/src/processor/mod.rs +++ b/programs/mpl-inscription/src/processor/mod.rs @@ -6,6 +6,7 @@ mod add_authority; mod clear_data; mod close; mod initialize; +mod initialize_from_mint; mod remove_authority; mod write_data; @@ -13,6 +14,7 @@ use add_authority::*; use clear_data::*; use close::*; use initialize::*; +use initialize_from_mint::*; use remove_authority::*; use write_data::*; @@ -30,6 +32,10 @@ impl Processor { msg!("Instruction: Initialize"); process_initialize(accounts) } + MplInscriptionInstruction::InitializeFromMint => { + msg!("Instruction: InitializeFromMint"); + process_initialize_from_mint(accounts) + } MplInscriptionInstruction::Close => { msg!("Instruction: Close"); process_close(accounts) diff --git a/programs/mpl-inscription/src/state.rs b/programs/mpl-inscription/src/state.rs index 24002b1..cdc1a16 100644 --- a/programs/mpl-inscription/src/state.rs +++ b/programs/mpl-inscription/src/state.rs @@ -11,6 +11,23 @@ pub const INITIAL_SIZE: usize = 1024; pub enum Key { Uninitialized, InscriptionMetadataAccount, + MintInscriptionMetadataAccount, +} + +#[repr(C)] +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] +pub enum InscriptionState { + Raw, + Validated, + Curated, +} + +#[repr(C)] +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] +pub enum DataType { + Binary, + Json, + Png, } #[repr(C)] @@ -18,6 +35,21 @@ pub enum Key { pub struct InscriptionMetadata { pub key: Key, pub bump: u8, + pub state: InscriptionState, pub inscription_number: Option, + pub inscription_bump: Option, pub update_authorities: Vec, } + +impl Default for InscriptionMetadata { + fn default() -> Self { + Self { + key: Key::InscriptionMetadataAccount, + bump: 0, + state: InscriptionState::Raw, + inscription_number: None, + inscription_bump: None, + update_authorities: vec![], + } + } +}