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
forloops 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 aforloop of N iterations costs2 × Ncomputation gas.while,do-while,for-in, andfor-ofloops have no update expression and cost 1 per iteration. Account for the doubled cost when budgeting a gas ceiling for contracts that use indexedforloops.
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:
-
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.
-
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:
- Calculate expected native coin amount:
gas × GAS_PRICE → XCHAIN → USD → native coin - Check the transaction output to the fee destination address
- Validate the paid amount is within the tolerance band (95%-110%)
- 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_pricesfrom 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 orINDEXER_ALLOW_LOCAL_PRICE_SOURCE=trueexplicitly 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 paramsaction,params(pipe-delimited wire params, without the ACTION name),source, and optionalfeeOutputSats. 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
validis the handler’s own verdict against current chain state and the fee is the handler’s own number.supported: falsemeans the action is not quotable here (native fees not enabled on the chain, orXEXEC/BATCH: the first is never wallet-broadcast, the second’s fee is the sum of its sub-actions’ state-dependent fees).valid: nullis the third answer, returned forDEPLOYandEXECUTE. 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: falsecarries the handler’s reason verbatim inerror/status: an oracle price that is missing/stale, a suppliedfeeOutputSatsbelow 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 rarebusy: true+retryable: trueresponse means the quote queue is momentarily full; retry. A client sizes itsFEE_DESTINATIONoutput torequiredFeeSats. -
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, …).
DEPLOYandEXECUTEget a fee but no verdict (seevalid: nullabove);XEXEC/BATCHget 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.