# Transaction Utils

```typescript
// transaction/util.ts

import {
  EVMGasType,
  getEVMGasTypeForTransaction,
  NetworkName,
  RailgunERC20AmountRecipient,
  type RailgunERC20Amount,
  type RailgunNFTAmount,
  type RailgunNFTAmountRecipient,
  type TransactionGasDetails,
} from "@railgun-community/shared-models";
import {
  getShieldPrivateKeySignatureMessage,
  NFTTokenType,
} from "@railgun-community/wallet";
import { keccak256, type HDNodeWallet, type Wallet } from "ethers";
import { TEST_NETWORK } from "../utils/constants";
import { getProviderWallet } from "../utils/provider";

/**
 * Generates a shield private key signature by signing a predefined message with the provided wallet
 * and then hashing the signature with keccak256.
 *
 * @param wallet - The wallet (Wallet or HDNodeWallet) used to sign the shield signature message
 * @returns A Promise that resolves to the shield private key signature as a hex string
 */
export const getShieldSignature = async (
  wallet: Wallet | HDNodeWallet
): Promise<string> => {
  const shieldSignatureMessage = getShieldPrivateKeySignatureMessage();
  const shieldPrivateKey = keccak256(
    await wallet.signMessage(shieldSignatureMessage)
  );
  return shieldPrivateKey;
};

/**
 * Serializes token address and amount into a RailgunERC20Amount format for relay adapt unshield transactions.
 *
 * @param tokenAddress - The Ethereum address of the ERC20 token
 * @param amount - The token amount as a BigInt value
 * @returns A RailgunERC20Amount object containing the token address and amount
 */
export const serializeERC20RelayAdaptUnshield = (
  tokenAddress: string,
  amount: bigint
): RailgunERC20Amount => {
  return {
    tokenAddress,
    amount,
  };
};

/**
 * Serializes an ERC721 token for relay adaptation to unshield it.
 *
 * This function creates a RailgunNFTAmount object that represents an ERC721 token
 * with the specified address and token ID. The amount is always set to 1 since
 * ERC721 tokens are non-fungible and can only be transferred as whole units.
 *
 * @param tokenAddress - The contract address of the ERC721 token.
 * @param tokenSubID - The unique identifier of the specific ERC721 token.
 * @returns A RailgunNFTAmount object configured for ERC721 unshielding.
 */
export const serializeERC721RelayAdaptUnshield = (
  tokenAddress: string,
  tokenSubID: string
): RailgunNFTAmount => {
  return {
    nftAddress: tokenAddress,
    amount: 1n,
    tokenSubID,
    nftTokenType: NFTTokenType.ERC721,
  };
};

/**
 * Serializes ERC20 transfer data into a RailgunERC20AmountRecipient object.
 *
 * @param tokenAddress - The address of the ERC20 token contract
 * @param amount - The amount of tokens to transfer as a bigint
 * @param recipient - The address of the transfer recipient
 * @returns A RailgunERC20AmountRecipient object containing the transfer details
 */
export const serializeERC20Transfer = (
  tokenAddress: string,
  amount: bigint,
  recipient: string
): RailgunERC20AmountRecipient => {
  return {
    tokenAddress,
    amount,
    recipientAddress: recipient,
  };
};

/**
 * Serializes an ERC721 NFT transfer into a RailgunNFTAmountRecipient object.
 *
 * @param nftAddress - The contract address of the ERC721 NFT
 * @param tokenSubID - The token ID of the ERC721 NFT
 * @param recipient - The address of the recipient who will receive the NFT
 * @returns A RailgunNFTAmountRecipient object representing the ERC721 transfer with amount always set to 1n
 */
export const serializeERC721Transfer = (
  nftAddress: string,
  tokenSubID: string,
  recipient: string
): RailgunNFTAmountRecipient => {
  return {
    nftAddress,
    amount: 1n, // shield amount - always 1n for ERC-721
    tokenSubID,
    nftTokenType: NFTTokenType.ERC721,
    recipientAddress: recipient,
  };
};

export const getOriginalGasDetailsForTransaction = async (
  network: NetworkName,
  sendWithPublicWallet: boolean
): Promise<TransactionGasDetails> => {
  // MOCK HANDLE WALLET MANAGEMENT AND GAS ESTIMATES
  const { wallet } = getProviderWallet();
  const gasDetails = await getGasDetailsForTransaction(
    network,
    0n,
    sendWithPublicWallet,
    wallet
  );
  return gasDetails;
};

/**
 * Retrieves gas details for a transaction based on network and wallet information.
 *
 * This function determines the appropriate EVM gas type for the transaction and
 * creates a structured gas details object with the necessary gas parameters.
 * For Type0 and Type1 transactions, it returns gasPrice, while for Type2 transactions,
 * it returns maxFeePerGas and maxPriorityFeePerGas according to EIP-1559.
 *
 * @param network - The blockchain network name to perform the transaction on
 * @param gasEstimate - The estimated gas amount required for the transaction as a bigint
 * @param sendWithPublicWallet - Indicates whether the transaction is being sent from a public wallet
 * @param wallet - The wallet instance used to sign and populate the transaction
 *
 * @returns A promise that resolves to a TransactionGasDetails object containing appropriate gas parameters
 * for the specified network and transaction type
 *
 * @example
 * const gasDetails = await getGasDetailsForTransaction(
 *   'ethereum',
 *   250000n,
 *   false,
 *   myWallet
 * );
 */
export const getGasDetailsForTransaction = async (
  network: NetworkName,
  gasEstimate: bigint,
  sendWithPublicWallet: boolean,
  wallet: Wallet | HDNodeWallet
) => {
  const evmGasType: EVMGasType = getEVMGasTypeForTransaction(
    network,
    sendWithPublicWallet
  );

  let gasDetails: TransactionGasDetails;

  // populate tx
  // send 1 wei to self. get gas details
  // THIS IS AN INSECURE WAY TO GET GAS ESTIMATE
  // DO NOT USE IN PRODUCTION
  const { maxFeePerGas, maxPriorityFeePerGas } =
    await wallet.populateTransaction({
      to: wallet.address,
      value: 1n,
    });

  switch (evmGasType) {
    case EVMGasType.Type0:
    case EVMGasType.Type1:
      gasDetails = {
        evmGasType,
        gasEstimate,
        gasPrice: BigInt(maxFeePerGas?.valueOf() ?? 0), // Proper calculation of network gasPrice is not covered in this guide
      };
      break;
    case EVMGasType.Type2:
      // Proper calculation of gas Max Fee and gas Max Priority Fee is not covered in this guide. See: https://docs.alchemy.com/docs/how-to-build-a-gas-fee-estimator-using-eip-1559

      gasDetails = {
        evmGasType,
        gasEstimate,
        maxFeePerGas: BigInt(maxFeePerGas?.valueOf() ?? 0),
        maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas?.valueOf() ?? 0),
      };
      break;
  }
  return gasDetails;
};

// TEST_ function examples are 'real world usage examples' 
// as to how it can be consumed in your application.
export const TEST_gasDetails = async () => {
  const { wallet } = getProviderWallet();
  const gasDetails = await getGasDetailsForTransaction(
    TEST_NETWORK,
    21000n,
    true,
    wallet
  );
  console.log("gasDetails", gasDetails);
};

process.on("uncaughtException", (err, origin) => {
  console.log("Uncaught Error", err, origin);
});

process.on("unhandledRejection", async (reason, promise) => {
  console.log("Unhandled rejection", reason, promise);
});

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.railgun.org/developer-guide/wallet/transactions/transaction-utils.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
