PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-coin-sol/src/lib

Просмотр файла: solInstructionFactory.ts

import { coins, SolCoin } from '@bitgo/statics';
import {
  createAssociatedTokenAccountInstruction,
  createCloseAccountInstruction,
  createTransferCheckedInstruction,
} from '@solana/spl-token';
import {
  Authorized,
  Lockup,
  PublicKey,
  StakeAuthorizationLayout,
  StakeProgram,
  SystemProgram,
  Transaction,
  TransactionInstruction,
  ComputeBudgetProgram,
} from '@solana/web3.js';
import assert from 'assert';
import BigNumber from 'bignumber.js';
import { InstructionBuilderTypes, MEMO_PROGRAM_PK } from './constants';
import {
  AtaClose,
  AtaInit,
  InstructionParams,
  Memo,
  Nonce,
  StakingActivate,
  StakingAuthorize,
  StakingDeactivate,
  StakingDelegate,
  StakingWithdraw,
  TokenTransfer,
  Transfer,
  WalletInit,
  SetPriorityFee,
} from './iface';

/**
 * Construct Solana instructions from instructions params
 *
 * @param {InstructionParams} instructionToBuild - the data containing the instruction params
 * @returns {TransactionInstruction[]} An array containing supported Solana instructions
 */
export function solInstructionFactory(instructionToBuild: InstructionParams): TransactionInstruction[] {
  switch (instructionToBuild.type) {
    case InstructionBuilderTypes.NonceAdvance:
      return advanceNonceInstruction(instructionToBuild);
    case InstructionBuilderTypes.Memo:
      return memoInstruction(instructionToBuild);
    case InstructionBuilderTypes.Transfer:
      return transferInstruction(instructionToBuild);
    case InstructionBuilderTypes.TokenTransfer:
      return tokenTransferInstruction(instructionToBuild);
    case InstructionBuilderTypes.CreateNonceAccount:
      return createNonceAccountInstruction(instructionToBuild);
    case InstructionBuilderTypes.StakingActivate:
      return stakingInitializeInstruction(instructionToBuild);
    case InstructionBuilderTypes.StakingDeactivate:
      return stakingDeactivateInstruction(instructionToBuild);
    case InstructionBuilderTypes.StakingWithdraw:
      return stakingWithdrawInstruction(instructionToBuild);
    case InstructionBuilderTypes.CreateAssociatedTokenAccount:
      return createATAInstruction(instructionToBuild);
    case InstructionBuilderTypes.CloseAssociatedTokenAccount:
      return closeATAInstruction(instructionToBuild);
    case InstructionBuilderTypes.StakingAuthorize:
      return stakingAuthorizeInstruction(instructionToBuild);
    case InstructionBuilderTypes.StakingDelegate:
      return stakingDelegateInstruction(instructionToBuild);
    case InstructionBuilderTypes.SetPriorityFee:
      return fetchPriorityFeeInstruction(instructionToBuild);
    default:
      throw new Error(`Invalid instruction type or not supported`);
  }
}

/**
 * Construct Advance Nonce Solana instructions
 *
 * @param {Nonce} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Advance Nonce Solana instruction
 */
function advanceNonceInstruction(data: Nonce): TransactionInstruction[] {
  const {
    params: { authWalletAddress, walletNonceAddress },
  } = data;
  assert(authWalletAddress, 'Missing authWalletAddress param');
  assert(walletNonceAddress, 'Missing walletNonceAddress param');
  const nonceInstruction = SystemProgram.nonceAdvance({
    noncePubkey: new PublicKey(walletNonceAddress),
    authorizedPubkey: new PublicKey(authWalletAddress),
  });
  return [nonceInstruction];
}

function fetchPriorityFeeInstruction(instructionToBuild: SetPriorityFee): TransactionInstruction[] {
  const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: instructionToBuild.params.fee,
  });

  return [addPriorityFee];
}

/**
 * Construct Memo Solana instructions
 *
 * @param {Memo} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Memo Solana instruction
 */
function memoInstruction(data: Memo): TransactionInstruction[] {
  const {
    params: { memo },
  } = data;
  assert(memo, 'Missing memo param');
  const memoInstruction = new TransactionInstruction({
    keys: [],
    programId: new PublicKey(MEMO_PROGRAM_PK),
    data: Buffer.from(memo),
  });
  return [memoInstruction];
}

/**
 * Construct Transfer Solana instructions
 *
 * @param {Transfer} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Transfer Solana instruction
 */
function transferInstruction(data: Transfer): TransactionInstruction[] {
  const {
    params: { fromAddress, toAddress, amount },
  } = data;
  assert(fromAddress, 'Missing fromAddress param');
  assert(toAddress, 'Missing toAddress param');
  assert(amount, 'Missing toAddress param');
  const transferInstruction = SystemProgram.transfer({
    fromPubkey: new PublicKey(fromAddress),
    toPubkey: new PublicKey(toAddress),
    lamports: parseInt(amount, 10),
  });
  return [transferInstruction];
}

/**
 * Construct Transfer Solana instructions
 *
 * @param {Transfer} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Transfer Solana instruction
 */
function tokenTransferInstruction(data: TokenTransfer): TransactionInstruction[] {
  const {
    params: { fromAddress, toAddress, amount, tokenName, sourceAddress },
  } = data;
  assert(fromAddress, 'Missing fromAddress (owner) param');
  assert(toAddress, 'Missing toAddress param');
  assert(amount, 'Missing amount param');
  assert(tokenName, 'Missing token name');
  assert(sourceAddress, 'Missing ata address');
  const token = coins.get(data.params.tokenName);
  assert(token instanceof SolCoin);
  const transferInstruction = createTransferCheckedInstruction(
    new PublicKey(sourceAddress),
    new PublicKey(token.tokenAddress),
    new PublicKey(toAddress),
    new PublicKey(fromAddress),
    BigInt(amount),
    token.decimalPlaces
  );
  return [transferInstruction];
}

/**
 * Construct Create and Initialize Nonce Solana instructions
 *
 * @param {WalletInit} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Create and Initialize Nonce Solana instruction
 */
function createNonceAccountInstruction(data: WalletInit): TransactionInstruction[] {
  const {
    params: { fromAddress, nonceAddress, authAddress, amount },
  } = data;
  assert(fromAddress, 'Missing fromAddress param');
  assert(nonceAddress, 'Missing nonceAddress param');
  assert(authAddress, 'Missing authAddress param');
  assert(amount, 'Missing amount param');
  const nonceAccountInstruction = SystemProgram.createNonceAccount({
    fromPubkey: new PublicKey(fromAddress),
    noncePubkey: new PublicKey(nonceAddress),
    authorizedPubkey: new PublicKey(authAddress),
    lamports: new BigNumber(amount).toNumber(),
  });
  return nonceAccountInstruction.instructions;
}

/**
 * Construct Create Staking Account and Delegate Solana instructions
 *
 * @param {StakingActivate} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Create Staking Account and Delegate Solana instructions
 */
function stakingInitializeInstruction(data: StakingActivate): TransactionInstruction[] {
  const {
    params: { fromAddress, stakingAddress, amount, validator, isMarinade },
  } = data;
  assert(fromAddress, 'Missing fromAddress param');
  assert(stakingAddress, 'Missing stakingAddress param');
  assert(amount, 'Missing amount param');
  assert(validator, 'Missing validator param');
  assert(isMarinade !== undefined, 'Missing isMarinade param');

  const fromPubkey = new PublicKey(fromAddress);
  const stakePubkey = new PublicKey(stakingAddress);
  const validatorPubkey = new PublicKey(validator);
  const tx = new Transaction();

  const stakerPubkey = isMarinade ? validatorPubkey : fromPubkey;
  const walletInitStaking = StakeProgram.createAccount({
    fromPubkey,
    stakePubkey,
    authorized: new Authorized(stakerPubkey, fromPubkey), // staker and withdrawer
    lockup: new Lockup(0, 0, fromPubkey), // No minimum epoch to withdraw
    lamports: new BigNumber(amount).toNumber(),
  });
  tx.add(walletInitStaking);

  if (!isMarinade) {
    const delegateStaking = StakeProgram.delegate({
      stakePubkey: new PublicKey(stakingAddress),
      authorizedPubkey: new PublicKey(fromAddress),
      votePubkey: new PublicKey(validator),
    });
    tx.add(delegateStaking);
  }

  return tx.instructions;
}

/**
 * Construct staking deactivate Solana instructions
 *
 * @param {StakingDeactivate} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing staking deactivate instruction
 */
function stakingDeactivateInstruction(data: StakingDeactivate): TransactionInstruction[] {
  const {
    params: { fromAddress, stakingAddress, isMarinade, recipients },
  } = data;
  assert(fromAddress, 'Missing fromAddress param');
  if (!isMarinade) {
    assert(stakingAddress, 'Missing stakingAddress param');
  }

  if (isMarinade) {
    const tx = new Transaction();
    assert(recipients, 'Missing recipients param');
    const toPubkeyAddress = new PublicKey(recipients[0].address || '');
    const transferInstruction = SystemProgram.transfer({
      fromPubkey: new PublicKey(fromAddress),
      toPubkey: toPubkeyAddress,
      lamports: parseInt(recipients[0].amount, 10),
    });

    tx.add(transferInstruction);
    return tx.instructions;
  }

  if (data.params.amount && data.params.unstakingAddress) {
    const tx = new Transaction();
    const unstakingAddress = new PublicKey(data.params.unstakingAddress);

    const allocateAccount = SystemProgram.allocate({
      accountPubkey: unstakingAddress,
      space: StakeProgram.space,
    });
    tx.add(allocateAccount);

    const assignAccount = SystemProgram.assign({
      accountPubkey: unstakingAddress,
      programId: StakeProgram.programId,
    });
    tx.add(assignAccount);

    const splitStake = StakeProgram.split(
      {
        stakePubkey: new PublicKey(stakingAddress),
        authorizedPubkey: new PublicKey(fromAddress),
        splitStakePubkey: unstakingAddress,
        lamports: new BigNumber(data.params.amount).toNumber(),
      },
      0
    );
    tx.add(splitStake.instructions[1]);

    const deactivateStaking = StakeProgram.deactivate({
      stakePubkey: unstakingAddress,
      authorizedPubkey: new PublicKey(fromAddress),
    });
    tx.add(deactivateStaking);

    return tx.instructions;
  } else {
    const deactivateStaking = StakeProgram.deactivate({
      stakePubkey: new PublicKey(stakingAddress),
      authorizedPubkey: new PublicKey(fromAddress),
    });

    return deactivateStaking.instructions;
  }
}

/**
 * Construct Staking Withdraw Solana instructions
 *
 * @param {StakingWithdraw} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Staking Withdraw  Solana instructions
 */
function stakingWithdrawInstruction(data: StakingWithdraw): TransactionInstruction[] {
  const {
    params: { fromAddress, stakingAddress, amount },
  } = data;
  assert(fromAddress, 'Missing fromAddress param');
  assert(stakingAddress, 'Missing stakingAddress param');
  assert(amount, 'Missing amount param');

  const withdrawStaking = StakeProgram.withdraw({
    stakePubkey: new PublicKey(stakingAddress),
    authorizedPubkey: new PublicKey(fromAddress),
    toPubkey: new PublicKey(fromAddress),
    lamports: new BigNumber(amount).toNumber(),
  });

  return withdrawStaking.instructions;
}

/**
 * Construct Create and Initialize Nonce Solana instructions
 *
 * @param {WalletInit} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Create and Initialize Nonce Solana instruction
 */
function createATAInstruction(data: AtaInit): TransactionInstruction[] {
  const {
    params: { mintAddress, ataAddress, ownerAddress, payerAddress },
  } = data;
  assert(mintAddress, 'Missing mintAddress param');
  assert(ataAddress, 'Missing ataAddress param');
  assert(ownerAddress, 'Missing ownerAddress param');
  assert(payerAddress, 'Missing payerAddress param');

  const associatedTokenAccountInstruction = createAssociatedTokenAccountInstruction(
    new PublicKey(payerAddress),
    new PublicKey(ataAddress),
    new PublicKey(ownerAddress),
    new PublicKey(mintAddress)
  );
  return [associatedTokenAccountInstruction];
}

/**
 * Construct Close ATA Solana instructions
 *
 * @param {WalletInit} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Close ATA Solana instruction
 */
function closeATAInstruction(data: AtaClose): TransactionInstruction[] {
  const {
    params: { accountAddress, destinationAddress, authorityAddress },
  } = data;
  assert(accountAddress, 'Missing accountAddress param');
  assert(destinationAddress, 'Missing destinationAddress param');
  assert(authorityAddress, 'Missing authorityAddress param');

  const closeAssociatedTokenAccountInstruction = createCloseAccountInstruction(
    new PublicKey(accountAddress),
    new PublicKey(destinationAddress),
    new PublicKey(authorityAddress)
  );
  return [closeAssociatedTokenAccountInstruction];
}

/**
 * Construct Staking Account Authorize Solana instructions
 *
 * @param {StakingAuthorize} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Staking Account Authorize instructions
 */
function stakingAuthorizeInstruction(data: StakingAuthorize): TransactionInstruction[] {
  const {
    params: { stakingAddress, oldAuthorizeAddress, newAuthorizeAddress, newWithdrawAddress },
  } = data;
  assert(stakingAddress, 'Missing stakingAddress param');
  assert(oldAuthorizeAddress, 'Missing oldAuthorizeAddress param');
  assert(newAuthorizeAddress, 'Missing newAuthorizeAddress param');
  assert(newWithdrawAddress, 'Missing newWithdrawAddress param');

  const tx = new Transaction();

  const authorizeStaking = StakeProgram.authorize({
    stakePubkey: new PublicKey(stakingAddress),
    authorizedPubkey: new PublicKey(oldAuthorizeAddress),
    newAuthorizedPubkey: new PublicKey(newAuthorizeAddress),
    stakeAuthorizationType: StakeAuthorizationLayout.Staker,
  });

  const authorizeWithdraw = StakeProgram.authorize({
    stakePubkey: new PublicKey(stakingAddress),
    authorizedPubkey: new PublicKey(oldAuthorizeAddress),
    newAuthorizedPubkey: new PublicKey(newAuthorizeAddress),
    stakeAuthorizationType: StakeAuthorizationLayout.Withdrawer,
    custodianPubkey: new PublicKey(newWithdrawAddress),
  });
  tx.add(authorizeStaking);
  tx.add(authorizeWithdraw);

  return tx.instructions;
}

/**
 * Construct Delegate Solana instructions
 *
 * @param {StakingActivate} data - the data to build the instruction
 * @returns {TransactionInstruction[]} An array containing Delegate Solana instructions
 */
function stakingDelegateInstruction(data: StakingDelegate): TransactionInstruction[] {
  const {
    params: { fromAddress, stakingAddress, validator },
  } = data;
  assert(fromAddress, 'Missing fromAddress param');
  assert(stakingAddress, 'Missing stakingAddress param');
  assert(validator, 'Missing validator param');
  const tx = new Transaction();
  const delegateStaking = StakeProgram.delegate({
    stakePubkey: new PublicKey(stakingAddress),
    authorizedPubkey: new PublicKey(fromAddress),
    votePubkey: new PublicKey(validator),
  });
  tx.add(delegateStaking);

  return tx.instructions;
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!