PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-sol/src/lib
Просмотр файла: instructionParamsFactory.ts
import { decodeTransferCheckedInstruction } from '@solana/spl-token';
import {
AllocateParams,
AssignParams,
AuthorizeStakeParams,
CreateAccountParams,
DeactivateStakeParams,
DecodedTransferInstruction,
DelegateStakeParams,
InitializeStakeParams,
SplitStakeParams,
StakeInstruction,
StakeProgram,
SystemInstruction,
TransactionInstruction,
ComputeBudgetInstruction,
} from '@solana/web3.js';
import { NotSupported, TransactionType } from '@bitgo/sdk-core';
import { coins, SolCoin } from '@bitgo/statics';
import assert from 'assert';
import { InstructionBuilderTypes, ValidInstructionTypesEnum, walletInitInstructionIndexes } from './constants';
import {
AtaClose,
AtaInit,
InstructionParams,
Memo,
Nonce,
StakingActivate,
StakingAuthorize,
StakingDeactivate,
StakingDelegate,
StakingWithdraw,
TokenTransfer,
Transfer,
WalletInit,
SetPriorityFee,
} from './iface';
import { getInstructionType } from './utils';
/**
* Construct instructions params from Solana instructions
*
* @param {TransactionType} type - the transaction type
* @param {TransactionInstruction[]} instructions - solana instructions
* @returns {InstructionParams[]} An array containing instruction params
*/
export function instructionParamsFactory(
type: TransactionType,
instructions: TransactionInstruction[],
coinName?: string
): InstructionParams[] {
switch (type) {
case TransactionType.WalletInitialization:
return parseWalletInitInstructions(instructions);
case TransactionType.Send:
return parseSendInstructions(instructions);
case TransactionType.StakingActivate:
return parseStakingActivateInstructions(instructions);
case TransactionType.StakingDeactivate:
return parseStakingDeactivateInstructions(instructions, coinName);
case TransactionType.StakingWithdraw:
return parseStakingWithdrawInstructions(instructions);
case TransactionType.AssociatedTokenAccountInitialization:
return parseAtaInitInstructions(instructions);
case TransactionType.CloseAssociatedTokenAccount:
return parseAtaCloseInstructions(instructions);
case TransactionType.StakingAuthorize:
return parseStakingAuthorizeInstructions(instructions);
case TransactionType.StakingAuthorizeRaw:
return parseStakingAuthorizeRawInstructions(instructions);
case TransactionType.StakingDelegate:
return parseStakingDelegateInstructions(instructions);
default:
throw new NotSupported('Invalid transaction, transaction type not supported: ' + type);
}
}
/**
* Parses Solana instructions to Wallet initialization tx instructions params
*
* @param {TransactionInstruction[]} instructions - containing create and initialize nonce solana instructions
* @returns {InstructionParams[]} An array containing instruction params for Wallet initialization tx
*/
function parseWalletInitInstructions(instructions: TransactionInstruction[]): Array<WalletInit | Memo> {
const instructionData: Array<WalletInit | Memo> = [];
const createInstruction = SystemInstruction.decodeCreateAccount(instructions[walletInitInstructionIndexes.Create]);
const nonceInitInstruction = SystemInstruction.decodeNonceInitialize(
instructions[walletInitInstructionIndexes.InitializeNonceAccount]
);
const walletInit: WalletInit = {
type: InstructionBuilderTypes.CreateNonceAccount,
params: {
fromAddress: createInstruction.fromPubkey.toString(),
nonceAddress: nonceInitInstruction.noncePubkey.toString(),
authAddress: nonceInitInstruction.authorizedPubkey.toString(),
amount: createInstruction.lamports.toString(),
},
};
instructionData.push(walletInit);
const memo = getMemo(instructions, walletInitInstructionIndexes);
if (memo) {
instructionData.push(memo);
}
return instructionData;
}
/**
* Parses Solana instructions to Send tx instructions params
* Only supports Memo, Transfer and Advance Nonce Solana instructions
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for Send tx
*/
function parseSendInstructions(
instructions: TransactionInstruction[]
): Array<Nonce | Memo | Transfer | TokenTransfer | AtaInit | AtaClose | SetPriorityFee> {
const instructionData: Array<Nonce | Memo | Transfer | TokenTransfer | AtaInit | AtaClose | SetPriorityFee> = [];
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.Memo:
const memo: Memo = { type: InstructionBuilderTypes.Memo, params: { memo: instruction.data.toString() } };
instructionData.push(memo);
break;
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.Transfer:
const transferInstruction = SystemInstruction.decodeTransfer(instruction);
const transfer: Transfer = {
type: InstructionBuilderTypes.Transfer,
params: {
fromAddress: transferInstruction.fromPubkey.toString(),
toAddress: transferInstruction.toPubkey.toString(),
amount: transferInstruction.lamports.toString(),
},
};
instructionData.push(transfer);
break;
case ValidInstructionTypesEnum.TokenTransfer:
const tokenTransferInstruction = decodeTransferCheckedInstruction(instruction);
const tokenName = findTokenName(tokenTransferInstruction.keys.mint.pubkey.toString());
const tokenTransfer: TokenTransfer = {
type: InstructionBuilderTypes.TokenTransfer,
params: {
fromAddress: tokenTransferInstruction.keys.owner.pubkey.toString(),
toAddress: tokenTransferInstruction.keys.destination.pubkey.toString(),
amount: tokenTransferInstruction.data.amount.toString(),
tokenName,
sourceAddress: tokenTransferInstruction.keys.source.pubkey.toString(),
},
};
instructionData.push(tokenTransfer);
break;
case ValidInstructionTypesEnum.InitializeAssociatedTokenAccount:
const mintAddress = instruction.keys[ataInitInstructionKeysIndexes.MintAddress].pubkey.toString();
const mintTokenName = findTokenName(mintAddress);
const ataInit: AtaInit = {
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
params: {
mintAddress,
ataAddress: instruction.keys[ataInitInstructionKeysIndexes.ATAAddress].pubkey.toString(),
ownerAddress: instruction.keys[ataInitInstructionKeysIndexes.OwnerAddress].pubkey.toString(),
payerAddress: instruction.keys[ataInitInstructionKeysIndexes.PayerAddress].pubkey.toString(),
tokenName: mintTokenName,
},
};
instructionData.push(ataInit);
break;
case ValidInstructionTypesEnum.CloseAssociatedTokenAccount:
const accountAddress = instruction.keys[closeAtaInstructionKeysIndexes.AccountAddress].pubkey.toString();
const destinationAddress =
instruction.keys[closeAtaInstructionKeysIndexes.DestinationAddress].pubkey.toString();
const authorityAddress = instruction.keys[closeAtaInstructionKeysIndexes.AuthorityAddress].pubkey.toString();
const ataClose: AtaClose = {
type: InstructionBuilderTypes.CloseAssociatedTokenAccount,
params: {
accountAddress,
destinationAddress,
authorityAddress,
},
};
instructionData.push(ataClose);
break;
case ValidInstructionTypesEnum.SetPriorityFee:
const setComputeUnitPriceParams = ComputeBudgetInstruction.decodeSetComputeUnitPrice(instruction);
const setPriorityFee: SetPriorityFee = {
type: InstructionBuilderTypes.SetPriorityFee,
params: {
fee: setComputeUnitPriceParams.microLamports,
},
};
instructionData.push(setPriorityFee);
break;
default:
throw new NotSupported(
'Invalid transaction, instruction type not supported: ' + getInstructionType(instruction)
);
}
}
return instructionData;
}
/**
* Parses Solana instructions to create staking tx and delegate tx instructions params
* Only supports Nonce, StakingActivate and Memo Solana instructions
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for staking activate tx
*/
function parseStakingActivateInstructions(
instructions: TransactionInstruction[]
): Array<Nonce | StakingActivate | Memo> {
const instructionData: Array<Nonce | StakingActivate | Memo> = [];
const stakingInstructions = {} as StakingInstructions;
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.Memo:
const memo: Memo = { type: InstructionBuilderTypes.Memo, params: { memo: instruction.data.toString() } };
instructionData.push(memo);
break;
case ValidInstructionTypesEnum.Create:
stakingInstructions.create = SystemInstruction.decodeCreateAccount(instruction);
break;
case ValidInstructionTypesEnum.StakingInitialize:
stakingInstructions.initialize = StakeInstruction.decodeInitialize(instruction);
break;
case ValidInstructionTypesEnum.StakingDelegate:
stakingInstructions.delegate = StakeInstruction.decodeDelegate(instruction);
break;
}
}
validateStakingInstructions(stakingInstructions);
const stakingActivate: StakingActivate = {
type: InstructionBuilderTypes.StakingActivate,
params: {
fromAddress: stakingInstructions.create?.fromPubkey.toString() || '',
stakingAddress: stakingInstructions.initialize?.stakePubkey.toString() || '',
amount: stakingInstructions.create?.lamports.toString() || '',
validator:
stakingInstructions.delegate?.votePubkey.toString() ||
stakingInstructions.initialize?.authorized.staker.toString() ||
'',
isMarinade: stakingInstructions.delegate === undefined,
},
};
instructionData.push(stakingActivate);
return instructionData;
}
/**
* Parses Solana instructions to create delegate tx
* Only supports Nonce, StakingDelegate
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for staking delegate tx
*/
function parseStakingDelegateInstructions(instructions: TransactionInstruction[]): Array<Nonce | StakingDelegate> {
const instructionData: Array<Nonce | StakingDelegate> = [];
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.StakingDelegate:
const stakingDelegateParams = StakeInstruction.decodeDelegate(instruction);
const stakingDelegate: StakingDelegate = {
type: InstructionBuilderTypes.StakingDelegate,
params: {
fromAddress: stakingDelegateParams.authorizedPubkey.toString() || '',
stakingAddress: stakingDelegateParams.stakePubkey.toString() || '',
validator: stakingDelegateParams.votePubkey.toString() || '',
},
};
instructionData.push(stakingDelegate);
break;
}
}
return instructionData;
}
interface StakingInstructions {
create?: CreateAccountParams;
initialize?: InitializeStakeParams;
delegate?: DelegateStakeParams;
authorize?: AuthorizeStakeParams[];
}
function validateStakingInstructions(stakingInstructions: StakingInstructions) {
if (!stakingInstructions.create) {
throw new NotSupported('Invalid staking activate transaction, missing create stake account instruction');
}
if (!stakingInstructions.initialize && stakingInstructions.delegate) {
return;
} else if (!stakingInstructions.delegate && stakingInstructions.initialize) {
return;
} else if (!stakingInstructions.delegate && !stakingInstructions.initialize) {
// If both are missing something is wrong
throw new NotSupported(
'Invalid staking activate transaction, missing initialize stake account/delegate instruction'
);
}
}
/**
* Parses Solana instructions to create deactivate stake tx instructions params. Supports full stake
* account deactivation and partial stake account deactivation.
*
* When partially deactivating a stake account this method expects the following instructions: Allocate,
* to allocate a new staking account, Assign, to assign the newly created staking account to the
* Stake Program, Split, to split the current stake account, and StakingDeactivate to deactivate the
* newly created stake account.
*
* Supports Nonce, StakingDeactivate, Memo, Allocate, Assign, and Split Solana instructions.
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for staking deactivate tx
*/
function parseStakingDeactivateInstructions(
instructions: TransactionInstruction[],
coinName?: string
): Array<Nonce | StakingDeactivate | Memo> {
const instructionData: Array<Nonce | StakingDeactivate | Memo> = [];
const unstakingInstructions: UnstakingInstructions[] = [];
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.Memo:
const memo: Memo = {
type: InstructionBuilderTypes.Memo,
params: { memo: instruction.data.toString() },
};
instructionData.push(memo);
break;
case ValidInstructionTypesEnum.Allocate:
if (
unstakingInstructions.length > 0 &&
unstakingInstructions[unstakingInstructions.length - 1].allocate === undefined
) {
unstakingInstructions[unstakingInstructions.length - 1].allocate =
SystemInstruction.decodeAllocate(instruction);
} else {
unstakingInstructions.push({
allocate: SystemInstruction.decodeAllocate(instruction),
});
}
break;
case ValidInstructionTypesEnum.Assign:
if (
unstakingInstructions.length > 0 &&
unstakingInstructions[unstakingInstructions.length - 1].assign === undefined
) {
unstakingInstructions[unstakingInstructions.length - 1].assign = SystemInstruction.decodeAssign(instruction);
} else {
unstakingInstructions.push({
assign: SystemInstruction.decodeAssign(instruction),
});
}
break;
case ValidInstructionTypesEnum.Split:
if (
unstakingInstructions.length > 0 &&
unstakingInstructions[unstakingInstructions.length - 1].split === undefined
) {
unstakingInstructions[unstakingInstructions.length - 1].split = StakeInstruction.decodeSplit(instruction);
} else {
unstakingInstructions.push({
split: StakeInstruction.decodeSplit(instruction),
});
}
break;
case ValidInstructionTypesEnum.StakingDeactivate:
if (
unstakingInstructions.length > 0 &&
unstakingInstructions[unstakingInstructions.length - 1].deactivate === undefined
) {
unstakingInstructions[unstakingInstructions.length - 1].deactivate =
StakeInstruction.decodeDeactivate(instruction);
} else {
unstakingInstructions.push({
deactivate: StakeInstruction.decodeDeactivate(instruction),
});
}
break;
case ValidInstructionTypesEnum.Transfer:
if (
unstakingInstructions.length > 0 &&
unstakingInstructions[unstakingInstructions.length - 1].transfer === undefined
) {
unstakingInstructions[unstakingInstructions.length - 1].transfer =
SystemInstruction.decodeTransfer(instruction);
} else {
unstakingInstructions.push({
transfer: SystemInstruction.decodeTransfer(instruction),
});
}
break;
}
}
for (const unstakingInstruction of unstakingInstructions) {
validateUnstakingInstructions(unstakingInstruction);
const stakingDeactivate: StakingDeactivate = {
type: InstructionBuilderTypes.StakingDeactivate,
params: {
fromAddress: unstakingInstruction.deactivate?.authorizedPubkey.toString() || '',
stakingAddress:
unstakingInstruction.split?.stakePubkey.toString() ||
unstakingInstruction.deactivate?.stakePubkey.toString() ||
'',
amount: unstakingInstruction.split?.lamports.toString(),
unstakingAddress: unstakingInstruction.split?.splitStakePubkey.toString(),
isMarinade: unstakingInstruction.deactivate === undefined,
recipients:
unstakingInstruction.deactivate === undefined
? [
{
address: unstakingInstruction.transfer?.toPubkey.toString() || '',
amount: unstakingInstruction.transfer?.lamports.toString() || '',
},
]
: undefined,
},
};
instructionData.push(stakingDeactivate);
}
return instructionData;
}
interface UnstakingInstructions {
allocate?: AllocateParams;
assign?: AssignParams;
split?: SplitStakeParams;
deactivate?: DeactivateStakeParams;
transfer?: DecodedTransferInstruction;
}
function validateUnstakingInstructions(unstakingInstructions: UnstakingInstructions) {
if (!unstakingInstructions.deactivate) {
if (
unstakingInstructions.transfer &&
!unstakingInstructions.allocate &&
!unstakingInstructions.assign &&
!unstakingInstructions.split
) {
return;
}
throw new NotSupported('Invalid deactivate stake transaction, missing deactivate stake account instruction');
} else if (
unstakingInstructions.allocate ||
unstakingInstructions.assign ||
unstakingInstructions.split ||
unstakingInstructions.transfer
) {
if (!unstakingInstructions.allocate) {
throw new NotSupported(
'Invalid partial deactivate stake transaction, missing allocate unstake account instruction'
);
} else if (!unstakingInstructions.assign) {
throw new NotSupported(
'Invalid partial deactivate stake transaction, missing assign unstake account instruction'
);
} else if (!unstakingInstructions.split) {
throw new NotSupported('Invalid partial deactivate stake transaction, missing split stake account instruction');
} else if (
unstakingInstructions.allocate.accountPubkey.toString() !== unstakingInstructions.assign.accountPubkey.toString()
) {
throw new NotSupported(
'Invalid partial deactivate stake transaction, must allocate and assign the same public key'
);
} else if (unstakingInstructions.allocate.space !== StakeProgram.space) {
throw new NotSupported(
`Invalid partial deactivate stake transaction, unstaking account must allocate ${StakeProgram.space} bytes`
);
} else if (unstakingInstructions.assign.programId.toString() !== StakeProgram.programId.toString()) {
throw new NotSupported(
'Invalid partial deactivate stake transaction, the unstake account must be assigned to the Stake Program'
);
} else if (
unstakingInstructions.allocate.accountPubkey.toString() !==
unstakingInstructions.split.splitStakePubkey.toString()
) {
throw new NotSupported('Invalid partial deactivate stake transaction, must allocate the unstaking account');
} else if (
unstakingInstructions.split.stakePubkey.toString() === unstakingInstructions.split.splitStakePubkey.toString()
) {
throw new NotSupported(
'Invalid partial deactivate stake transaction, the unstaking account must be different from the Stake Account'
);
} else if (!unstakingInstructions.transfer) {
throw new NotSupported(
'Invalid partial deactivate stake transaction, missing funding of unstake address instruction'
);
}
}
}
/**
* Parses Solana instructions to create staking withdraw tx instructions params
* Only supports Nonce, StakingWithdraw, and Memo Solana instructions
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for staking withdraw tx
*/
function parseStakingWithdrawInstructions(
instructions: TransactionInstruction[]
): Array<Nonce | StakingWithdraw | Memo> {
const instructionData: Array<Nonce | StakingWithdraw | Memo> = [];
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.Memo:
const memo: Memo = {
type: InstructionBuilderTypes.Memo,
params: { memo: instruction.data.toString() },
};
instructionData.push(memo);
break;
case ValidInstructionTypesEnum.StakingWithdraw:
const withdrawInstruction = StakeInstruction.decodeWithdraw(instruction);
const stakingWithdraw: StakingWithdraw = {
type: InstructionBuilderTypes.StakingWithdraw,
params: {
fromAddress: withdrawInstruction.authorizedPubkey.toString(),
stakingAddress: withdrawInstruction.stakePubkey.toString(),
amount: withdrawInstruction.lamports.toString(),
},
};
instructionData.push(stakingWithdraw);
break;
}
}
return instructionData;
}
/**
* Get the memo object from instructions if it exists
*
* @param {TransactionInstruction[]} instructions - the array of supported Solana instructions to be parsed
* @param {Record<string, number>} instructionIndexes - the instructions indexes of the current transaction
* @returns {Memo | undefined} - memo object or undefined
*/
function getMemo(instructions: TransactionInstruction[], instructionIndexes: Record<string, number>): Memo | undefined {
const instructionsLength = Object.keys(instructionIndexes).length;
if (instructions.length === instructionsLength && instructions[instructionIndexes.Memo]) {
return {
type: InstructionBuilderTypes.Memo,
params: { memo: instructions[instructionIndexes.Memo].data.toString() },
};
}
}
const ataInitInstructionKeysIndexes = {
PayerAddress: 0,
ATAAddress: 1,
OwnerAddress: 2,
MintAddress: 3,
};
const closeAtaInstructionKeysIndexes = {
AccountAddress: 0,
DestinationAddress: 1,
AuthorityAddress: 2,
};
/**
* Parses Solana instructions to initialize associated token account tx instructions params
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for Send tx
*/
function parseAtaInitInstructions(instructions: TransactionInstruction[]): Array<AtaInit | Memo | Nonce> {
const instructionData: Array<AtaInit | Memo | Nonce> = [];
let memo: Memo | undefined;
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.Memo:
memo = { type: InstructionBuilderTypes.Memo, params: { memo: instruction.data.toString() } };
break;
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.InitializeAssociatedTokenAccount:
const mintAddress = instruction.keys[ataInitInstructionKeysIndexes.MintAddress].pubkey.toString();
const tokenName = findTokenName(mintAddress);
const ataInit: AtaInit = {
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
params: {
mintAddress,
ataAddress: instruction.keys[ataInitInstructionKeysIndexes.ATAAddress].pubkey.toString(),
ownerAddress: instruction.keys[ataInitInstructionKeysIndexes.OwnerAddress].pubkey.toString(),
payerAddress: instruction.keys[ataInitInstructionKeysIndexes.PayerAddress].pubkey.toString(),
tokenName,
},
};
instructionData.push(ataInit);
break;
default:
throw new NotSupported(
'Invalid transaction, instruction type not supported: ' + getInstructionType(instruction)
);
}
}
if (memo) {
instructionData.push(memo);
}
return instructionData;
}
const ataCloseInstructionKeysIndexes = {
AccountAddress: 0,
DestinationAddress: 1,
AuthorityAddress: 2,
};
/**
* Parses Solana instructions to close associated token account tx instructions params
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for Send tx
*/
function parseAtaCloseInstructions(instructions: TransactionInstruction[]): Array<AtaClose | Nonce> {
const instructionData: Array<AtaClose | Nonce> = [];
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.CloseAssociatedTokenAccount:
const ataClose: AtaClose = {
type: InstructionBuilderTypes.CloseAssociatedTokenAccount,
params: {
accountAddress: instruction.keys[ataCloseInstructionKeysIndexes.AccountAddress].pubkey.toString(),
destinationAddress: instruction.keys[ataCloseInstructionKeysIndexes.DestinationAddress].pubkey.toString(),
authorityAddress: instruction.keys[ataCloseInstructionKeysIndexes.AuthorityAddress].pubkey.toString(),
},
};
instructionData.push(ataClose);
break;
default:
throw new NotSupported(
'Invalid transaction, instruction type not supported: ' + getInstructionType(instruction)
);
}
}
return instructionData;
}
/**
* Parses Solana instructions to authorized staking account params
* Only supports Nonce, Authorize instructions
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for staking authorize tx
*/
function parseStakingAuthorizeInstructions(
instructions: TransactionInstruction[]
): Array<Nonce | StakingAuthorize | Memo> {
const instructionData: Array<Nonce | StakingAuthorize | Memo> = [];
for (const instruction of instructions) {
const type = getInstructionType(instruction);
switch (type) {
case ValidInstructionTypesEnum.AdvanceNonceAccount:
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instruction);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
break;
case ValidInstructionTypesEnum.Memo:
const memo: Memo = { type: InstructionBuilderTypes.Memo, params: { memo: instruction.data.toString() } };
instructionData.push(memo);
break;
case ValidInstructionTypesEnum.Authorize:
const authorize = StakeInstruction.decodeAuthorize(instruction);
instructionData.push({
type: InstructionBuilderTypes.StakingAuthorize,
params: {
stakingAddress: authorize.stakePubkey.toString(),
oldAuthorizeAddress: authorize.authorizedPubkey.toString(),
newAuthorizeAddress: authorize.newAuthorizedPubkey.toString(),
newWithdrawAddress: authorize.custodianPubkey?.toString() || '',
},
});
break;
}
}
return instructionData;
}
/**
* Parses Solana instructions to authorized staking account params
* Only supports Nonce, Authorize instructions
*
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
* @returns {InstructionParams[]} An array containing instruction params for staking authorize tx
*/
function parseStakingAuthorizeRawInstructions(instructions: TransactionInstruction[]): Array<Nonce | StakingAuthorize> {
const instructionData: Array<Nonce | StakingAuthorize> = [];
assert(instructions.length === 2, 'Invalid number of instructions');
const advanceNonceInstruction = SystemInstruction.decodeNonceAdvance(instructions[0]);
const nonce: Nonce = {
type: InstructionBuilderTypes.NonceAdvance,
params: {
walletNonceAddress: advanceNonceInstruction.noncePubkey.toString(),
authWalletAddress: advanceNonceInstruction.authorizedPubkey.toString(),
},
};
instructionData.push(nonce);
const authorize = instructions[1];
assert(authorize.keys.length === 5, 'Invalid number of keys in authorize instruction');
instructionData.push({
type: InstructionBuilderTypes.StakingAuthorize,
params: {
stakingAddress: authorize.keys[0].pubkey.toString(),
oldAuthorizeAddress: authorize.keys[2].pubkey.toString(),
newAuthorizeAddress: authorize.keys[3].pubkey.toString(),
custodianAddress: authorize.keys[4].pubkey.toString(),
},
});
return instructionData;
}
function findTokenName(mintAddress: string): string {
let token: string | undefined;
coins.forEach((value, key) => {
if (value instanceof SolCoin && value.tokenAddress === mintAddress) {
token = value.name;
}
});
assert(token);
return token;
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!