Estimate Gas Costs

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

Example

Gas Estimation for a Transfer
typescript
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 sending a transaction, the receipt includes the actual fees paid:

typescript
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:

BlockGasParameters Structure
typescript
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)
    };
}

Example

Using Gas Parameters
typescript
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
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
// 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:

Transfer with Gas Estimation
typescript
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);