datahaven-xyz/datahaven · error · Error

InvalidTokenTransferFees

InvalidTokenTransferFees

Error message

InvalidTokenTransferFees

What it means

The system pallet's `set_token_transfer_fees` extrinsic throws `Error::InvalidTokenTransferFees` when fee validation fails: `create_asset_xcm` and `transfer_asset_xcm` must be non-zero, and `register_token` must be greater than `meth(100)` (100 units, intentionally expensive to deter token-registration spam). Fees that are zero or too cheap are rejected before dispatching the SetTokenTransferFees command to Ethereum.

Solutions

  1. Pass non-zero values for create_asset_xcm and transfer_asset_xcm.
  2. Ensure register_token is denominated correctly and exceeds meth(100); recompute from the fee policy before dispatching.
  3. Convert units explicitly (e.g. multiply decimals) in the proposal script to avoid denomination mistakes.

Example fix

// before
let register_token = U256::from(100); // way below meth(100) minimum
System::set_token_transfer_fees(origin, U256::one(), U256::one(), register_token)?;

// after
let one_hundred = meth(100); // e.g. 100 * 10^18
assert!(create_asset_xcm > 0 && transfer_asset_xcm > 0 && register_token > one_hundred);
System::set_token_transfer_fees(origin, create_asset_xcm, transfer_asset_xcm, register_token)?;
Defensive patterns

Strategy: validation

Validate before calling

if (createAssetXcm === 0n || transferAssetXcm === 0n || registerToken <= meth(100)) throw new Error('fees fail policy');

Type guard

function feesSatisfyPolicy(c, t, r) { return c > 0n && t > 0n && r > meth(100); }

Try / catch

try { await api.tx.system.setTokenTransferFees(c, t, r).signAndSend(root); } catch (e) { if (String(e).includes('InvalidTokenTransferFees')) { recalcFees(); } else { throw e; } }

Prevention

When it happens

Trigger: Calling `set_token_transfer_fees(origin, create_asset_xcm, transfer_asset_xcm, register_token)` with root origin where either XCM fee is 0, or `register_token <= meth(100)` (i.e. at most 100 ETH-equivalent in the configured unit).

Common situations: Governance proposals setting fees in the wrong denomination (e.g. wei vs whole units) making register_token far below the 100-unit floor; typos passing 0 for an XCM fee; scripts copying fees from another chain with different units.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13). Data as JSON: /api/errors/7e8d4c7850d54b73. Report an issue: GitHub.

Appendix: source

Thrown at operator/pallets/system/src/lib.rs:374

        /// - `origin`: Must be root
        /// - `create_asset_xcm`: The XCM execution cost for creating a new asset class on AssetHub,
        ///   in DOT
        /// - `transfer_asset_xcm`: The XCM execution cost for performing a reserve transfer on
        ///   AssetHub, in DOT
        /// - `register_token`: The Ether fee for registering a new token, to discourage spamming
        #[pallet::call_index(9)]
        #[pallet::weight((T::WeightInfo::set_token_transfer_fees(), DispatchClass::Operational))]
        pub fn set_token_transfer_fees(
            origin: OriginFor<T>,
            create_asset_xcm: u128,
            transfer_asset_xcm: u128,
            register_token: U256,
        ) -> DispatchResult {
            ensure_root(origin)?;

            // Basic validation of new costs. Particularly for token registration, we want to ensure
            // its relatively expensive to discourage spamming. Like at least 100 USD.
            ensure!(
                create_asset_xcm > 0 && transfer_asset_xcm > 0 && register_token > meth(100),
                Error::<T>::InvalidTokenTransferFees
            );

            let command = Command::SetTokenTransferFees {
                create_asset_xcm,
                transfer_asset_xcm,
                register_token,
            };
            Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::<T>::No)?;

            Self::deposit_event(Event::<T>::SetTokenTransferFees {
                create_asset_xcm,
                transfer_asset_xcm,
                register_token,
            });
            Ok(())
        }

View on GitHub (pinned to edcb13dbbc)