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
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:
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)
};
}Example
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);