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. |
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.
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.
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)
};
}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.
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.
// 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.
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);