Gas and Fees

XChain uses a unified gas-based fee system. All protocol fees are expressed in gas units, converted to XCHAIN via a single GAS_PRICE parameter, and paid in either native coin (BTC/LTC/DOGE) via oracle price conversion or XCHAIN balance deduction (BTC only).

Fee Conversion Paths

Native coin payment (all chains):

flowchart LR
    GC["gas cost"] --> GP["GAS_PRICE"] --> XA["XCHAIN amount"] --> XU["XCHAIN/USD oracle"] --> USD["USD"] --> UC["USD/coin oracle"] --> NC["native coin"]

XCHAIN balance payment (BTC only):

flowchart LR
    GC2["gas cost"] --> GP2["GAS_PRICE"] --> XA2["XCHAIN amount"] --> DB["debit from user's<br>XCHAIN balance"]

On BTC, the indexer uses implicit detection: if the transaction includes a native coin output to the fee destination address, it’s validated as native coin payment against the oracle. If there is no fee output, the indexer debits XCHAIN from the user’s balance. On LTC/DOGE, native coin payment is the only option; a missing fee output means the action is rejected.

The fee destination address is the per-network ADDRESS.FEE_DESTINATION value from the bundled coin registry (pinned per coin and network). On testnet and regtest it can be redirected at runtime with the XCHAIN_FEE_DESTINATION_<COIN>_<NETWORK> environment variable (e.g. XCHAIN_FEE_DESTINATION_DOGE_TESTNET); on mainnet the override is ignored with a warning, because fee acceptance is consensus-relevant and must not depend on operator environment. If the registry value were the unset placeholder, native-coin fee detection would be disabled and the indexer would fall back to XCHAIN-balance deduction on BTC (and reject fee-bearing actions on LTC/DOGE).

GAS_PRICE

Parameter Value Notes
GAS_PRICE 0.00001 XCHAIN/gas Governance-adjustable. Single lever to scale all fees.
FEE_PAYMENT_MODE xchain or native (per chain) Informational per-chain config. xchain = accepts both XCHAIN-balance and native-coin payment (BTC). native = native-coin payment only (LTC, DOGE). Readable by wallets and the SDK via GET /{COIN}/api/feeschedule.

Anchor: ISSUE = 100,000 gas = 1.0 XCHAIN at initial GAS_PRICE.

Complete Fee Schedule

Platform Action Fees

Action Gas Cost XCHAIN (initial) Notes
ISSUE 100,000 1.0 Same on all chains
Sub-token ISSUE 50,000 0.5 Half of ISSUE
ORDER/DISPENSER/SWAP expiration 550/day ~0.0055/day 90-day free period
AIRDROP 100/recipient 0.001/recipient 1,000 recipients = 1 XCHAIN
DIVIDEND 100/recipient 0.001/recipient Same as AIRDROP

VM Fees

Operation Gas Cost XCHAIN (initial) Notes
EXECUTE base fee 1,000 0.01 Minimum to invoke any contract
DEPLOY base fee 100,000 1.0 Permanent state: on par with ISSUE
DEPLOY per byte 10 None 10KB = 100,000 extra gas; 64KB max
State read 100 0.001 Single indexed DB lookup
State write 200 0.002 2x read
State delete 100 0.001 Same as read
Oracle read 100 0.001 Same as state read
Cross-chain read 100 0.001 Cross-chain state read (same cost as local state read)
Action emission 500 0.005 Anti-spam for emitted actions
Attestation request 5,000 0.05 Covers off-chain data request overhead (attestation.request)
Cross-chain call request 2,000 0.02 Additional fee on top of action emission for emit.crossExecute(); the federation relay work. The call also pre-pays its remote gasLimit plus the cross-chain callback ceiling, with no refund of unused remote gas
Cross-chain callback ceiling 20,000 0.2 Fixed gas ceiling the result/expiry callback runs against on the source chain, pre-paid at emit.crossExecute() time
Computation 1/instruction None Metered by isolated-vm; one charge per control-flow point

Indexed for loops are charged twice per iteration. The gas meter injects a control-flow charge at the top of the loop body and a second charge into the update expression, so a for loop of N iterations costs 2 × N computation gas. while, do-while, for-in, and for-of loops have no update expression and cost 1 per iteration. Account for the doubled cost when budgeting a gas ceiling for contracts that use indexed for loops.

Hard Caps (Primary Spam Deterrent)

  • Max 50 emitted actions per execution
  • Max 10,000 state keys per contract
  • Max 64KB state value per key
  • 30-second wall-clock safety-net timeout per execution (see Execution Termination; the gas ceiling is the primary limit and halts normal contracts in well under 1 second)
  • 8MB memory limit per isolate
  • Max 64KB contract code size

Execution Termination

Contract execution is bounded by two independent mechanisms operating at different tiers:

  1. Gas exhaustion (primary). Every instruction and every host operation (state reads/writes, oracle reads, action emissions) consumes gas, metered by isolated-vm. When a contract’s gas budget is depleted, execution halts immediately. This is the enforced limit that governs the common case: a normally-behaving contract terminates here, typically in well under 1 second of wall-clock time. Gas is the spam and resource-abuse deterrent, see the Hard Caps and fee schedule above.

  2. Wall-clock timeout (secondary safety net). A 30-second wall-clock limit backstops gas metering. It only fires when the gas ceiling fails to terminate execution in real time, for example, a contract engineered to maximize wall-clock time per unit of gas (calling host operations that are cheap in gas terms but slow in wall-clock terms). Under normal operation this limit is never reached; it exists solely to guarantee an execution cannot run unbounded if gas accounting is somehow outpaced.

The 30-second value is deliberately generous so that legitimate contracts are never killed prematurely on slower hardware (e.g. regtest machines), leaving the gas ceiling to enforce the practical limit.

Pathological case: because the safety net is set to 30 seconds, a contract specially crafted to burn wall-clock time cheaply relative to gas can hold a single indexer worker process busy for up to 30 seconds per EXECUTE. The gas ceiling makes this expensive to attempt at scale, but operators should be aware that the wall-clock backstop (not 100ms) is the true upper bound on a single execution’s duration.

Expiration Free Period

The first 90 days of any ORDER/DISPENSER/SWAP are free. Fees only apply to days beyond 90:

  • 90-day order (default): 0 gas (free)
  • 180-day order: 49,500 gas (0.495 XCHAIN)
  • 365-day order: 151,250 gas (~1.51 XCHAIN)

Example Costs (at GAS_PRICE = 0.00001, XCHAIN = $1)

Scenario Gas XCHAIN USD
ISSUE a token 100,000 1.0 $1.00
90-day ORDER (default) 0 0 $0
1-year ORDER 151,250 ~1.51 $1.51
AIRDROP to 1,000 recipients 100,000 1.0 $1.00
Simple contract call ~2,600 0.026 $0.026
Deploy 10KB contract 200,000 2.0 $2.00

Fee Collection and Distribution

Fees can be paid two ways. Native-coin fees (BTC/LTC/DOGE) are collected at the per-chain fee destination address. XCHAIN-balance fees are routed by the ADDRESS action’s FEE_PREFERENCE field:

Bucket FEE_PREFERENCE Purpose
Destroy (burn) 1 Permanently removes XCHAIN from supply (deflationary)
Protocol Development 2 (default) Funds ongoing platform development
Community Development 3 Community grants, ecosystem growth

The XCHAIN Token

XCHAIN is a standard XChain token issued via ISSUE on the BTC chain only. It does not exist natively on LTC or DOGE. The XCHAIN ticker is reserved on all chains to prevent unauthorized issuance.

Fixed supply, zero pre-mint. XCHAIN has a permanent MAX_SUPPLY cap set at genesis, but supply starts at zero: there is no pre-mint and no operator allocation. Supply is created only through public mints during the launch window, up to the cap; once the mint window closes, no further XCHAIN can ever be created, by anyone, including the issuing address. Supply only ever decreases after that, via the burn bucket above.

Validator rewards are paid from a dedicated, pre-funded reward pool address. Never by minting. The pool is seeded at genesis and topped up manually over time. When the pool cannot cover a pending reward, the COLLECT is rejected and the reward stays claimable until the pool is replenished (see COLLECT).

XCHAIN’s value is driven by:

  • Staking demand: validators must stake XCHAIN (1,000 for oracle, 5,000 for cross-chain)
  • Fixed, capped supply: no inflation; rewards are paid from a finite pool, and the burn bucket is deflationary

Oracle Price Validation

When a user pays a fee in native coin, the indexer validates the payment against the decentralized oracle:

  1. Calculate expected native coin amount: gas × GAS_PRICE → XCHAIN → USD → native coin
  2. Check the transaction output to the fee destination address
  3. Validate the paid amount is within the tolerance band (95%-110%)
  4. Record the fee with the oracle round reference

The tolerance band accounts for price movement between transaction creation and confirmation.

Operator note (price source): This validation reads price_snapshots / oracle_prices from the configured hub DB (HUB_DB_HOST / HUB_DB_NAME), or, when those are unset, from the indexer’s own database. The local-DB fallback is correct only for a single-host node whose local DB holds the synced hub copy. To prevent a distributed node from silently valuing fees against stale or empty local price data (a consensus-divergence risk), a mainnet indexer fails closed at startup unless either the hub DB is configured or INDEXER_ALLOW_LOCAL_PRICE_SOURCE=true explicitly acknowledges an intentional single-host setup. testnet/regtest only log a warning. See the indexer configuration reference.

Native-coin fees are non-refundable

A native-coin fee is a real on-chain output paying the fee destination, settled in the same transaction as the action. If the action then fails validation (e.g. the ticker is already taken, the parameters are invalid, or the fee is underpaid), the indexer marks the action invalid but cannot refund the native coin; it is already final on-chain and is forfeited to the fee destination. This differs from XCHAIN-balance fees, which are only debited when the action succeeds. Clients (wallet/SDK) should pre-validate an action and confirm the fee amount against the current oracle price before broadcasting with a native-coin fee, so users never pay for an action that will fail.

For a large action that the encoder splits across a P2SH commit + reveal transaction pair, place the native-coin fee output on the commit (first) transaction. The commit always confirms before the reveal (the reveal spends it), so the fee is fully received before the action is processed; the decoder attributes the commit’s fee output to the reveal action.

Client pre-validation (sizing the fee + refusing doomed transactions)

Because a native-coin fee is forfeited if the action fails, clients should never broadcast one they can’t price. The indexer exposes a read-only pre-flight that computes the fee against current chain state + current oracle prices without persisting anything, surfaced publicly through the explorer (the indexer itself is not internet-facing):

  • GET /{COIN}/api/feequote: query params action, params (pipe-delimited wire params, without the ACTION name), source, and optional feeOutputSats. Returns:

    {
      "supported": true, "valid": true, "error": null,
      "status": "valid", "validated": true,
      "xchainFee": "1.00000000",
      "requiredFeeNative": "0.00002000", "requiredFeeSats": 2000,
      "minAcceptable": "0.00001900", "maxAcceptable": "0.00002200",
      "feeDestination": "<address>", "oracleRound": 42,
      "toleranceMin": "0.95000000", "toleranceMax": "1.10000000"
    }
    

    The quote runs the REAL action handler inside a forced-rollback dry-run, so valid is the handler’s own verdict against current chain state and the fee is the handler’s own number. supported: false means the action is not quotable here (native fees not enabled on the chain, or XEXEC/BATCH: the first is never wallet-broadcast, the second’s fee is the sum of its sub-actions’ state-dependent fees). valid: null is the third answer, returned for DEPLOY and EXECUTE. These never run in the public pre-flight (they would execute caller-supplied code on a shared node), so they are priced straight from the gas schedule instead: staticQuote: true, validated: false, and a fee that is exactly what the chain checks the native output against, with NO validity verdict attached. Size the output from it and broadcast, but tell the user the action itself is unverified. valid: false carries the handler’s reason verbatim in error/status: an oracle price that is missing/stale, a supplied feeOutputSats below the acceptance floor, or the action itself being one the chain would reject (insufficient balance, taken ticker, expired order, …), which would forfeit the native fee if broadcast. A rare busy: true + retryable: true response means the quote queue is momentarily full; retry. A client sizes its FEE_DESTINATION output to requiredFeeSats.

  • GET /{COIN}/api/feeschedule: the gas schedule, GAS_PRICE, tolerance band, fee destination, and the latest XCHAIN/USD + COIN/USD oracle prices (with a freshness flag) for display / rough estimates.

The xchain-sdk wraps this: sdk.quoteNativeFee(actionData, { source }) returns the quote, and sdk.estimateFees(actionData, { payFeeInNativeCoin: true, ... }) calls the quote, sizes the FEE_DESTINATION output via the encoder’s customOutputs, and throws rather than build a transaction that can’t be priced. The xchain-wallet enforces the same gate at its broadcast chokepoint and warns the user that a native-coin fee is forfeited if the transaction is rejected.

Phase-2 scope (live): the pre-flight covers every action except the VM/compound set. SEND, edits, AIRDROP, DIVIDEND and the rest are quoted by the real handler, including failure reasons a fee estimator could never see (insufficient balance, taken ticker, …). DEPLOY and EXECUTE get a fee but no verdict (see valid: null above); XEXEC/BATCH get neither. On LTC/DOGE the native output is the ONLY way to pay a protocol fee (no XCHAIN fee lane), so “not quotable” there means “not payable”: that is why the two VM actions are priced statically rather than refused.

Governance

All fee parameters are governance-adjustable via the hub’s PBFT voting mechanism:

Parameter Adjustment
GAS_PRICE Scales all fees proportionally
Individual gas costs Fine-tune specific action costs
Tolerance band Adjust fee validation window
Free period Adjust expiration fee free days

See also: Tokens | Ledger | Smart Contracts | Cross-Chain


Copyright © 2025–2026 Dankest, LLC

Based on XChain Platform by Dankest, LLC – https://dankest.llc

Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.

You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.

Edit this page on GitHub ↗