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

Define IDL for initialize permanent delegate instruction #44

Merged
merged 3 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
1 change: 1 addition & 0 deletions clients/js/src/generated/instructions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export * from './initializeMintCloseAuthority';
export * from './initializeMultisig';
export * from './initializeMultisig2';
export * from './initializeNonTransferableMint';
export * from './initializePermanentDelegate';
export * from './initializeTokenGroup';
export * from './initializeTokenGroupMember';
export * from './initializeTokenMetadata';
Expand Down
174 changes: 174 additions & 0 deletions clients/js/src/generated/instructions/initializePermanentDelegate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/**
* This code was AUTOGENERATED using the codama library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun codama to update it.
*
* @see https://github.com/codama-idl/codama
*/

import {
combineCodec,
getAddressDecoder,
getAddressEncoder,
getStructDecoder,
getStructEncoder,
getU8Decoder,
getU8Encoder,
transformEncoder,
type Address,
type Codec,
type Decoder,
type Encoder,
type IAccountMeta,
type IInstruction,
type IInstructionWithAccounts,
type IInstructionWithData,
type WritableAccount,
} from '@solana/web3.js';
import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs';
import { getAccountMetaFactory, type ResolvedAccount } from '../shared';

export const INITIALIZE_PERMANENT_DELEGATE_DISCRIMINATOR = 35;

export function getInitializePermanentDelegateDiscriminatorBytes() {
return getU8Encoder().encode(INITIALIZE_PERMANENT_DELEGATE_DISCRIMINATOR);
}

export type InitializePermanentDelegateInstruction<
TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS,
TAccountMint extends string | IAccountMeta<string> = string,
TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
> = IInstruction<TProgram> &
IInstructionWithData<Uint8Array> &
IInstructionWithAccounts<
[
TAccountMint extends string
? WritableAccount<TAccountMint>
: TAccountMint,
...TRemainingAccounts,
]
>;

export type InitializePermanentDelegateInstructionData = {
discriminator: number;
/** Authority that may sign for `Transfer`s and `Burn`s on any account */
delegate: Address;
};

export type InitializePermanentDelegateInstructionDataArgs = {
/** Authority that may sign for `Transfer`s and `Burn`s on any account */
delegate: Address;
};

export function getInitializePermanentDelegateInstructionDataEncoder(): Encoder<InitializePermanentDelegateInstructionDataArgs> {
return transformEncoder(
getStructEncoder([
['discriminator', getU8Encoder()],
['delegate', getAddressEncoder()],
]),
(value) => ({
...value,
discriminator: INITIALIZE_PERMANENT_DELEGATE_DISCRIMINATOR,
})
);
}

export function getInitializePermanentDelegateInstructionDataDecoder(): Decoder<InitializePermanentDelegateInstructionData> {
return getStructDecoder([
['discriminator', getU8Decoder()],
['delegate', getAddressDecoder()],
]);
}

export function getInitializePermanentDelegateInstructionDataCodec(): Codec<
InitializePermanentDelegateInstructionDataArgs,
InitializePermanentDelegateInstructionData
> {
return combineCodec(
getInitializePermanentDelegateInstructionDataEncoder(),
getInitializePermanentDelegateInstructionDataDecoder()
);
}

export type InitializePermanentDelegateInput<
TAccountMint extends string = string,
> = {
/** The mint to initialize. */
mint: Address<TAccountMint>;
delegate: InitializePermanentDelegateInstructionDataArgs['delegate'];
};

export function getInitializePermanentDelegateInstruction<
TAccountMint extends string,
TProgramAddress extends Address = typeof TOKEN_2022_PROGRAM_ADDRESS,
>(
input: InitializePermanentDelegateInput<TAccountMint>,
config?: { programAddress?: TProgramAddress }
): InitializePermanentDelegateInstruction<TProgramAddress, TAccountMint> {
// Program address.
const programAddress = config?.programAddress ?? TOKEN_2022_PROGRAM_ADDRESS;

// Original accounts.
const originalAccounts = {
mint: { value: input.mint ?? null, isWritable: true },
};
const accounts = originalAccounts as Record<
keyof typeof originalAccounts,
ResolvedAccount
>;

// Original args.
const args = { ...input };

const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
const instruction = {
accounts: [getAccountMeta(accounts.mint)],
programAddress,
data: getInitializePermanentDelegateInstructionDataEncoder().encode(
args as InitializePermanentDelegateInstructionDataArgs
),
} as InitializePermanentDelegateInstruction<TProgramAddress, TAccountMint>;

return instruction;
}

export type ParsedInitializePermanentDelegateInstruction<
TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS,
TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
> = {
programAddress: Address<TProgram>;
accounts: {
/** The mint to initialize. */
mint: TAccountMetas[0];
};
data: InitializePermanentDelegateInstructionData;
};

export function parseInitializePermanentDelegateInstruction<
TProgram extends string,
TAccountMetas extends readonly IAccountMeta[],
>(
instruction: IInstruction<TProgram> &
IInstructionWithAccounts<TAccountMetas> &
IInstructionWithData<Uint8Array>
): ParsedInitializePermanentDelegateInstruction<TProgram, TAccountMetas> {
if (instruction.accounts.length < 1) {
// TODO: Coded error.
throw new Error('Not enough accounts');
}
let accountIndex = 0;
const getNextAccount = () => {
const accountMeta = instruction.accounts![accountIndex]!;
accountIndex += 1;
return accountMeta;
};
return {
programAddress: instruction.programAddress,
accounts: {
mint: getNextAccount(),
},
data: getInitializePermanentDelegateInstructionDataDecoder().decode(
instruction.data
),
};
}
8 changes: 8 additions & 0 deletions clients/js/src/generated/programs/token2022.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
type ParsedInitializeMultisig2Instruction,
type ParsedInitializeMultisigInstruction,
type ParsedInitializeNonTransferableMintInstruction,
type ParsedInitializePermanentDelegateInstruction,
type ParsedInitializeTokenGroupInstruction,
type ParsedInitializeTokenGroupMemberInstruction,
type ParsedInitializeTokenMetadataInstruction,
Expand Down Expand Up @@ -166,6 +167,7 @@ export enum Token2022Instruction {
InitializeNonTransferableMint,
EnableCpiGuard,
DisableCpiGuard,
InitializePermanentDelegate,
InitializeMetadataPointer,
UpdateMetadataPointer,
InitializeGroupPointer,
Expand Down Expand Up @@ -427,6 +429,9 @@ export function identifyToken2022Instruction(
) {
return Token2022Instruction.DisableCpiGuard;
}
if (containsBytes(data, getU8Encoder().encode(35), 0)) {
return Token2022Instruction.InitializePermanentDelegate;
}
if (
containsBytes(data, getU8Encoder().encode(39), 0) &&
containsBytes(data, getU8Encoder().encode(0), 1)
Expand Down Expand Up @@ -706,6 +711,9 @@ export type ParsedToken2022Instruction<
| ({
instructionType: Token2022Instruction.DisableCpiGuard;
} & ParsedDisableCpiGuardInstruction<TProgram>)
| ({
instructionType: Token2022Instruction.InitializePermanentDelegate;
} & ParsedInitializePermanentDelegateInstruction<TProgram>)
| ({
instructionType: Token2022Instruction.InitializeMetadataPointer;
} & ParsedInitializeMetadataPointerInstruction<TProgram>)
Expand Down
6 changes: 6 additions & 0 deletions clients/js/src/getInitializeInstructionsForExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
getInitializeTokenMetadataInstruction,
getInitializeTransferFeeConfigInstruction,
getInitializeNonTransferableMintInstruction,
getInitializePermanentDelegateInstruction,
} from './generated';

/**
Expand Down Expand Up @@ -85,6 +86,11 @@ export function getPreInitializeInstructionsForMintExtensions(
];
case 'NonTransferable':
return getInitializeNonTransferableMintInstruction({ mint });
case 'PermanentDelegate':
return getInitializePermanentDelegateInstruction({
mint,
delegate: extension.delegate,
});
default:
return [];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Account, address, some } from '@solana/web3.js';
import test from 'ava';
import { Mint, extension, fetchMint } from '../../../src';
import {
createDefaultSolanaClient,
createMint,
generateKeyPairSignerWithSol,
} from '../../_setup';

test('it initializes a mint with permanent delegate', async (t) => {
// Given some signer accounts
const client = createDefaultSolanaClient();
const [authority] = await Promise.all([generateKeyPairSignerWithSol(client)]);

// And a permanent delegate extension
const permanentDelegate = address(
'6sPR6MzvjMMP5LSZzEtTe4ZBVX9rhBmtM1dmfFtkNTbW'
);
const permanentDelegateExtension = extension('PermanentDelegate', {
delegate: permanentDelegate,
});

// When we create a mint with this extension
const mintAddress = await createMint({
authority,
client,
extensions: [permanentDelegateExtension],
payer: authority,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because here, we're testing the initialisation, would you mind using the longer version instead of the createMint helper?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure will update this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lorisleiva I have updated code with a longer version of mint and rebased it with the main branch. Please review.


// Then we expect the mint account to exist with the permanent delegate
const mintAccount = await fetchMint(client.rpc, mintAddress);
t.like(mintAccount, <Account<Mint>>{
address: mintAddress,
data: {
mintAuthority: some(authority.address),
isInitialized: true,
extensions: some([permanentDelegateExtension]),
},
});
});
58 changes: 58 additions & 0 deletions program/idl.json
Original file line number Diff line number Diff line change
Expand Up @@ -5565,6 +5565,64 @@
}
]
},
{
"kind": "instructionNode",
"name": "initializePermanentDelegate",
"docs": [
"Initialize the permanent delegate on a new mint.",
"",
"Fails if the mint has already been initialized, so must be called before `InitializeMint`.",
"",
"The mint must have exactly enough space allocated for the base mint (82 bytes),",
"plus 83 bytes of padding, 1 byte reserved for the account type,",
"then space required for this extension, plus any others."
],
"optionalAccountStrategy": "programId",
"accounts": [
{
"kind": "instructionAccountNode",
"name": "mint",
"isWritable": true,
"isSigner": false,
"isOptional": false,
"docs": ["The mint to initialize."]
}
],
"arguments": [
{
"kind": "instructionArgumentNode",
"name": "discriminator",
"defaultValueStrategy": "omitted",
"docs": [],
"type": {
"kind": "numberTypeNode",
"format": "u8",
"endian": "le"
},
"defaultValue": {
"kind": "numberValueNode",
"number": 35
}
},
{
"kind": "instructionArgumentNode",
"name": "delegate",
"docs": [
"Authority that may sign for `Transfer`s and `Burn`s on any account"
],
"type": {
"kind": "publicKeyTypeNode"
}
}
],
"discriminators": [
{
"kind": "fieldDiscriminatorNode",
"name": "discriminator",
"offset": 0
}
]
},
{
"kind": "instructionNode",
"name": "initializeMetadataPointer",
Expand Down