Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
32 changes: 16 additions & 16 deletions clients/js-legacy/src/instructions/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import type { DecodedInitializeMint2Instruction } from './initializeMint2.js';
import { decodeInitializeMint2Instruction } from './initializeMint2.js';
import type { DecodedInitializeMultisigInstruction } from './initializeMultisig.js';
import { decodeInitializeMultisigInstruction } from './initializeMultisig.js';
import type { DecodedInitializeMultisig2Instruction } from './initializeMultisig2.js';
import { decodeInitializeMultisig2Instruction } from './initializeMultisig2.js';
import type { DecodedMintToInstruction } from './mintTo.js';
import { decodeMintToInstruction } from './mintTo.js';
import type { DecodedMintToCheckedInstruction } from './mintToChecked.js';
Expand All @@ -48,7 +50,7 @@ import { TokenInstruction } from './types.js';
import type { DecodedUiAmountToAmountInstruction } from './uiAmountToAmount.js';
import { decodeUiAmountToAmountInstruction } from './uiAmountToAmount.js';

/** TODO: docs */
/** Union type for all decoded instructions */
export type DecodedInstruction =
| DecodedInitializeMintInstruction
| DecodedInitializeAccountInstruction
Expand All @@ -69,14 +71,12 @@ export type DecodedInstruction =
| DecodedInitializeAccount2Instruction
| DecodedSyncNativeInstruction
| DecodedInitializeAccount3Instruction
| DecodedInitializeMultisig2Instruction
| DecodedInitializeMint2Instruction
| DecodedAmountToUiAmountInstruction
| DecodedUiAmountToAmountInstruction
// | DecodedInitializeMultisig2Instruction
// TODO: implement ^ and remove `never`
| never;
| DecodedUiAmountToAmountInstruction;

/** TODO: docs */
/** Decode and validate any token instruction */
export function decodeInstruction(
instruction: TransactionInstruction,
programId = TOKEN_PROGRAM_ID,
Expand Down Expand Up @@ -106,11 +106,11 @@ export function decodeInstruction(
if (type === TokenInstruction.SyncNative) return decodeSyncNativeInstruction(instruction, programId);
if (type === TokenInstruction.InitializeAccount3)
return decodeInitializeAccount3Instruction(instruction, programId);
if (type === TokenInstruction.InitializeMultisig2)
return decodeInitializeMultisig2Instruction(instruction, programId);
if (type === TokenInstruction.InitializeMint2) return decodeInitializeMint2Instruction(instruction, programId);
if (type === TokenInstruction.AmountToUiAmount) return decodeAmountToUiAmountInstruction(instruction, programId);
if (type === TokenInstruction.UiAmountToAmount) return decodeUiAmountToAmountInstruction(instruction, programId);
// TODO: implement
if (type === TokenInstruction.InitializeMultisig2) throw new TokenInvalidInstructionTypeError();

throw new TokenInvalidInstructionTypeError();
}
Expand Down Expand Up @@ -213,21 +213,21 @@ export function isSyncNativeInstruction(decoded: DecodedInstruction): decoded is
return decoded.data.instruction === TokenInstruction.SyncNative;
}

/** TODO: docs */
/** Type guard to check if instruction is InitializeAccount3 */
export function isInitializeAccount3Instruction(
decoded: DecodedInstruction,
): decoded is DecodedInitializeAccount3Instruction {
return decoded.data.instruction === TokenInstruction.InitializeAccount3;
}

/** TODO: docs, implement */
// export function isInitializeMultisig2Instruction(
// decoded: DecodedInstruction
// ): decoded is DecodedInitializeMultisig2Instruction {
// return decoded.data.instruction === TokenInstruction.InitializeMultisig2;
// }
/** Type guard to check if instruction is InitializeMultisig2 */
export function isInitializeMultisig2Instruction(
decoded: DecodedInstruction,
): decoded is DecodedInitializeMultisig2Instruction {
return decoded.data.instruction === TokenInstruction.InitializeMultisig2;
}

/** TODO: docs */
/** Type guard to check if instruction is InitializeMint2 */
export function isInitializeMint2Instruction(
decoded: DecodedInstruction,
): decoded is DecodedInitializeMint2Instruction {
Expand Down
142 changes: 141 additions & 1 deletion clients/js-legacy/src/instructions/initializeMultisig2.ts
Original file line number Diff line number Diff line change
@@ -1 +1,141 @@
export {}; // TODO: implement
import { struct, u8 } from '@solana/buffer-layout';
import type { AccountMeta, Signer } from '@solana/web3.js';
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID } from '../constants.js';
import {
TokenInvalidInstructionDataError,
TokenInvalidInstructionKeysError,
TokenInvalidInstructionProgramError,
TokenInvalidInstructionTypeError,
} from '../errors.js';
import { TokenInstruction } from './types.js';

/** InitializeMultisig2 instruction data */
export interface InitializeMultisig2InstructionData {
instruction: TokenInstruction.InitializeMultisig2;
m: number;
}

/** InitializeMultisig2 instruction layout */
export const initializeMultisig2InstructionData = struct<InitializeMultisig2InstructionData>([
u8('instruction'),
u8('m'),
]);

/**
* Construct an InitializeMultisig2 instruction
*
* @param account Multisig account
* @param signers Full set of signers
* @param m Number of required signatures
* @param programId SPL Token program account
*
* @return Instruction to add to a transaction
*/
export function createInitializeMultisig2Instruction(
account: PublicKey,
signers: (Signer | PublicKey)[],
m: number,
programId = TOKEN_PROGRAM_ID,
): TransactionInstruction {
const keys = [{ pubkey: account, isSigner: false, isWritable: true }];
for (const signer of signers) {
keys.push({
pubkey: signer instanceof PublicKey ? signer : signer.publicKey,
isSigner: false,
isWritable: false,
});
}

const data = Buffer.alloc(initializeMultisig2InstructionData.span);
initializeMultisig2InstructionData.encode(
{
instruction: TokenInstruction.InitializeMultisig2,
m,
},
data,
);

return new TransactionInstruction({ keys, programId, data });
}

/** A decoded, valid InitializeMultisig2 instruction */
export interface DecodedInitializeMultisig2Instruction {
programId: PublicKey;
keys: {
account: AccountMeta;
signers: AccountMeta[];
};
data: {
instruction: TokenInstruction.InitializeMultisig2;
m: number;
};
}

/**
* Decode an InitializeMultisig2 instruction and validate it
*
* @param instruction Transaction instruction to decode
* @param programId SPL Token program account
*
* @return Decoded, valid instruction
*/
export function decodeInitializeMultisig2Instruction(
instruction: TransactionInstruction,
programId = TOKEN_PROGRAM_ID,
): DecodedInitializeMultisig2Instruction {
if (!instruction.programId.equals(programId)) throw new TokenInvalidInstructionProgramError();
if (instruction.data.length !== initializeMultisig2InstructionData.span)
throw new TokenInvalidInstructionDataError();

const {
keys: { account, signers },
data,
} = decodeInitializeMultisig2InstructionUnchecked(instruction);
if (data.instruction !== TokenInstruction.InitializeMultisig2) throw new TokenInvalidInstructionTypeError();
if (!account || !signers.length) throw new TokenInvalidInstructionKeysError();

return {
programId,
keys: {
account,
signers,
},
data,
};
}

/** A decoded, non-validated InitializeMultisig2 instruction */
export interface DecodedInitializeMultisig2InstructionUnchecked {
programId: PublicKey;
keys: {
account: AccountMeta | undefined;
signers: AccountMeta[];
};
data: {
instruction: number;
m: number;
};
}

/**
* Decode an InitializeMultisig2 instruction without validating it
*
* @param instruction Transaction instruction to decode
*
* @return Decoded, non-validated instruction
*/
export function decodeInitializeMultisig2InstructionUnchecked({
programId,
keys: [account, ...signers],
data,
}: TransactionInstruction): DecodedInitializeMultisig2InstructionUnchecked {
return {
programId,
keys: {
account,
signers,
},
data: initializeMultisig2InstructionData.decode(data),
};
}