🔧Fee Market Module with Dynamic Fees
This guide explains how to use the feemarket module to implement dynamic fee calculation for standard Cosmos SDK transactions on the cheqd network. The feemarket module provides real-time gas pricing based on network congestion and demand.
Note: Identity transactions (DIDs and DID-Linked Resources) have their own fixed pricing model and are not affected by the feemarket module's dynamic pricing.
Understanding Dynamic Fees
Dynamic fees adjust transaction costs based on network conditions:
High Network Activity: Gas prices increase to prioritize transactions
Low Network Activity: Gas prices decrease to reduce transaction costs
Real-time Pricing: Prices update continuously based on network demand
Gas Price Endpoints
You can fetch current gas prices directly from the cheqd network APIs:
Mainnet
GET https://api.cheqd.net/feemarket/v1/gas_price/ncheq
Testnet
GET https://api.cheqd.network/feemarket/v1/gas_price/ncheq
API Response Format
The API returns a decimal value that needs to be converted:
{
"price": "0.500000000000000000"
}
Interpretation: 0.500000000000000000 × 10^4 = 5000ncheq
The price represents the cost per unit of gas in ncheq
(nano CHEQ tokens).
SDK Implementation
Prerequisites
Ensure your SDK includes the feemarket module:
import {
createCheqdSDK,
FeemarketModule,
CheqdNetwork,
AbstractCheqdSDKModule,
type ICheqdSDKOptions
} from '@cheqd/sdk';
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
async function initializeSDKWithFeemarket() {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(
'your twelve word mnemonic phrase goes here like this example',
{ prefix: 'cheqd' }
);
const options: ICheqdSDKOptions = {
modules: [
FeemarketModule as unknown as AbstractCheqdSDKModule
],
rpcUrl: 'https://rpc.cheqd.network',
network: CheqdNetwork.Testnet,
wallet: wallet
};
const sdk = await createCheqdSDK(options);
return { sdk, wallet };
}
Fetching Dynamic Gas Prices
import { FeemarketModule } from '@cheqd/sdk';
import { coin } from '@cosmjs/stargate';
async function getDynamicGasPrice(denom: string = 'ncheq') {
const { sdk } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
try {
// Fetch current dynamic gas price for the denomination
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom(denom);
console.log(`💰 Current gas price for ${denom}:`);
console.log(` Amount: ${gasPrice.amount}`);
console.log(` Denom: ${gasPrice.denom}`);
return gasPrice;
} catch (error) {
console.error('Error fetching dynamic gas price:', error);
// Fallback to network API if SDK query fails
return await fetchGasPriceFromAPI();
}
}
// Alternative: Direct API fetch
async function fetchGasPriceFromAPI(): Promise<{ amount: string; denom: string }> {
try {
const response = await fetch('https://api.cheqd.network/feemarket/v1/gas_price/ncheq');
const data = await response.json();
// Convert decimal to integer format
// Example: "0.500000000000000000" * 10^4 = 5000
const priceDecimal = parseFloat(data.price);
const priceInteger = Math.ceil(priceDecimal * 10000).toString();
console.log(`🌐 Fetched from API: ${data.price} → ${priceInteger}ncheq`);
return {
amount: priceInteger,
denom: 'ncheq'
};
} catch (error) {
console.error('Error fetching from API:', error);
// Final fallback to default
return {
amount: '5000',
denom: 'ncheq'
};
}
}
Standard Cosmos SDK Transactions with Dynamic Fees
Token Transfer
import { MsgSend } from 'cosmjs-types/cosmos/bank/v1beta1/tx';
async function transferWithDynamicFees() {
const { sdk, wallet } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
const sender = (await wallet.getAccounts())[0].address;
const recipient = 'cheqd1recipient...';
// Create transfer message
const transferMsg = {
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: MsgSend.fromPartial({
fromAddress: sender,
toAddress: recipient,
amount: [coin('1000000', 'ncheq')] // 1 CHEQ
})
};
try {
// Get dynamic gas price
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
// Calculate fee based on dynamic pricing
const estimatedGas = '200000'; // Estimated gas for transfer
const fee = FeemarketModule.generateFeesFromGasPrice(gasPrice, sender, estimatedGas);
console.log('💸 Dynamic fee calculation:');
console.log(` Gas Price: ${gasPrice.amount}${gasPrice.denom}`);
console.log(` Estimated Gas: ${estimatedGas}`);
console.log(` Total Fee: ${fee.amount[0].amount}${fee.amount[0].denom}`);
const result = await sdk.signer.signAndBroadcast(
sender,
[transferMsg],
fee,
'Transfer with dynamic fee'
);
if (result.code === 0) {
console.log('✅ Transfer successful with dynamic fee!');
console.log(` Transaction hash: ${result.transactionHash}`);
console.log(` Gas used: ${result.gasUsed}`);
console.log(` Gas wanted: ${result.gasWanted}`);
// Calculate actual fee cost
const actualFeeAmount = parseInt(fee.amount[0].amount) / 1000000000; // Convert ncheq to CHEQ
console.log(` Fee paid: ${actualFeeAmount} CHEQ`);
} else {
console.error(`❌ Transfer failed: ${result.rawLog}`);
}
} catch (error) {
console.error('Error during transfer:', error);
}
}
Staking Delegation
import { MsgDelegate } from 'cosmjs-types/cosmos/staking/v1beta1/tx';
async function delegateWithDynamicFees() {
const { sdk, wallet } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
const delegator = (await wallet.getAccounts())[0].address;
const validator = 'cheqdvaloper1validator...';
// Create delegation message
const delegateMsg = {
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
value: MsgDelegate.fromPartial({
delegatorAddress: delegator,
validatorAddress: validator,
amount: coin('10000000', 'ncheq') // 10 CHEQ
})
};
try {
// Get dynamic gas price
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
// Higher gas estimate for staking operations
const estimatedGas = '300000';
const fee = FeemarketModule.generateFeesFromGasPrice(gasPrice, delegator, estimatedGas);
console.log('🥩 Delegation with dynamic fee:');
console.log(` Delegating: 10 CHEQ to ${validator}`);
console.log(` Dynamic fee: ${parseInt(fee.amount[0].amount) / 1000000000} CHEQ`);
const result = await sdk.signer.signAndBroadcast(
delegator,
[delegateMsg],
fee,
'Delegation with dynamic fee'
);
if (result.code === 0) {
console.log('✅ Delegation successful!');
console.log(` Transaction hash: ${result.transactionHash}`);
console.log(` Gas efficiency: ${(parseInt(result.gasUsed!) / parseInt(estimatedGas) * 100).toFixed(1)}%`);
} else {
console.error(`❌ Delegation failed: ${result.rawLog}`);
}
} catch (error) {
console.error('Error during delegation:', error);
}
}
Claiming Staking Rewards
import { MsgWithdrawDelegatorReward } from 'cosmjs-types/cosmos/distribution/v1beta1/tx';
async function claimRewardsWithDynamicFees() {
const { sdk, wallet } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
const delegator = (await wallet.getAccounts())[0].address;
const validators = [
'cheqdvaloper1validator1...',
'cheqdvaloper1validator2...',
'cheqdvaloper1validator3...'
];
// Create reward claim messages for multiple validators
const claimMessages = validators.map(validator => ({
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: MsgWithdrawDelegatorReward.fromPartial({
delegatorAddress: delegator,
validatorAddress: validator
})
}));
try {
// Get dynamic gas price
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
// Gas scales with number of validators
const gasPerValidator = 100000;
const estimatedGas = (gasPerValidator * validators.length).toString();
const fee = FeemarketModule.generateFeesFromGasPrice(gasPrice, delegator, estimatedGas);
console.log('🎁 Claiming rewards with dynamic fee:');
console.log(` Validators: ${validators.length}`);
console.log(` Estimated Gas: ${estimatedGas}`);
console.log(` Dynamic Fee: ${parseInt(fee.amount[0].amount) / 1000000000} CHEQ`);
const result = await sdk.signer.signAndBroadcast(
delegator,
claimMessages,
fee,
'Claim rewards with dynamic fee'
);
if (result.code === 0) {
console.log('✅ Rewards claimed successfully!');
console.log(` Transaction hash: ${result.transactionHash}`);
console.log(` Claimed from ${validators.length} validators`);
// Show fee efficiency
const gasEfficiency = (parseInt(result.gasUsed!) / parseInt(estimatedGas) * 100).toFixed(1);
console.log(` Gas efficiency: ${gasEfficiency}%`);
} else {
console.error(`❌ Claim rewards failed: ${result.rawLog}`);
}
} catch (error) {
console.error('Error during reward claim:', error);
}
}
Governance Voting
import { MsgVote, VoteOption } from 'cosmjs-types/cosmos/gov/v1beta1/tx';
async function voteWithDynamicFees() {
const { sdk, wallet } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
const voter = (await wallet.getAccounts())[0].address;
const proposalId = 1; // Replace with actual proposal ID
// Create vote message
const voteMsg = {
typeUrl: '/cosmos.gov.v1beta1.MsgVote',
value: MsgVote.fromPartial({
proposalId: BigInt(proposalId),
voter: voter,
option: VoteOption.VOTE_OPTION_YES
})
};
try {
// Get dynamic gas price
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
// Voting typically uses minimal gas
const estimatedGas = '150000';
const fee = FeemarketModule.generateFeesFromGasPrice(gasPrice, voter, estimatedGas);
console.log('🗳️ Voting with dynamic fee:');
console.log(` Proposal ID: ${proposalId}`);
console.log(` Vote: YES`);
console.log(` Dynamic Fee: ${parseInt(fee.amount[0].amount) / 1000000000} CHEQ`);
const result = await sdk.signer.signAndBroadcast(
voter,
[voteMsg],
fee,
'Governance vote with dynamic fee'
);
if (result.code === 0) {
console.log('✅ Vote submitted successfully!');
console.log(` Transaction hash: ${result.transactionHash}`);
} else {
console.error(`❌ Vote failed: ${result.rawLog}`);
}
} catch (error) {
console.error('Error during vote:', error);
}
}
Fee Optimization Strategies
Gas Estimation
async function estimateOptimalGas(messages: any[], signer: string) {
const { sdk } = await initializeSDKWithFeemarket();
try {
// Simulate transaction to get accurate gas estimate
const simResult = await sdk.signer.simulate(signer, messages, '');
// Add safety margin (20% buffer)
const safeGasLimit = Math.ceil(simResult.gasUsed * 1.2);
console.log('⛽ Gas estimation:');
console.log(` Simulated: ${simResult.gasUsed}`);
console.log(` Safe limit: ${safeGasLimit}`);
return safeGasLimit.toString();
} catch (error) {
console.error('Gas simulation failed:', error);
// Fallback to conservative estimate
return '300000';
}
}
Fee Comparison
async function compareFeeOptions() {
const { sdk } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
const sender = 'cheqd1sender...';
const gasAmount = '200000';
try {
// Get dynamic price
const dynamicPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
const dynamicFee = FeemarketModule.generateFeesFromGasPrice(dynamicPrice, sender, gasAmount);
// Static price comparison
const staticPrice = { amount: '5000', denom: 'ncheq' };
const staticFee = FeemarketModule.generateFeesFromGasPrice(staticPrice, sender, gasAmount);
console.log('💰 Fee comparison:');
console.log(` Dynamic: ${parseInt(dynamicFee.amount[0].amount) / 1000000000} CHEQ`);
console.log(` Static: ${parseInt(staticFee.amount[0].amount) / 1000000000} CHEQ`);
const savings = parseInt(staticFee.amount[0].amount) - parseInt(dynamicFee.amount[0].amount);
if (savings > 0) {
console.log(` 💸 Save: ${savings / 1000000000} CHEQ with dynamic pricing`);
} else {
console.log(` ⚠️ Dynamic fee is ${Math.abs(savings) / 1000000000} CHEQ higher`);
}
return { dynamicFee, staticFee, savings };
} catch (error) {
console.error('Error comparing fees:', error);
return null;
}
}
Advanced Usage
Batch Transaction Optimization
async function optimizedBatchTransactions() {
const { sdk, wallet } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
const sender = (await wallet.getAccounts())[0].address;
// Multiple operations in one transaction
const messages = [
// Transfer tokens
{
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: MsgSend.fromPartial({
fromAddress: sender,
toAddress: 'cheqd1recipient1...',
amount: [coin('500000', 'ncheq')]
})
},
// Delegate tokens
{
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
value: MsgDelegate.fromPartial({
delegatorAddress: sender,
validatorAddress: 'cheqdvaloper1validator...',
amount: coin('1000000', 'ncheq')
})
}
];
try {
// Estimate gas for batch
const estimatedGas = await estimateOptimalGas(messages, sender);
// Get dynamic pricing
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
const fee = FeemarketModule.generateFeesFromGasPrice(gasPrice, sender, estimatedGas);
console.log('📦 Batch transaction:');
console.log(` Operations: ${messages.length}`);
console.log(` Total Gas: ${estimatedGas}`);
console.log(` Dynamic Fee: ${parseInt(fee.amount[0].amount) / 1000000000} CHEQ`);
const result = await sdk.signer.signAndBroadcast(
sender,
messages,
fee,
'Optimized batch transaction'
);
if (result.code === 0) {
console.log('✅ Batch transaction successful!');
console.log(` Saved gas by batching operations`);
} else {
console.error(`❌ Batch transaction failed: ${result.rawLog}`);
}
} catch (error) {
console.error('Error in batch transaction:', error);
}
}
Fee Market Monitoring
async function monitorFeeMarket() {
const { sdk } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
console.log('📊 Monitoring fee market...');
const monitorInterval = setInterval(async () => {
try {
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] Gas Price: ${gasPrice.amount}${gasPrice.denom}`);
// Alert if price is unusually high
if (parseInt(gasPrice.amount) > 10000) {
console.warn('⚠️ High network congestion detected!');
console.log('💡 Consider waiting for lower fees or using fee abstraction');
}
} catch (error) {
console.error('Error monitoring fees:', error);
}
}, 30000); // Check every 30 seconds
// Stop monitoring after 5 minutes
setTimeout(() => {
clearInterval(monitorInterval);
console.log('📊 Fee monitoring stopped');
}, 300000);
}
Best Practices
1. Gas Estimation
Always simulate transactions before broadcasting
Add 10-20% buffer to gas estimates
Use batch transactions to optimize gas usage
2. Fee Management
Monitor network congestion regularly
Consider fee abstraction for predictable costs
Use dynamic pricing during low-congestion periods
3. Error Handling
async function robustTransactionWithDynamicFees(messages: any[], sender: string) {
const { sdk } = await initializeSDKWithFeemarket();
const feemarketModule = new FeemarketModule(sdk.signer, sdk.querier);
let retries = 3;
while (retries > 0) {
try {
// Get fresh gas price for each attempt
const gasPrice = await feemarketModule.generateSafeGasPriceByDenom('ncheq');
const estimatedGas = await estimateOptimalGas(messages, sender);
const fee = FeemarketModule.generateFeesFromGasPrice(gasPrice, sender, estimatedGas);
const result = await sdk.signer.signAndBroadcast(
sender,
messages,
fee,
'Transaction with retry logic'
);
if (result.code === 0) {
console.log('✅ Transaction successful!');
return result;
} else {
throw new Error(`Transaction failed: ${result.rawLog}`);
}
} catch (error) {
retries--;
console.warn(`⚠️ Attempt failed, ${retries} retries left:`, error);
if (retries > 0) {
// Wait before retry with exponential backoff
await new Promise(resolve => setTimeout(resolve, (4 - retries) * 1000));
} else {
throw error;
}
}
}
}
4. Performance Tips
Cache gas prices for short periods (30-60 seconds)
Use WebSocket connections for real-time price updates
Implement circuit breakers for high-fee scenarios
Troubleshooting
Common Issues
High Gas Fees
Check network congestion
Consider using fee abstraction
Wait for off-peak hours
Transaction Failures
Increase gas limit
Verify account balance
Check message formatting
API Failures
Implement fallback pricing
Use multiple RPC endpoints
Handle network timeouts
Error Codes
async function handleTransactionErrors(result: any) {
switch (result.code) {
case 5:
console.error('❌ Insufficient funds for gas');
console.log('💡 Add more tokens to your account');
break;
case 11:
console.error('❌ Out of gas');
console.log('💡 Increase gas limit or optimize transaction');
break;
case 32:
console.error('❌ Account not found');
console.log('💡 Verify account address and funding');
break;
default:
console.error(`❌ Transaction failed with code ${result.code}`);
console.log(`Raw log: ${result.rawLog}`);
}
}
Next Steps
Getting Help
If you encounter issues with dynamic fees:
Check the cheqd SDK GitHub repository for updates
Review network status at status.cheqd.io
Join the cheqd Community Discord for support
Consult the Cosmos SDK documentation for standard transaction types
Last updated
Was this helpful?