Estimate Gas Costs

Overview

OP_NET uses a gas system where computational operations cost gas units paid in satoshis. Understanding gas costs is essential for estimating transaction fees and optimizing contract interactions.

Every contract call returns a CallResult object that automatically provides all gas estimates. These estimates include both gas units consumed and the equivalent cost in satoshis.

CallResult Gas Properties

The CallResult object provides comprehensive gas information.

Property Type Description
estimatedGas bigint | undefined Gas units consumed by the operation.
refundedGas bigint | undefined Gas units refunded after execution.
estimatedSatGas bigint Cost in satoshis (pre-calculated).
estimatedRefundedGasInSat bigint Refund amount in satoshis.
typescript
Gas Estimation for a Transfer
const result = await contract.transfer(recipient, amount, new Uint8Array(0));

// Gas consumed (in gas units)
console.log('Gas used:', result.estimatedGas);

// Gas refunded (in gas units)
console.log('Refunded gas:', result.refundedGas);

// Cost in satoshis (pre-calculated)
console.log('Cost in sats:', result.estimatedSatGas);

// Refunded amount in satoshis
console.log('Refunded sats:', result.estimatedRefundedGasInSat);

Transaction Fees

After broadcasting a transaction, the returned receipt includes the actual fees paid in satoshis via the estimatedFees field. This value reflects the final fee, which may differ from pre-broadcast estimates.

See the Transaction Result section for details.

typescript
Transaction Fees
const receipt = await simulation.sendTransaction(params);

// Actual fees paid in satoshis
console.log('Fees paid:', receipt.estimatedFees, 'sats');

Network Gas Parameters

The provider exposes current network gas parameters, which are useful for fee estimation and transaction planning.

typescript
BlockGasParameters Interface
interface BlockGasParameters {
    blockNumber: bigint;      // Current block number
    gasUsed: bigint;          // Gas used in current block
    targetGasLimit: bigint;   // Target gas limit
    ema: bigint;              // Exponential moving average
    baseGas: bigint;          // Base gas price
    gasPerSat: bigint;        // Gas units per satoshi
    bitcoin: BitcoinFees;     // Bitcoin fee estimates
}

interface BitcoinFees {
    conservative: number;     // Conservative fee estimate
    recommended: {
        low: number;          // Low priority
        medium: number;       // Medium priority
        high: number;         // High priority (next block)
    };
}
typescript
Using Gas Parameters
const gasParams = await provider.gasParameters();

console.log('Gas per sat:', gasParams.gasPerSat);
console.log('Base gas:', gasParams.baseGas);
console.log('Target gas limit:', gasParams.targetGasLimit);

// Bitcoin fee rates (sat/vB)
console.log('High fee:', gasParams.bitcoin.recommended.high);
console.log('Medium fee:', gasParams.bitcoin.recommended.medium);
console.log('Low fee:', gasParams.bitcoin.recommended.low);
console.log('Conservative:', gasParams.bitcoin.conservative);

Fee Selection

Select appropriate fee rates based on transaction urgency.

typescript
Specifying a Fee
const gasParams = await provider.gasParameters();

// Non-urgent transactions: use low fee
const economyParams: TransactionParameters = {
    ...baseParams,
    feeRate: gasParams.bitcoin.recommended.low,
};

// Urgent transactions: use high fee (next block inclusion)
const urgentParams: TransactionParameters = {
    ...baseParams,
    feeRate: gasParams.bitcoin.recommended.high,
};

Access List Optimization

Access lists can optimize gas consumption for repeated operations. The first call discovers storage access patterns, and subsequent calls can reuse this information.

typescript
Specifying an Access List
// First call discovers storage access patterns
const firstCall = await contract.transfer(recipient, amount, new Uint8Array(0));

// Set access list for subsequent calls
contract.setAccessList(firstCall.accessList);

// Subsequent calls may consume less gas
const optimizedCall = await contract.transfer(recipient2, amount2, new Uint8Array(0));

Complete Example

The following example demonstrates a complete transfer workflow with gas estimation and fee selection.

typescript
Transfer with Gas Estimation
import {
    getContract,
    IOP20Contract,
    JSONRpcProvider,
    OP_20_ABI,
    TransactionParameters,
} from 'opnet';
import {
    Address,
    AddressTypes,
    Mnemonic,
    MLDSASecurityLevel,
    Wallet,
} from '@btc-vision/transaction';
import { Network, networks } from '@btc-vision/bitcoin';

async function transferWithGasCheck(): Promise<void> {
    const network: Network = networks.regtest;
    const provider: JSONRpcProvider = new JSONRpcProvider(
        'https://regtest.opnet.org',
        network
    );

    const mnemonic = new Mnemonic(
        'your seed phrase here ...',
        '',
        network,
        MLDSASecurityLevel.LEVEL2
    );
    const wallet: Wallet = mnemonic.deriveOPWallet(AddressTypes.P2TR, 0);

    const tokenAddress: Address = Address.fromString('0x...');
    const token: IOP20Contract = getContract<IOP20Contract>(
        tokenAddress,
        OP_20_ABI,
        provider,
        network,
        wallet.address
    );

    // Simulate transfer
    const recipient: Address = Address.fromString('0x...');
    const simulation = await token.transfer(
        recipient,
        100_00000000n,
        new Uint8Array(0)
    );

    // Check for revert
    if (simulation.revert) {
        console.error('Transaction would fail:', simulation.revert);
        return;
    }

    // Gas cost is pre-calculated in satoshis
    console.log('Gas cost:', simulation.estimatedSatGas, 'sats');

    // Get current fee rates
    const gasParams = await provider.gasParameters();

    // Send transaction
    const params: TransactionParameters = {
        signer: wallet.keypair,
        mldsaSigner: null,
        refundTo: wallet.p2tr,
        maximumAllowedSatToSpend: 100_000n,
        feeRate: gasParams.bitcoin.recommended.medium,
        network: network,
    };

    const receipt = await simulation.sendTransaction(params);

    console.log('Transaction ID:', receipt.transactionId);
    console.log('Fees paid:', receipt.estimatedFees, 'sats');

    await provider.close();
}

transferWithGasCheck().catch(console.error);