All files / src/transactions/builders sponsor-transaction.ts

87.17% Statements 34/39
76.47% Branches 13/17
100% Functions 1/1
86.84% Lines 33/38

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  9x 9x 9x 9x 9x 9x 9x 9x   9x 9x 9x 9x                     9x     5x           5x 5x         5x   5x 1x     1x 1x                           1x 1x     5x   1x       1x 1x 1x     5x             5x   5x         5x   5x    
import { StacksTransaction } from '../transaction';
import { IntegerType, TransactionVersion } from 'micro-stacks/common';
import { AddressHashMode, SingleSigHashMode } from '../common/constants';
import { StacksMainnet, StacksTestnet } from 'micro-stacks/network';
import { createStacksPrivateKey, pubKeyfromPrivKey, publicKeyToString } from '../keys';
import { PayloadType } from '../payload';
import { publicKeyToStxAddress, StacksNetworkVersion } from 'micro-stacks/crypto';
import { createSingleSigSpendingCondition } from '../authorization';
import { TransactionSigner } from '../signer';
import { SponsorOptionsOpts } from './types';
import { getNonce } from '../fetchers/get-nonce';
import { estimateTransfer } from '../fetchers/estimate-stx-transfer';
import { estimateContractFunctionCall } from '../fetchers/estimate-contract-function-call';
import { estimateContractDeploy } from '../fetchers/estimate-contract-deploy';
 
/**
 * Constructs and signs a sponsored transaction as the sponsor
 *
 * @param  {SponsorOptionsOpts} sponsorOptions - the sponsor options object
 *
 * Returns a signed sponsored transaction.
 *
 * @return {ClarityValue}
 */
export async function sponsorTransaction(
  sponsorOptions: SponsorOptionsOpts
): Promise<StacksTransaction> {
  const defaultOptions = {
    fee: 0 as IntegerType,
    sponsorNonce: 0 as IntegerType,
    sponsorAddressHashmode: AddressHashMode.SerializeP2PKH as SingleSigHashMode,
  };
 
  const options = Object.assign(defaultOptions, sponsorOptions);
  const network =
    sponsorOptions.network ??
    (options.transaction.version === TransactionVersion.Mainnet
      ? new StacksMainnet()
      : new StacksTestnet());
  const sponsorPubKey = pubKeyfromPrivKey(options.sponsorPrivateKey);
 
  if (sponsorOptions.fee === undefined || sponsorOptions.fee === null) {
    let txFee = BigInt(0);
    switch (options.transaction.payload.payloadType) {
      case PayloadType.TokenTransfer:
        txFee = await estimateTransfer(options.transaction, network);
        break;
      case PayloadType.SmartContract:
        txFee = await estimateContractDeploy(options.transaction, network);
        break;
      case PayloadType.ContractCall:
        txFee = await estimateContractFunctionCall(options.transaction, network);
        break;
      default:
        throw new Error(
          `Sponsored transactions not supported for transaction type ${
            PayloadType[options.transaction.payload.payloadType]
          }`
        );
    }
    options.transaction.setFee(txFee);
    options.fee = txFee;
  }
 
  if (sponsorOptions.sponsorNonce === undefined || sponsorOptions.sponsorNonce === null) {
    const addressVersion =
      network.version === TransactionVersion.Mainnet
        ? StacksNetworkVersion.mainnetP2PKH
        : StacksNetworkVersion.testnetP2PKH;
 
    const senderAddress = publicKeyToStxAddress(publicKeyToString(sponsorPubKey), addressVersion);
    const sponsorNonce = await getNonce(senderAddress, network);
    options.sponsorNonce = sponsorNonce;
  }
 
  const sponsorSpendingCondition = createSingleSigSpendingCondition(
    options.sponsorAddressHashmode,
    publicKeyToString(sponsorPubKey),
    options.sponsorNonce,
    options.fee
  );
 
  options.transaction.setSponsor(sponsorSpendingCondition);
 
  const privKey = createStacksPrivateKey(options.sponsorPrivateKey);
  const signer = TransactionSigner.createSponsorSigner(
    options.transaction,
    sponsorSpendingCondition
  );
  await signer.signSponsor(privKey);
 
  return signer.transaction;
}