Fetching Transactions
Overview
The provider exposes methods to retrieve transaction data from the OP_NET network. Transactions can be fetched individually by their hash or id, returning comprehensive details about the operation including contract interactions, deployments, gas consumption, and execution status. Transaction receipts provide additional information such as events emitted during execution and the final state changes applied to the blockchain.
Fetching a Transaction by Hash
Using getTransaction()
The getTransaction() method retrieves a transaction by its hash or id. This method returns the transaction data including sender and recipient addresses, calldata, gas usage, and block inclusion information. If the transaction is not found, the method throws an error.
The returned TransactionData object provides access to all transaction fields, including the contract address for contract interactions, the decoded input data, and the transaction's position within its containing block.
Method Signature
async getTransaction(
txHash: string
): Promise<TransactionBase<OPNetTransactionTypes>>
Transaction Types
OP_NET supports multiple transaction types including contract deployments, contract interactions, and generic transfers. For a comprehensive explanation of transaction structures, fields, and type-specific behavior, refer to the Transaction Types Explained section.
Transaction Inputs
The TransactionInput interface represents a transaction input, referencing the UTXO being spent. The originalTransactionId and outputTransactionIndex identify the source output, while scriptSignature contains the unlocking script. The sequenceId field specifies the input's sequence number for relative time-locks, and transactionInWitness holds the witness data for SegWit transactions.
interface TransactionInput {
originalTransactionId: string | undefined; // Source transaction ID
outputTransactionIndex: number | undefined; // Source output index
scriptSignature: ScriptSig | undefined; // Script signature
sequenceId: number; // Sequence number
transactionInWitness: string[]; // Witness data
}Transaction Outputs
The TransactionOutput interface represents a transaction output, defining where funds are sent. The value field specifies the amount in satoshis, while scriptPubKey contains the locking script with hex representation and associated addresses. The index indicates the output's position within the transaction, and script provides the decompiled script as an array of opcodes and data elements.
interface TransactionOutput {
value: bigint; // Output value in satoshis
scriptPubKey: ScriptPubKey; // Output script (object with hex, addresses, address)
index: number; // Output index
script: Array<number | Uint8Array> | null; // Decompiled script
}Basic Transaction Query
import { JSONRpcProvider } from 'opnet';
import { networks } from '@btc-vision/bitcoin';
const network = networks.regtest;
const provider = new JSONRpcProvider({ url: 'https://regtest.opnet.org', network });
const txHash = '63e77ba9fa4262b3d4d0d9d97fa8a7359534606c3f3af096284662e3f619f374';
const transaction = await provider.getTransaction(txHash);
console.log('Transaction:');
console.log(' ID:', transaction.id);
console.log(' Hash:', transaction.hash);
console.log(' Type:', transaction.OPNetType);
console.log(' Block:', transaction.blockNumber);Working with Transactions
Check Transaction Type
Each transaction includes an OPNetType field that identifies its category. The OPNetTransactionTypes enum defines the available types, allowing applications to determine how to process the transaction and cast it to the appropriate interface for accessing type-specific properties.
import { OPNetTransactionTypes } from 'opnet';
const transaction = await provider.getTransaction(txHash);
switch (transaction.OPNetType) {
case OPNetTransactionTypes.Interaction:
console.log('Contract interaction');
// Cast to InteractionTransaction for specific properties
break;
case OPNetTransactionTypes.Deployment:
console.log('Contract deployment');
// Cast to DeploymentTransaction for specific properties
break;
case OPNetTransactionTypes.Generic:
console.log('Generic Bitcoin transaction');
break;
}Handle Interaction Transactions
Interaction transactions represent calls to deployed contracts. After verifying the transaction type, cast it to InteractionTransaction to access contract-specific fields such as the target contract address, sender address, calldata payload, and compression status.
import { OPNetTransactionTypes, InteractionTransaction } from 'opnet';
const tx = await provider.getTransaction(txHash);
if (tx.OPNetType === OPNetTransactionTypes.Interaction) {
const interactionTx = tx as InteractionTransaction;
console.log('Contract:', interactionTx.contractAddress);
console.log('From:', interactionTx.from?.toHex());
console.log('Calldata:', toHex(interactionTx.calldata ?? new Uint8Array(0)));
console.log('Was compressed:', interactionTx.wasCompressed);
}Handle Deployment Transactions
Deployment transactions represent the creation of new contracts on the network. After verifying the transaction type, cast it to DeploymentTransaction to access deployment-specific fields such as the newly created contract address, deployer address, and contract bytecode.
import { OPNetTransactionTypes, DeploymentTransaction } from 'opnet';
const tx = await provider.getTransaction(txHash);
if (tx.OPNetType === OPNetTransactionTypes.Deployment) {
const deployTx = tx as DeploymentTransaction;
console.log('Deployed contract:', deployTx.contractAddress);
console.log('Deployer:', deployTx.from?.toHex());
console.log('Bytecode size:', deployTx.bytecode?.length ?? 0, 'bytes');
}Analyze Transaction Inputs and Outputs
Every transaction contains arrays of inputs and outputs representing the flow of funds. Inputs reference UTXOs being spent from previous transactions, while outputs define new UTXOs created by the transaction. Iterating through these arrays enables applications to trace fund origins, calculate total values, and build transaction graphs for analytics or visualization purposes.
const tx = await provider.getTransaction(txHash);
console.log('Inputs:', tx.inputs.length);
for (const input of tx.inputs) {
console.log(` ${input.originalTransactionId}:${input.outputTransactionIndex}`);
}
console.log('Outputs:', tx.outputs.length);
for (const output of tx.outputs) {
console.log(` Index ${output.index}: ${output.value} sats`);
}Hanlde Multiple Transactions
Transactions can be retrieved in bulk by fetching a block with the prefetchTxs parameter enabled. This returns all transactions included in the block within a single request, avoiding the need to fetch each transaction individually.
Get Block Transactions
async function getBlockTransactions(
provider: JSONRpcProvider,
blockNumber: bigint
): Promise<TransactionBase<OPNetTransactionTypes>[]> {
const block = await provider.getBlock(blockNumber, true);
return block.transactions;
}
// Usage
const transactions = await getBlockTransactions(provider, 100000n);
console.log('Transactions in block:', transactions.length);
for (const tx of transactions) {
console.log(`${tx.hash}: ${tx.OPNetType}`);
}Get Filtered Block Transactions
async function getInteractionTransactions(
provider: JSONRpcProvider,
blockNumber: bigint
): Promise<InteractionTransaction[]> {
const transactions = await getBlockTransactions(provider, blockNumber);
return transactions.filter(
(tx): tx is InteractionTransaction =>
tx.OPNetType === OPNetTransactionTypes.Interaction
);
}
// Usage
const interactions = await getInteractionTransactions(provider, 100000n);
console.log('Interactions:', interactions.length);Transaction Analysis
Transaction data includes fields for analyzing execution cost and status. Gas usage metrics indicate the computational resources consumed, while the revert field reveals whether the transaction failed and the reason for failure. These details enable applications to monitor transaction outcomes, calculate costs, and provide meaningful feedback to users.
Calculate Gas Cost
function analyzeGasCost(tx: TransactionBase<OPNetTransactionTypes>): {
gasUsed: bigint;
specialGasUsed: bigint;
totalGas: bigint;
burnedBitcoin: bigint;
} {
return {
gasUsed: tx.gasUsed,
specialGasUsed: tx.specialGasUsed,
totalGas: tx.gasUsed + tx.specialGasUsed,
burnedBitcoin: tx.burnedBitcoin,
};
}
// Usage
const tx = await provider.getTransaction(txHash);
const gasAnalysis = analyzeGasCost(tx);
console.log('Gas used:', gasAnalysis.gasUsed);
console.log('Total gas:', gasAnalysis.totalGas);
Check Transaction Status
async function getTransactionStatus(
provider: JSONRpcProvider,
txHash: string
): Promise<{
confirmed: boolean;
blockNumber?: bigint;
reverted: boolean;
revertReason?: string;
}> {
try {
const tx = await provider.getTransaction(txHash);
return {
confirmed: tx.blockNumber !== undefined,
blockNumber: tx.blockNumber,
reverted: tx.revert !== undefined,
revertReason: tx.revert,
};
} catch {
return {
confirmed: false,
reverted: false,
};
}
}
// Usage
const status = await getTransactionStatus(provider, txHash);
if (status.confirmed) {
console.log('Confirmed in block:', status.blockNumber);
} else {
console.log('Not yet confirmed');
}
if (status.reverted) {
console.log('Transaction reverted:', status.revertReason);
}Complete Transaction Service
class TransactionService {
constructor(private provider: JSONRpcProvider) {}
async get(txHash: string): Promise<TransactionBase<OPNetTransactionTypes>> {
return this.provider.getTransaction(txHash);
}
async getInteraction(txHash: string): Promise<InteractionTransaction | null> {
const tx = await this.get(txHash);
if (tx.OPNetType === OPNetTransactionTypes.Interaction) {
return tx as InteractionTransaction;
}
return null;
}
async getDeployment(txHash: string): Promise<DeploymentTransaction | null> {
const tx = await this.get(txHash);
if (tx.OPNetType === OPNetTransactionTypes.Deployment) {
return tx as DeploymentTransaction;
}
return null;
}
async isConfirmed(txHash: string): Promise<boolean> {
try {
const tx = await this.get(txHash);
return tx.blockNumber !== undefined;
} catch {
return false;
}
}
async isReverted(txHash: string): Promise<boolean> {
try {
const tx = await this.get(txHash);
return tx.revert !== undefined;
} catch {
return false;
}
}
async getRevertReason(txHash: string): Promise<string | undefined> {
const tx = await this.get(txHash);
return tx.revert;
}
async waitForConfirmation(
txHash: string,
timeoutMs: number = 60000
): Promise<TransactionBase<OPNetTransactionTypes> | null> {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
try {
const tx = await this.get(txHash);
if (tx.blockNumber !== undefined) {
return tx;
}
} catch {
// Transaction not found yet
}
await new Promise(r => setTimeout(r, 5000));
}
return null;
}
}
// Usage
const txService = new TransactionService(provider);
// Get transaction
const tx = await txService.get(txHash);
console.log('Transaction type:', tx.OPNetType);
// Check if confirmed
const confirmed = await txService.isConfirmed(txHash);
console.log('Confirmed:', confirmed);
// Wait for confirmation
const confirmedTx = await txService.waitForConfirmation(txHash, 120000);
if (confirmedTx) {
console.log('Confirmed in block:', confirmedTx.blockNumber);
}