All files / src/transactions/builders make-stx-token-transfer.ts

79.03% Statements 49/62
84.21% Branches 16/19
75% Functions 3/4
79.03% Lines 49/62

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 1729x             9x 9x 9x 9x 9x   9x           9x 9x 9x             9x 9x                     9x       18x                 18x   18x   18x 18x   18x   14x               4x                 18x 3x   15x     18x 18x 1x 1x       18x 18x                   18x       18x 1x 1x     18x   1x     1x       1x 1x       18x                       9x     13x 13x   13x 13x 13x   13x 13x   13x                                        
import { StacksTransaction } from '../transaction';
import {
  createStacksPrivateKey,
  getPublicKeyFromStacksPrivateKey,
  pubKeyfromPrivKey,
  publicKeyFromBuffer,
  publicKeyToString,
} from '../keys';
import { bytesToHex, hexToBytes, omit, TransactionVersion } from 'micro-stacks/common';
import { TransactionSigner } from '../signer';
import { StacksMainnet } from 'micro-stacks/network';
import { AddressHashMode, PostConditionMode } from '../common/constants';
import { PostCondition } from '../postcondition';
import { createTokenTransferPayload } from '../payload';
import {
  createMultiSigSpendingCondition,
  createSingleSigSpendingCondition,
  createSponsoredAuth,
  createStandardAuth,
} from '../authorization';
import { createLPList } from '../types';
import { c32address, StacksNetworkVersion } from 'micro-stacks/crypto';
import {
  SignedMultiSigTokenTransferOptions,
  SignedTokenTransferOptions,
  UnsignedMultiSigTokenTransferOptions,
  UnsignedTokenTransferOptions,
} from './types';
import { getNonce } from '../fetchers/get-nonce';
import { estimateTransfer } from '../fetchers/estimate-stx-transfer';
 
/**
 * Generates an unsigned Stacks token transfer transaction
 *
 * Returns a Stacks token transfer transaction.
 *
 * @param  {UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions} txOptions - an options object for the token transfer
 *
 * @return {Promise<StacksTransaction>}
 */
export async function makeUnsignedSTXTokenTransfer(
  txOptions: UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions,
  sync?: boolean
): Promise<StacksTransaction> {
  const defaultOptions = {
    fee: BigInt(0),
    nonce: BigInt(0),
    network: new StacksMainnet(),
    postConditionMode: PostConditionMode.Deny,
    memo: '',
    sponsored: false,
  };
 
  const options = Object.assign(defaultOptions, txOptions);
 
  const payload = createTokenTransferPayload(options.recipient, options.amount, options.memo);
 
  let authorization = null;
  let spendingCondition = null;
 
  if ('publicKey' in options) {
    // single-sig
    spendingCondition = createSingleSigSpendingCondition(
      AddressHashMode.SerializeP2PKH,
      options.publicKey,
      options.nonce,
      options.fee
    );
  } else {
    // multi-sig
    spendingCondition = createMultiSigSpendingCondition(
      AddressHashMode.SerializeP2SH,
      options.numSignatures,
      options.publicKeys,
      options.nonce,
      options.fee
    );
  }
 
  if (options.sponsored) {
    authorization = createSponsoredAuth(spendingCondition);
  } else {
    authorization = createStandardAuth(spendingCondition);
  }
 
  const postConditions: PostCondition[] = [];
  if (options.postConditions && options.postConditions.length > 0) {
    options.postConditions.forEach(postCondition => {
      postConditions.push(postCondition);
    });
  }
 
  const lpPostConditions = createLPList(postConditions);
  const transaction = new StacksTransaction(
    options.network.version,
    authorization,
    payload,
    lpPostConditions,
    options.postConditionMode,
    options.anchorMode,
    options.network.chainId
  );
 
  Iif (sync) {
    transaction.setFee(0);
    transaction.setNonce(0);
  } else {
    if (txOptions.fee === undefined || txOptions.fee === null) {
      const txFee = await estimateTransfer(transaction, options.network);
      transaction.setFee(txFee);
    }
 
    if (txOptions.nonce === undefined || txOptions.nonce === null) {
      const addressVersion =
        options.network.version === TransactionVersion.Mainnet
          ? StacksNetworkVersion.mainnetP2PKH
          : StacksNetworkVersion.testnetP2PKH;
      const senderAddress = c32address(
        addressVersion,
        hexToBytes(transaction.auth.spendingCondition!.signer)
      );
      const txNonce = await getNonce(senderAddress, options.network);
      transaction.setNonce(txNonce);
    }
  }
 
  return transaction;
}
 
/**
 * Generates a signed Stacks token transfer transaction
 *
 * Returns a signed Stacks token transfer transaction.
 *
 * @param  {SignedTokenTransferOptions | SignedMultiSigTokenTransferOptions} txOptions - an options object for the token transfer
 *
 * @return {StacksTransaction}
 */
export async function makeSTXTokenTransfer(
  txOptions: SignedTokenTransferOptions | SignedMultiSigTokenTransferOptions
): Promise<StacksTransaction> {
  if ('senderKey' in txOptions) {
    const privKey = createStacksPrivateKey(txOptions.senderKey);
 
    const publicKey = publicKeyToString(getPublicKeyFromStacksPrivateKey(privKey));
    const options = omit(txOptions, 'senderKey');
    const transaction = await makeUnsignedSTXTokenTransfer({ publicKey, ...options });
 
    const signer = new TransactionSigner(transaction);
    await signer.signOrigin(privKey);
 
    return transaction;
  } else E{
    const options = omit(txOptions, 'signerKeys');
    const transaction = await makeUnsignedSTXTokenTransfer(options);
 
    const signer = new TransactionSigner(transaction);
    let pubKeys = txOptions.publicKeys;
    for (const key of txOptions.signerKeys) {
      const pubKey = pubKeyfromPrivKey(key);
      pubKeys = pubKeys.filter(pk => pk !== bytesToHex(pubKey.data));
      await signer.signOrigin(createStacksPrivateKey(key));
    }
 
    for (const key of pubKeys) {
      signer.appendOrigin(publicKeyFromBuffer(hexToBytes(key)));
    }
 
    return transaction;
  }
}