|
1 |
| -## Calculating Fees |
| 1 | +# Calculating Fees |
2 | 2 |
|
3 |
| -Make sure to create a `fee` object in addition to your message. |
| 3 | +This document provides a reference for calculating transaction fees for Cosmos SDK blockchains using Telescope-generated types. |
4 | 4 |
|
5 |
| -```js |
6 |
| -import { coins } from '@cosmjs/amino'; |
| 5 | +## Fee Structure |
7 | 6 |
|
8 |
| -const fee = { |
9 |
| - amount: coins(0, 'uosmo'), |
10 |
| - gas: '250000' |
| 7 | +| Component | Description | |
| 8 | +| --------- | ----------- | |
| 9 | +| Gas | Computational resources required to process a transaction | |
| 10 | +| Gas Price | Price per unit of gas in a specific denomination | |
| 11 | +| Fee | Total amount paid for transaction processing (Gas × Gas Price) | |
| 12 | + |
| 13 | +## Fee Object Structure |
| 14 | + |
| 15 | +```typescript |
| 16 | +export interface StdFee { |
| 17 | + readonly amount: readonly Coin[]; |
| 18 | + readonly gas: string; |
11 | 19 | }
|
| 20 | + |
| 21 | +export interface Coin { |
| 22 | + readonly denom: string; |
| 23 | + readonly amount: string; |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +## Gas Estimation Methods |
| 28 | + |
| 29 | +| Method | Description | Usage | |
| 30 | +| ------ | ----------- | ----- | |
| 31 | +| Manual Specification | Explicitly set gas limit | Simple transactions with known gas requirements | |
| 32 | +| Simulation | Estimate gas by simulating transaction | Complex transactions with variable gas needs | |
| 33 | +| Auto | Automatically estimate and adjust gas | General-purpose usage | |
| 34 | + |
| 35 | +## Manual Fee Calculation |
| 36 | + |
| 37 | +```typescript |
| 38 | +// Define a standard fee with fixed gas |
| 39 | +const fee = { |
| 40 | + amount: [{ denom: "uatom", amount: "5000" }], |
| 41 | + gas: "200000" // 200,000 gas units |
| 42 | +}; |
| 43 | +``` |
| 44 | + |
| 45 | +## Using Gas Prices |
| 46 | + |
| 47 | +```typescript |
| 48 | +import { calculateFee, GasPrice } from "@cosmjs/stargate"; |
| 49 | + |
| 50 | +// Define gas price (e.g., 0.025 uatom per gas unit) |
| 51 | +const gasPrice = GasPrice.fromString("0.025uatom"); |
| 52 | + |
| 53 | +// Calculate fee for a given gas limit |
| 54 | +const fee = calculateFee(200000, gasPrice); |
| 55 | +// Result: { amount: [{ denom: "uatom", amount: "5000" }], gas: "200000" } |
| 56 | +``` |
| 57 | + |
| 58 | +## Automatic Gas Estimation |
| 59 | + |
| 60 | +```typescript |
| 61 | +import { SigningStargateClient } from "@cosmjs/stargate"; |
| 62 | + |
| 63 | +// Auto gas estimation when sending transactions |
| 64 | +const result = await client.signAndBroadcast( |
| 65 | + sender, |
| 66 | + messages, |
| 67 | + "auto" // Use auto gas estimation |
| 68 | +); |
| 69 | +``` |
| 70 | + |
| 71 | +## Gas Estimation via Simulation |
| 72 | + |
| 73 | +```typescript |
| 74 | +import { SigningStargateClient } from "@cosmjs/stargate"; |
| 75 | + |
| 76 | +// First simulate the transaction |
| 77 | +const gasEstimate = await client.simulate( |
| 78 | + sender, |
| 79 | + messages, |
| 80 | + memo |
| 81 | +); |
| 82 | + |
| 83 | +// Apply a safety margin (e.g., 1.3x the estimated gas) |
| 84 | +const gasLimit = Math.round(gasEstimate * 1.3); |
| 85 | + |
| 86 | +// Calculate fee with the estimated gas |
| 87 | +const fee = calculateFee(gasLimit, gasPrice); |
12 | 88 | ```
|
13 | 89 |
|
14 |
| -if you are broadcasting multiple messages in a batch, you should `simulate` your tx and estimate the fee |
| 90 | +## Fee Adjustment Strategies |
15 | 91 |
|
16 |
| -```js |
17 |
| -import { Dec, IntPretty } from '@keplr-wallet/unit'; |
| 92 | +| Strategy | Description | Use Case | |
| 93 | +| -------- | ----------- | -------- | |
| 94 | +| Fixed Multiplier | Multiply estimated gas by a factor | General usage (1.3-1.5x typical) | |
| 95 | +| Minimum Gas | Set a floor for gas estimation | Ensure transaction validity | |
| 96 | +| Dynamic Pricing | Adjust gas price based on network congestion | Prioritize during high traffic | |
18 | 97 |
|
19 |
| -const gasEstimated = await stargateClient.simulate(address, msgs, memo); |
| 98 | +## Fee Estimation with Telescope-generated Clients |
| 99 | + |
| 100 | +```typescript |
| 101 | +import { createTxClient } from "./tx"; |
| 102 | + |
| 103 | +const txClient = await createTxClient({ |
| 104 | + signer: wallet, |
| 105 | + rpcEndpoint: "https://rpc.cosmos.network" |
| 106 | +}); |
| 107 | + |
| 108 | +// Simulate to estimate gas |
| 109 | +const gasEstimate = await txClient.simulate({ |
| 110 | + messages, |
| 111 | + signer: sender |
| 112 | +}); |
| 113 | + |
| 114 | +// Apply safety margin and calculate fee |
| 115 | +const gasLimit = Math.round(gasEstimate * 1.3); |
20 | 116 | const fee = {
|
21 |
| - amount: coins(0, 'uosmo'), |
22 |
| - gas: new IntPretty(new Dec(gasEstimated).mul(new Dec(1.3))) |
23 |
| - .maxDecimals(0) |
24 |
| - .locale(false) |
25 |
| - .toString() |
| 117 | + amount: [{ denom: "uatom", amount: (gasLimit * 0.025).toString() }], |
| 118 | + gas: gasLimit.toString() |
26 | 119 | };
|
27 | 120 | ```
|
| 121 | + |
| 122 | +## Fee Denom Selection |
| 123 | + |
| 124 | +```typescript |
| 125 | +// Get available account balances |
| 126 | +const { balances } = await queryClient.cosmos.bank.v1beta1.allBalances({ |
| 127 | + address: sender |
| 128 | +}); |
| 129 | + |
| 130 | +// Select appropriate denom for fees |
| 131 | +function selectFeeDenom(balances, preferredDenom = "uatom") { |
| 132 | + // First try preferred denom |
| 133 | + const preferred = balances.find(coin => coin.denom === preferredDenom); |
| 134 | + if (preferred && parseInt(preferred.amount) > 5000) { |
| 135 | + return preferredDenom; |
| 136 | + } |
| 137 | + |
| 138 | + // Fallback to first denom with sufficient balance |
| 139 | + const fallback = balances.find(coin => parseInt(coin.amount) > 5000); |
| 140 | + return fallback ? fallback.denom : preferredDenom; |
| 141 | +} |
| 142 | + |
| 143 | +const feeDenom = selectFeeDenom(balances); |
| 144 | +``` |
| 145 | + |
| 146 | +## Fee Grants |
| 147 | + |
| 148 | +Some chains support fee grants, allowing one account to pay fees for another: |
| 149 | + |
| 150 | +```typescript |
| 151 | +import { MsgGrantAllowance } from "./cosmos/feegrant/v1beta1/tx"; |
| 152 | +import { BasicAllowance } from "./cosmos/feegrant/v1beta1/feegrant"; |
| 153 | + |
| 154 | +// Create a basic allowance |
| 155 | +const expirationDate = new Date(); |
| 156 | +expirationDate.setMonth(expirationDate.getMonth() + 1); // 1 month expiry |
| 157 | + |
| 158 | +const basicAllowance = BasicAllowance.fromPartial({ |
| 159 | + spendLimit: [{ denom: "uatom", amount: "1000000" }], |
| 160 | + expiration: expirationDate |
| 161 | +}); |
| 162 | + |
| 163 | +// Grant fee allowance |
| 164 | +const grantMsg = MsgGrantAllowance.fromPartial({ |
| 165 | + granter: "cosmos1granter...", |
| 166 | + grantee: "cosmos1grantee...", |
| 167 | + allowance: Any.pack(basicAllowance, "/cosmos.feegrant.v1beta1.BasicAllowance") |
| 168 | +}); |
| 169 | +``` |
| 170 | + |
| 171 | +## Chain-Specific Fee Parameters |
| 172 | + |
| 173 | +| Parameter | Value Source | Example | |
| 174 | +| --------- | ----------- | ------- | |
| 175 | +| Minimum Gas Prices | Chain configuration | "0.0025uatom" | |
| 176 | +| Fee Denominations | Chain-accepted tokens | ["uatom", "uosmo", ...] | |
| 177 | +| Gas Adjustment | Client configuration | 1.3 | |
| 178 | + |
| 179 | +## Gas Weights for Common Operations |
| 180 | + |
| 181 | +| Operation | Approximate Gas Cost | Notes | |
| 182 | +| --------- | -------------------- | ----- | |
| 183 | +| Token Transfer | 80,000 - 100,000 | Single MsgSend | |
| 184 | +| Delegate | 140,000 - 180,000 | MsgDelegate | |
| 185 | +| Undelegate | 150,000 - 190,000 | MsgUndelegate | |
| 186 | +| Claim Rewards | 150,000 - 250,000 | MsgWithdrawDelegatorReward | |
| 187 | +| Submit Proposal | 200,000+ | Depends on proposal content size | |
| 188 | +| Vote on Proposal | 100,000 - 140,000 | MsgVote | |
| 189 | + |
| 190 | +## Handling Out of Gas Errors |
| 191 | + |
| 192 | +```typescript |
| 193 | +try { |
| 194 | + const result = await client.signAndBroadcast(sender, messages, fee); |
| 195 | + // Process result |
| 196 | +} catch (error) { |
| 197 | + if (error.message.includes("out of gas")) { |
| 198 | + // Increase gas and retry |
| 199 | + const newGasLimit = Math.round(parseInt(fee.gas) * 1.5); |
| 200 | + const newFee = { |
| 201 | + amount: fee.amount, |
| 202 | + gas: newGasLimit.toString() |
| 203 | + }; |
| 204 | + // Retry with new fee |
| 205 | + const retryResult = await client.signAndBroadcast(sender, messages, newFee); |
| 206 | + // Process retry result |
| 207 | + } else { |
| 208 | + // Handle other errors |
| 209 | + console.error("Transaction failed:", error); |
| 210 | + } |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +## Best Practices |
| 215 | + |
| 216 | +1. Always use gas estimation for complex transactions |
| 217 | +2. Apply a safety margin of 1.3x to 1.5x to estimated gas |
| 218 | +3. Set a reasonable maximum gas limit to avoid excessive fees |
| 219 | +4. Check account balances before sending to ensure sufficient funds for fees |
| 220 | +5. Consider network congestion and adjust gas prices accordingly |
| 221 | +6. Use appropriate denominations accepted by validators |
| 222 | +7. Test fee calculations on testnets before mainnet deployment |
0 commit comments