Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fee estimation api #962

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ static_assertions = { workspace = true }
frame-support = { workspace = true }
sp-core = { workspace = true }
sp-std = { workspace = true }
sp-api = { workspace = true }
sp-runtime = { workspace = true }
sp-weights = { workspace = true }

# Polkadot dependencies
polkadot-primitives = { workspace = true }
Expand Down
8 changes: 8 additions & 0 deletions primitives/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ pub mod time {
}
}

pub mod transaction {
use frame_support::parameter_types;

parameter_types! {
/// Maximum size of an encoded extrinsic, derived from the max block size
pub const MaxExtrinsicSize: u32 = 2 * 1024 * 1024;
}
}
pub mod chain {
pub use crate::{AssetId, Balance};
pub use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight};
Expand Down
2 changes: 1 addition & 1 deletion primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use frame_support::sp_runtime::{
use sp_core::H160;

pub mod constants;

pub mod runtime_api;
/// An index to a block.
pub type BlockNumber = u32;

Expand Down
20 changes: 20 additions & 0 deletions primitives/src/runtime_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::constants::transaction::MaxExtrinsicSize;
use codec::Codec;
use frame_support::BoundedVec;
use sp_weights::Weight;

sp_api::decl_runtime_apis! {
pub trait FeeEstimationApi<AccountId, AssetId, Balance>
Copy link
Contributor

Choose a reason for hiding this comment

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

Naming.

I would go with slightly different naming .

TransactionFeeAPI

and methods:

  • estimate_fee
  • estimate_uxt_fee

I let @jak-pan share his thoughts here on naming,

where
AccountId: Codec,
AssetId: Codec,
Balance: Codec,
{
fn estimate_fee_payment(weight: Weight, account_id: AccountId) -> (AssetId, Balance);
Copy link
Contributor

Choose a reason for hiding this comment

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

The result of both methods should return an Option.

In the actual implementation it seems it does unwrap_or in multiple places , so it can potentially return incorrect result, not the one you expect.

I would change the return value to be a struct. This would help with potential future extension if we want to include additional details.


fn estimate_fee_payment_for_extrinsic(
uxt: BoundedVec<u8, MaxExtrinsicSize>,
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to use boundedVec here and also no need for new constant.

Use the same as it is done in TransactionPaymentAPI.

`uxt: <Block as BlockT>::Extrinsic,`

account_id: AccountId
) -> (AssetId, Balance);
}
}
81 changes: 69 additions & 12 deletions runtime/hydradx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ use sp_std::{convert::From, prelude::*};
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
// A few exports that help ease life for downstream crates.

use frame_support::{construct_runtime, pallet_prelude::Hooks, weights::Weight};
pub use hex_literal::hex;
use orml_traits::MultiCurrency;
Expand All @@ -79,6 +80,12 @@ pub use primitives::{
};
use sp_api::impl_runtime_apis;
pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_runtime::traits::One;


use frame_support::dispatch::GetDispatchInfo;
use frame_support::BoundedVec;
use primitives::constants::transaction::MaxExtrinsicSize;

/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
Expand Down Expand Up @@ -450,19 +457,7 @@ use sp_core::OpaqueMetadata;
use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError;

impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: not sure what happened here but if possible, would you revert these changes

this PR should not touch/change anything else.

fn version() -> RuntimeVersion {
VERSION
}

fn execute_block(block: Block) {
Executive::execute_block(block)
}

fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
Executive::initialize_block(header)
}
}

impl sp_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
Expand Down Expand Up @@ -1186,4 +1181,66 @@ impl_runtime_apis! {
Default::default()
}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
VERSION
}

fn execute_block(block: Block) {
Executive::execute_block(block)
}

fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
Executive::initialize_block(header)
}
}
impl primitives::runtime_api::FeeEstimationApi<Block, AccountId, AssetId, Balance> for Runtime {
fn estimate_fee_payment(weight: Weight, account_id: AccountId) -> (AssetId, Balance) {
// Get the currency configured for this account
let currency = MultiTransactionPayment::account_currency(&account_id);

// Convert weight to base fee using native asset calculations
let native_fee = TransactionPayment::weight_to_fee(weight);

// Get the price of the account's currency if applicable
let price = MultiTransactionPayment::price(currency).unwrap_or(Price::one());
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not correct. if there is not price, it should not return anything, hence the comment about return an option.


// Calculate the final fee in the account's currency
let fee_in_currency = price.checked_mul_int(native_fee).unwrap_or(native_fee);
Copy link
Contributor

@enthusiastmartin enthusiastmartin Dec 19, 2024

Choose a reason for hiding this comment

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

again, if it fails - it returns incorrect info.

after you change, this should look something like :

`price.checked_mul_int(fee).map(|f| f.max(One::one()))`

we dont want to see 0 fee. this would be consistent with other stuff.


(currency, fee_in_currency)
}

fn estimate_fee_payment_for_extrinsic(
uxt: BoundedVec<u8, MaxExtrinsicSize>,
account_id: AccountId,
) -> (AssetId, Balance) {
use codec::DecodeAll;

// Decode the extrinsic
let uxt = UncheckedExtrinsic::decode_all(&mut &uxt[..])
.expect("Invalid extrinsic provided");
Copy link
Contributor

@enthusiastmartin enthusiastmartin Dec 19, 2024

Choose a reason for hiding this comment

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

this could cause panics easily.

there is not need to have boundedvev and and decode stuff. See my other comment to use differetn type for the paramter.


// Get dispatch info for weight calculation
let info = <UncheckedExtrinsic as GetDispatchInfo>::get_dispatch_info(&uxt);

// Calculate length component
let len = uxt.encoded_size() as u32;
let len_fee = TransactionPayment::length_to_fee(len);

// Calculate weight component
let weight_fee = TransactionPayment::weight_to_fee(info.weight);

// Total native fee
let native_fee = len_fee.saturating_add(weight_fee);

// Convert to account's preferred currency
let currency = MultiTransactionPayment::account_currency(&account_id);
let price = MultiTransactionPayment::price(currency).unwrap_or(Price::one());
let fee_in_currency = price.checked_mul_int(native_fee).unwrap_or(native_fee);

Comment on lines +1238 to +1241
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to do this again - just call the other method.

(currency, fee_in_currency)
}
}

}
Loading
Loading