diff --git a/modules/sdk-coin-sol/src/lib/constants.ts b/modules/sdk-coin-sol/src/lib/constants.ts index 65718fa580..2c84113617 100644 --- a/modules/sdk-coin-sol/src/lib/constants.ts +++ b/modules/sdk-coin-sol/src/lib/constants.ts @@ -28,6 +28,8 @@ export enum ValidInstructionTypesEnum { Split = 'Split', Authorize = 'Authorize', SetPriorityFee = 'SetPriorityFee', + MintTo = 'MintTo', + Burn = 'Burn', } // Internal instructions types @@ -45,6 +47,8 @@ export enum InstructionBuilderTypes { StakingAuthorize = 'Authorize', StakingDelegate = 'Delegate', SetPriorityFee = 'SetPriorityFee', + MintTo = 'MintTo', + Burn = 'Burn', } export const VALID_SYSTEM_INSTRUCTION_TYPES: ValidInstructionTypes[] = [ @@ -65,6 +69,8 @@ export const VALID_SYSTEM_INSTRUCTION_TYPES: ValidInstructionTypes[] = [ ValidInstructionTypesEnum.Split, ValidInstructionTypesEnum.Authorize, ValidInstructionTypesEnum.SetPriorityFee, + ValidInstructionTypesEnum.MintTo, + ValidInstructionTypesEnum.Burn, ]; /** Const to check the order of the Wallet Init instructions when decode */ diff --git a/modules/sdk-coin-sol/src/lib/iface.ts b/modules/sdk-coin-sol/src/lib/iface.ts index b5a88b616d..c4126809de 100644 --- a/modules/sdk-coin-sol/src/lib/iface.ts +++ b/modules/sdk-coin-sol/src/lib/iface.ts @@ -38,7 +38,9 @@ export type InstructionParams = | AtaClose | TokenTransfer | StakingAuthorize - | StakingDelegate; + | StakingDelegate + | MintTo + | Burn; export interface Memo { type: InstructionBuilderTypes.Memo; @@ -78,6 +80,32 @@ export interface TokenTransfer { }; } +export interface MintTo { + type: InstructionBuilderTypes.MintTo; + params: { + mintAddress: string; + destinationAddress: string; + authorityAddress: string; + amount: string; + tokenName: string; + decimalPlaces?: number; + programId?: string; + }; +} + +export interface Burn { + type: InstructionBuilderTypes.Burn; + params: { + mintAddress: string; + accountAddress: string; + authorityAddress: string; + amount: string; + tokenName: string; + decimalPlaces?: number; + programId?: string; + }; +} + export interface StakingActivate { type: InstructionBuilderTypes.StakingActivate; params: { @@ -154,7 +182,9 @@ export type ValidInstructionTypes = | 'CloseAssociatedTokenAccount' | DecodedCloseAccountInstruction | 'TokenTransfer' - | 'SetPriorityFee'; + | 'SetPriorityFee' + | 'MintTo' + | 'Burn'; export type StakingAuthorizeParams = { stakingAddress: string; diff --git a/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts b/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts index 0cae83f3e3..a3742d8d07 100644 --- a/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts +++ b/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts @@ -1,6 +1,10 @@ import { DecodedTransferCheckedInstruction, decodeTransferCheckedInstruction, + DecodedBurnInstruction, + decodeBurnInstruction, + DecodedMintToInstruction, + decodeMintToInstruction, TOKEN_2022_PROGRAM_ID, } from '@solana/spl-token'; import { @@ -27,8 +31,10 @@ import { InstructionBuilderTypes, ValidInstructionTypesEnum, walletInitInstructi import { AtaClose, AtaInit, + Burn, InstructionParams, Memo, + MintTo, Nonce, StakingActivate, StakingAuthorize, @@ -125,8 +131,10 @@ function parseSendInstructions( instructions: TransactionInstruction[], instructionMetadata?: InstructionParams[], _useTokenAddressTokenName?: boolean -): Array { - const instructionData: Array = []; +): Array { + const instructionData: Array< + Nonce | Memo | Transfer | TokenTransfer | AtaInit | AtaClose | SetPriorityFee | MintTo | Burn + > = []; for (const instruction of instructions) { const type = getInstructionType(instruction); switch (type) { @@ -232,6 +240,60 @@ function parseSendInstructions( }; instructionData.push(setPriorityFee); break; + case ValidInstructionTypesEnum.MintTo: + let mintToInstruction: DecodedMintToInstruction; + if (instruction.programId.toString() !== TOKEN_2022_PROGRAM_ID.toString()) { + mintToInstruction = decodeMintToInstruction(instruction); + } else { + mintToInstruction = decodeMintToInstruction(instruction, TOKEN_2022_PROGRAM_ID); + } + const mintAddressForMint = mintToInstruction.keys.mint.pubkey.toString(); + const tokenNameForMint = findTokenName(mintAddressForMint, instructionMetadata, _useTokenAddressTokenName); + let programIDForMint: string | undefined; + if (instruction.programId) { + programIDForMint = instruction.programId.toString(); + } + const mintTo: MintTo = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress: mintAddressForMint, + destinationAddress: mintToInstruction.keys.destination.pubkey.toString(), + authorityAddress: mintToInstruction.keys.authority.pubkey.toString(), + amount: mintToInstruction.data.amount.toString(), + tokenName: tokenNameForMint, + decimalPlaces: undefined, + programId: programIDForMint, + }, + }; + instructionData.push(mintTo); + break; + case ValidInstructionTypesEnum.Burn: + let burnInstruction: DecodedBurnInstruction; + if (instruction.programId.toString() !== TOKEN_2022_PROGRAM_ID.toString()) { + burnInstruction = decodeBurnInstruction(instruction); + } else { + burnInstruction = decodeBurnInstruction(instruction, TOKEN_2022_PROGRAM_ID); + } + const mintAddressForBurn = burnInstruction.keys.mint.pubkey.toString(); + const tokenNameForBurn = findTokenName(mintAddressForBurn, instructionMetadata, _useTokenAddressTokenName); + let programIDForBurn: string | undefined; + if (instruction.programId) { + programIDForBurn = instruction.programId.toString(); + } + const burn: Burn = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress: mintAddressForBurn, + accountAddress: burnInstruction.keys.account.pubkey.toString(), + authorityAddress: burnInstruction.keys.owner.pubkey.toString(), + amount: burnInstruction.data.amount.toString(), + tokenName: tokenNameForBurn, + decimalPlaces: undefined, + programId: programIDForBurn, + }, + }; + instructionData.push(burn); + break; default: throw new NotSupported( 'Invalid transaction, instruction type not supported: ' + getInstructionType(instruction) diff --git a/modules/sdk-coin-sol/src/lib/solInstructionFactory.ts b/modules/sdk-coin-sol/src/lib/solInstructionFactory.ts index 8bdd3b87d9..614c08863a 100644 --- a/modules/sdk-coin-sol/src/lib/solInstructionFactory.ts +++ b/modules/sdk-coin-sol/src/lib/solInstructionFactory.ts @@ -2,6 +2,8 @@ import { SolCoin } from '@bitgo/statics'; import { createAssociatedTokenAccountInstruction, createCloseAccountInstruction, + createMintToInstruction, + createBurnInstruction, createTransferCheckedInstruction, TOKEN_2022_PROGRAM_ID, } from '@solana/spl-token'; @@ -24,6 +26,8 @@ import { AtaInit, InstructionParams, Memo, + MintTo, + Burn, Nonce, StakingActivate, StakingAuthorize, @@ -71,6 +75,10 @@ export function solInstructionFactory(instructionToBuild: InstructionParams): Tr return stakingDelegateInstruction(instructionToBuild); case InstructionBuilderTypes.SetPriorityFee: return fetchPriorityFeeInstruction(instructionToBuild); + case InstructionBuilderTypes.MintTo: + return mintToInstruction(instructionToBuild); + case InstructionBuilderTypes.Burn: + return burnInstruction(instructionToBuild); default: throw new Error(`Invalid instruction type or not supported`); } @@ -480,3 +488,61 @@ function stakingDelegateInstruction(data: StakingDelegate): TransactionInstructi return tx.instructions; } + +/** + * Construct MintTo Solana instructions + * + * @param {MintTo} data - the data to build the instruction + * @returns {TransactionInstruction[]} An array containing MintTo Solana instructions + */ +function mintToInstruction(data: MintTo): TransactionInstruction[] { + const { + params: { mintAddress, destinationAddress, authorityAddress, amount, programId }, + } = data; + assert(mintAddress, 'Missing mintAddress param'); + assert(destinationAddress, 'Missing destinationAddress param'); + assert(authorityAddress, 'Missing authorityAddress param'); + assert(amount, 'Missing amount param'); + + const mint = new PublicKey(mintAddress); + const destination = new PublicKey(destinationAddress); + const authority = new PublicKey(authorityAddress); + + let mintToInstr: TransactionInstruction; + if (programId && programId === TOKEN_2022_PROGRAM_ID.toString()) { + mintToInstr = createMintToInstruction(mint, destination, authority, BigInt(amount), [], TOKEN_2022_PROGRAM_ID); + } else { + mintToInstr = createMintToInstruction(mint, destination, authority, BigInt(amount)); + } + + return [mintToInstr]; +} + +/** + * Construct Burn Solana instructions + * + * @param {Burn} data - the data to build the instruction + * @returns {TransactionInstruction[]} An array containing Burn Solana instructions + */ +function burnInstruction(data: Burn): TransactionInstruction[] { + const { + params: { mintAddress, accountAddress, authorityAddress, amount, programId }, + } = data; + assert(mintAddress, 'Missing mintAddress param'); + assert(accountAddress, 'Missing accountAddress param'); + assert(authorityAddress, 'Missing authorityAddress param'); + assert(amount, 'Missing amount param'); + + const mint = new PublicKey(mintAddress); + const account = new PublicKey(accountAddress); + const authority = new PublicKey(authorityAddress); + + let burnInstr: TransactionInstruction; + if (programId && programId === TOKEN_2022_PROGRAM_ID.toString()) { + burnInstr = createBurnInstruction(account, mint, authority, BigInt(amount), [], TOKEN_2022_PROGRAM_ID); + } else { + burnInstr = createBurnInstruction(account, mint, authority, BigInt(amount)); + } + + return [burnInstr]; +} diff --git a/modules/sdk-coin-sol/src/lib/utils.ts b/modules/sdk-coin-sol/src/lib/utils.ts index 807617f024..7fc57926d0 100644 --- a/modules/sdk-coin-sol/src/lib/utils.ts +++ b/modules/sdk-coin-sol/src/lib/utils.ts @@ -10,9 +10,13 @@ import { BaseCoin, BaseNetwork, CoinNotDefinedError, coins, SolCoin } from '@bit import { ASSOCIATED_TOKEN_PROGRAM_ID, decodeCloseAccountInstruction, + decodeBurnInstruction, + decodeMintToInstruction, getAssociatedTokenAddress, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, + DecodedBurnInstruction, + DecodedMintToInstruction, } from '@solana/spl-token'; import { Keypair, @@ -279,7 +283,7 @@ export function getTransactionType(transaction: SolTransaction): TransactionType // check if deactivate instruction does not exist because deactivate can be include a transfer instruction const memoInstruction = instructions.find((instruction) => getInstructionType(instruction) === 'Memo'); const memoData = memoInstruction?.data.toString('utf-8'); - if (instructions.filter((instruction) => getInstructionType(instruction) === 'Deactivate').length == 0) { + if (instructions.filter((instruction) => getInstructionType(instruction) === 'Deactivate').length === 0) { for (const instruction of instructions) { const instructionType = getInstructionType(instruction); // Check if memo instruction is there and if it contains 'PrepareForRevoke' because Marinade staking deactivate transaction will have this @@ -345,9 +349,40 @@ export function getInstructionType(instruction: TransactionInstruction): ValidIn return 'CloseAssociatedTokenAccount'; } } catch (e) { - // ignore error and default to TokenTransfer - return 'TokenTransfer'; + // ignore error and continue to check for other instruction types } + + // Check for burn instructions (instruction code 8) + try { + let burnInstruction: DecodedBurnInstruction; + if (instruction.programId.toString() !== TOKEN_2022_PROGRAM_ID.toString()) { + burnInstruction = decodeBurnInstruction(instruction); + } else { + burnInstruction = decodeBurnInstruction(instruction, TOKEN_2022_PROGRAM_ID); + } + if (burnInstruction && burnInstruction.data.instruction === 8) { + return 'Burn'; + } + } catch (e) { + // ignore error and continue to check for other instruction types + } + + // Check for mint instructions (instruction code 7) + try { + let mintInstruction: DecodedMintToInstruction; + if (instruction.programId.toString() !== TOKEN_2022_PROGRAM_ID.toString()) { + mintInstruction = decodeMintToInstruction(instruction); + } else { + mintInstruction = decodeMintToInstruction(instruction, TOKEN_2022_PROGRAM_ID); + } + if (mintInstruction && mintInstruction.data.instruction === 7) { + return 'MintTo'; + } + } catch (e) { + // ignore error and continue to check for other instruction types + } + + // Default to TokenTransfer for other token instructions return 'TokenTransfer'; case StakeProgram.programId.toString(): return StakeInstruction.decodeInstructionType(instruction); @@ -546,8 +581,8 @@ export async function getAssociatedTokenAccountAddress( if (!programId) { const coin = getSolTokenFromAddressOnly(tokenMintAddress); - if (coin && coin instanceof SolCoin && (coin as any).programId) { - programId = (coin as any).programId.toString(); + if (coin && coin instanceof SolCoin && 'programId' in coin && coin.programId) { + programId = coin.programId.toString(); } else { programId = TOKEN_PROGRAM_ID.toString(); } @@ -562,13 +597,13 @@ export async function getAssociatedTokenAccountAddress( return ataAddress.toString(); } -export function validateMintAddress(mintAddress: string) { +export function validateMintAddress(mintAddress: string): void { if (!mintAddress || !isValidAddress(mintAddress)) { throw new BuildTransactionError('Invalid or missing mintAddress, got: ' + mintAddress); } } -export function validateOwnerAddress(ownerAddress: string) { +export function validateOwnerAddress(ownerAddress: string): void { if (!ownerAddress || !isValidAddress(ownerAddress)) { throw new BuildTransactionError('Invalid or missing ownerAddress, got: ' + ownerAddress); } diff --git a/modules/sdk-coin-sol/test/unit/instructionParamsFactory.ts b/modules/sdk-coin-sol/test/unit/instructionParamsFactory.ts index 28fd2c6b53..df3829e40c 100644 --- a/modules/sdk-coin-sol/test/unit/instructionParamsFactory.ts +++ b/modules/sdk-coin-sol/test/unit/instructionParamsFactory.ts @@ -9,6 +9,8 @@ import BigNumber from 'bignumber.js'; import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, + createMintToInstruction, + createBurnInstruction, TOKEN_2022_PROGRAM_ID, } from '@solana/spl-token'; @@ -278,6 +280,199 @@ describe('Instruction Parser Tests: ', function () { const result = instructionParamsFactory(TransactionType.AssociatedTokenAccountInitialization, ataInstructions); should.deepEqual(result, createATAParams); }); + + it('Mint To - Standard SPL Token instruction parsing', () => { + const mintAddress = testData.tokenTransfers.mintUSDC; + const destinationAddress = testData.tokenTransfers.sourceUSDC; + const authorityAddress = testData.authAccount.pub; + const amount = '1000000'; + const tokenName = testData.tokenTransfers.nameUSDC; + + const mintInstruction = createMintToInstruction( + new PublicKey(mintAddress), + new PublicKey(destinationAddress), + new PublicKey(authorityAddress), + BigInt(amount) + ); + + const expectedMintParams: InstructionParams = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress, + destinationAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces: undefined, + programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + }, + }; + + const result = instructionParamsFactory(TransactionType.Send, [mintInstruction]); + should.deepEqual(result, [expectedMintParams]); + }); + + it('Mint To - Token-2022 Program instruction parsing', () => { + const mintAddress = testData.sol2022TokenTransfers.mint; + const destinationAddress = testData.sol2022TokenTransfers.source; + const authorityAddress = testData.authAccount.pub; + const amount = '2000000'; + const tokenName = testData.sol2022TokenTransfers.name; + + const mintInstruction = createMintToInstruction( + new PublicKey(mintAddress), + new PublicKey(destinationAddress), + new PublicKey(authorityAddress), + BigInt(amount), + undefined, + TOKEN_2022_PROGRAM_ID + ); + + const expectedMintParams: InstructionParams = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress, + destinationAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces: undefined, + programId: TOKEN_2022_PROGRAM_ID.toString(), + }, + }; + + const result = instructionParamsFactory(TransactionType.Send, [mintInstruction]); + should.deepEqual(result, [expectedMintParams]); + }); + + it('Burn - Standard SPL Token instruction parsing', () => { + const mintAddress = testData.tokenTransfers.mintUSDC; + const accountAddress = testData.tokenTransfers.sourceUSDC; + const authorityAddress = testData.authAccount.pub; + const amount = '500000'; + const tokenName = testData.tokenTransfers.nameUSDC; + + const burnInstruction = createBurnInstruction( + new PublicKey(accountAddress), + new PublicKey(mintAddress), + new PublicKey(authorityAddress), + BigInt(amount) + ); + + const expectedBurnParams: InstructionParams = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress, + accountAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces: undefined, + programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + }, + }; + + const result = instructionParamsFactory(TransactionType.Send, [burnInstruction]); + should.deepEqual(result, [expectedBurnParams]); + }); + + it('Burn - Token-2022 Program instruction parsing', () => { + const mintAddress = testData.sol2022TokenTransfers.mint; + const accountAddress = testData.sol2022TokenTransfers.source; + const authorityAddress = testData.authAccount.pub; + const amount = '750000'; + const tokenName = testData.sol2022TokenTransfers.name; + + const burnInstruction = createBurnInstruction( + new PublicKey(accountAddress), + new PublicKey(mintAddress), + new PublicKey(authorityAddress), + BigInt(amount), + undefined, + TOKEN_2022_PROGRAM_ID + ); + + const expectedBurnParams: InstructionParams = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress, + accountAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces: undefined, + programId: TOKEN_2022_PROGRAM_ID.toString(), + }, + }; + + const result = instructionParamsFactory(TransactionType.Send, [burnInstruction]); + should.deepEqual(result, [expectedBurnParams]); + }); + + it('Mixed instructions - Mint, Burn, and Transfer', () => { + const authAccount = testData.authAccount.pub; + const nonceAccount = testData.nonceAccount.pub; + const mintAddress = testData.tokenTransfers.mintUSDC; + const destinationAddress = testData.tokenTransfers.sourceUSDC; + const accountAddress = testData.tokenTransfers.sourceUSDC; + const tokenName = testData.tokenTransfers.nameUSDC; + + // Nonce advance + const nonceAdvanceInstruction = SystemProgram.nonceAdvance({ + noncePubkey: new PublicKey(nonceAccount), + authorizedPubkey: new PublicKey(authAccount), + }); + const nonceAdvanceParams: InstructionParams = { + type: InstructionBuilderTypes.NonceAdvance, + params: { walletNonceAddress: nonceAccount, authWalletAddress: authAccount }, + }; + + // Mint instruction + const mintInstruction = createMintToInstruction( + new PublicKey(mintAddress), + new PublicKey(destinationAddress), + new PublicKey(authAccount), + BigInt('1000000') + ); + const expectedMintParams: InstructionParams = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress, + destinationAddress, + authorityAddress: authAccount, + amount: '1000000', + tokenName, + decimalPlaces: undefined, + programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + }, + }; + + // Burn instruction + const burnInstruction = createBurnInstruction( + new PublicKey(accountAddress), + new PublicKey(mintAddress), + new PublicKey(authAccount), + BigInt('500000') + ); + const expectedBurnParams: InstructionParams = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress, + accountAddress, + authorityAddress: authAccount, + amount: '500000', + tokenName, + decimalPlaces: undefined, + programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + }, + }; + + const instructions = [nonceAdvanceInstruction, mintInstruction, burnInstruction]; + const expectedParams = [nonceAdvanceParams, expectedMintParams, expectedBurnParams]; + + const result = instructionParamsFactory(TransactionType.Send, instructions); + should.deepEqual(result, expectedParams); + }); }); describe('Fail ', function () { it('Invalid type', () => { diff --git a/modules/sdk-coin-sol/test/unit/solInstructionFactory.ts b/modules/sdk-coin-sol/test/unit/solInstructionFactory.ts index b394906d85..dc0bd2ce73 100644 --- a/modules/sdk-coin-sol/test/unit/solInstructionFactory.ts +++ b/modules/sdk-coin-sol/test/unit/solInstructionFactory.ts @@ -4,7 +4,13 @@ import { solInstructionFactory } from '../../src/lib/solInstructionFactory'; import { InstructionBuilderTypes, MEMO_PROGRAM_PK } from '../../src/lib/constants'; import { InstructionParams } from '../../src/lib/iface'; import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; -import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction } from '@solana/spl-token'; +import { + createAssociatedTokenAccountInstruction, + createTransferCheckedInstruction, + createMintToInstruction, + createBurnInstruction, + TOKEN_2022_PROGRAM_ID, +} from '@solana/spl-token'; import BigNumber from 'bignumber.js'; describe('Instruction Builder Tests: ', function () { @@ -142,6 +148,194 @@ describe('Instruction Builder Tests: ', function () { ), ]); }); + + it('Mint To - Standard SPL Token', () => { + const mintAddress = testData.tokenTransfers.mintUSDC; + const destinationAddress = testData.tokenTransfers.sourceUSDC; + const authorityAddress = testData.authAccount.pub; + const amount = '1000000'; + const tokenName = testData.tokenTransfers.nameUSDC; + const decimalPlaces = testData.tokenTransfers.decimals; + + const mintParams: InstructionParams = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress, + destinationAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces, + }, + }; + + const result = solInstructionFactory(mintParams); + should.deepEqual(result, [ + createMintToInstruction( + new PublicKey(mintAddress), + new PublicKey(destinationAddress), + new PublicKey(authorityAddress), + BigInt(amount) + ), + ]); + }); + + it('Mint To - Token-2022 Program', () => { + const mintAddress = testData.sol2022TokenTransfers.mint; + const destinationAddress = testData.sol2022TokenTransfers.source; + const authorityAddress = testData.authAccount.pub; + const amount = '2000000'; + const tokenName = testData.sol2022TokenTransfers.name; + const decimalPlaces = testData.sol2022TokenTransfers.decimals; + + const mintParams: InstructionParams = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress, + destinationAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces, + programId: TOKEN_2022_PROGRAM_ID.toString(), + }, + }; + + const result = solInstructionFactory(mintParams); + should.deepEqual(result, [ + createMintToInstruction( + new PublicKey(mintAddress), + new PublicKey(destinationAddress), + new PublicKey(authorityAddress), + BigInt(amount), + undefined, + TOKEN_2022_PROGRAM_ID + ), + ]); + }); + + it('Burn - Standard SPL Token', () => { + const mintAddress = testData.tokenTransfers.mintUSDC; + const accountAddress = testData.tokenTransfers.sourceUSDC; + const authorityAddress = testData.authAccount.pub; + const amount = '500000'; + const tokenName = testData.tokenTransfers.nameUSDC; + const decimalPlaces = testData.tokenTransfers.decimals; + + const burnParams: InstructionParams = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress, + accountAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces, + }, + }; + + const result = solInstructionFactory(burnParams); + should.deepEqual(result, [ + createBurnInstruction( + new PublicKey(accountAddress), + new PublicKey(mintAddress), + new PublicKey(authorityAddress), + BigInt(amount) + ), + ]); + }); + + it('Burn - Token-2022 Program', () => { + const mintAddress = testData.sol2022TokenTransfers.mint; + const accountAddress = testData.sol2022TokenTransfers.source; + const authorityAddress = testData.authAccount.pub; + const amount = '750000'; + const tokenName = testData.sol2022TokenTransfers.name; + const decimalPlaces = testData.sol2022TokenTransfers.decimals; + + const burnParams: InstructionParams = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress, + accountAddress, + authorityAddress, + amount, + tokenName, + decimalPlaces, + programId: TOKEN_2022_PROGRAM_ID.toString(), + }, + }; + + const result = solInstructionFactory(burnParams); + should.deepEqual(result, [ + createBurnInstruction( + new PublicKey(accountAddress), + new PublicKey(mintAddress), + new PublicKey(authorityAddress), + BigInt(amount), + undefined, + TOKEN_2022_PROGRAM_ID + ), + ]); + }); + + it('Mint To - Without decimal places', () => { + const mintAddress = testData.tokenTransfers.mintUSDC; + const destinationAddress = testData.tokenTransfers.sourceUSDC; + const authorityAddress = testData.authAccount.pub; + const amount = '1000000'; + const tokenName = testData.tokenTransfers.nameUSDC; + + const mintParams: InstructionParams = { + type: InstructionBuilderTypes.MintTo, + params: { + mintAddress, + destinationAddress, + authorityAddress, + amount, + tokenName, + }, + }; + + const result = solInstructionFactory(mintParams); + should.deepEqual(result, [ + createMintToInstruction( + new PublicKey(mintAddress), + new PublicKey(destinationAddress), + new PublicKey(authorityAddress), + BigInt(amount) + ), + ]); + }); + + it('Burn - Without decimal places', () => { + const mintAddress = testData.tokenTransfers.mintUSDC; + const accountAddress = testData.tokenTransfers.sourceUSDC; + const authorityAddress = testData.authAccount.pub; + const amount = '500000'; + const tokenName = testData.tokenTransfers.nameUSDC; + + const burnParams: InstructionParams = { + type: InstructionBuilderTypes.Burn, + params: { + mintAddress, + accountAddress, + authorityAddress, + amount, + tokenName, + }, + }; + + const result = solInstructionFactory(burnParams); + should.deepEqual(result, [ + createBurnInstruction( + new PublicKey(accountAddress), + new PublicKey(mintAddress), + new PublicKey(authorityAddress), + BigInt(amount) + ), + ]); + }); }); describe('Fail ', function () {