Skip to content

Conversation

@ertemann
Copy link

@ertemann ertemann commented Nov 18, 2025

Summary

Adds automatic gas price discovery for chains with dynamic feemarket modules (Osmosis EIP-1559 and Skip feemarket). Instead of manually setting static gas prices, CosmJS can now query current prices from the chain, preventing transaction failures from outdated pricing.

Changes

  • New feemarket.ts: Query dynamic gas prices from Osmosis EIP-1559 and Skip feemarket modules
  • Updated clients: SigningStargateClient and SigningCosmWasmClient now support DynamicGasPriceConfig
  • Refactoring: Extracted calculateFeeForTransaction() helper
  • Tests: Some unit tests and an end-to-end validation on Osmosis and Neutron testnets

Usage

const client = await SigningStargateClient.connectWithSigner(rpc, signer, {
  gasPrice: {
    denom: "uosmo",
    multiplier: 1.3,
    minGasPrice: GasPrice.fromString("0.0025uosmo"),
    maxGasPrice: GasPrice.fromString("0.1uosmo"),
  }
});

// Gas price queried automatically per transaction
await client.signAndBroadcast(address, messages, "auto");

Features

Graceful fallback to minGasPrice if feemarket query fails
Min/max constraint enforcement with configurable multiplier
backward compatible - existing code unchanged
Automatic chain detection for Osmosis variants

Inspired by Hermes relayer PR #4004.

@ertemann ertemann changed the title add dynamic gasfee pricing as an option through feemarket and osmosis… add dynamic gasfee pricing as a signing client option - Feemarket/Osmosis Nov 18, 2025

// Multiply by multiplier 18 fractional digits for Dec type
const fractionalDigits = minGasPrice.amount.fractionalDigits;
const adjustedGasPrice = multiplyDecimalByNumber(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a cleaner way to do this please advise


// Replace with your mnemonic or set MNEMONIC env var
// WARNING: Never commit real mnemonics to git!
const MNEMONIC =
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throwaway mnemonic for you to test with.

@@ -0,0 +1,278 @@
#!/usr/bin/env node
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a complete integration test to validate the PR, can be removed once merged

@ertemann ertemann marked this pull request as draft November 18, 2025 15:00
@ertemann ertemann marked this pull request as ready for review November 18, 2025 20:10
}
}

// Minimal protobuf type definitions matching cosmjs-types pattern
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or should this actually go into cosmjs/types? they refer to /feemarket module ofcourse and not base SDK

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good enough as is and can be refactored later on

Copy link
Member

@webmaster128 webmaster128 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice stuff. Some first thoughts here

// E.g. https://github.com/cosmos/cosmos-sdk/issues/16020
private readonly defaultGasMultiplier = 1.4;
// Default multiplier for dynamic gas price (applied on top of queried price)
private readonly defaultDynamicGasMultiplier = 1.3;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a gas price multiplier, not a gas multiplier, right? I think it is important to highlight this e.g. by renaming

Suggested change
private readonly defaultDynamicGasMultiplier = 1.3;
private readonly defaultDynamicGasPriceMultiplier = 1.3;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right, oversight by me.

can we make it dynamicGasFeeMultiplier though seeing as it considers the fee portion of the total gasprice configuration ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A "fee" in Cosmos SDK contains the gas (gas limit) a transaction can use as well as the amount of Coins paid. This the price per gas can be calculated from that. So there is everything mixed together.

What we do here is having two data sources for the components of the fee:

  1. Tha gas (gas limit) is calculated through simulation
  2. The gas price is either configured or queried from chain starting with this PR

The multiplier here only affects 2. and not 1.

I.e. we do not multiply the fee (gas limit remains unchanged, granter and payer cannot be multiplied) but the gas price.

fee: "auto" | number,
): Promise<StdFee> {
const gasEstimation = await this.simulate(signerAddress, messages, memo);
const multiplier = typeof fee === "number" ? fee : this.defaultGasMultiplier;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point we know the gas limit, right? Everything else below is just about the price.

Adding

const gasLimit = Math.ceil(gasEstimation * multiplier)

here would remove the 4x Math.round(gasEstimation * multiplier) from below and increase readability.

const product = (BigInt(normalizedValue.atomics) * BigInt(multiplierDecimal.atomics)) / factor;

return Decimal.fromAtomics(product.toString(), fractionalDigits);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation might be correct. But as we do not need decimal math here we can also use float math more or less like this:

const x = value.toFloatApproximation() * multiplier; // the value we want

// now convert x to Decimal

Now exactly as the Decimal API is limited but I think you get the idea

Comment on lines +30 to +33
const normalizedValue =
value.fractionalDigits === fractionalDigits
? value
: Decimal.fromUserInput(value.toString(), fractionalDigits);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is something the class Decimal should have. I.e. convert decimal "2.0" to "2.000" by changing the number of decimals from 1 to 3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #1916 which will give you an API for that

}
}

// Minimal protobuf type definitions matching cosmjs-types pattern
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good enough as is and can be refactored later on

import { Decimal } from "@cosmjs/math";
import { fromUtf8 } from "@cosmjs/encoding";

import { GasPrice } from "./fee";

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused import GasPrice.
const response = new Uint8Array([
10, 17, 51, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
]);
const queryClient = createMockQueryClient(response);

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable queryClient.
10, 25, 10, 5, 117, 97, 116, 111, 109, 18, 16, 53, 51, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
48, 48,
]);
const queryClient = createMockQueryClient(response);

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable queryClient.
@ertemann
Copy link
Author

Thanks for the comments @webmaster128 will resolve remaining issues tomorrow, didnt have time this week yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants