Repository navigation
DOGE: native-fee output under 0.01 DOGE is emitted without the soft-dust fee, so the tx sits in the mempool and is never mined #23
Description
What happens
On Dogecoin, the native-coin protocol fee is added to the tx as a feeQuote / FEE_DESTINATION output. When the quoted fee is under 0.01 DOGE, that output is a soft-dust output, and the encoder builds the tx with the normal miner fee only. The tx is accepted by our testnet node but no miner picks it up.
Seen on DOGE testnet, 2026-10-15, placing a BET from the wallet (BetFeedDetail form, market #5435):
| tx | size | protocol-fee output | miner fee | rate | result | |
|---|---|---|---|---|---|---|
| BET placed from the BET form | 6f5cd285f5ab63de40c8947c0e72a8738d182d79fb96523b3cd9344e58000eb5 |
255 B | 0.00986772 DOGE | 0.00702325 DOGE | 0.0275 DOGE/kB | in the node mempool since block 67998665, still unmined 260+ blocks later |
| Same action (BET v2), Advanced action form, Normal fee | bc718dcef8e22f4f711f9c6f156c143b4daf09307703cba7ad0a807fae7e77c0 |
same shape | 0.00984791 DOGE | 0.296 DOGE (from the Confirm screen) | ~1.1 DOGE/kB | mined about 2 minutes later, BET #5439 valid |
The rate is not what keeps the first one out: the market-creation tx for the same market (c6147ce7…, 332 B, no fee output) paid only 0.0124 DOGE/kB and was mined, and the stuck BET pays 0.0275 DOGE/kB. The only thing the stuck tx has that the mined low-rate one does not is the sub-0.01 output with a miner fee under 0.01 DOGE.
The stuck tx also holds the sender's only UTXO, so that address cannot do anything else until it expires.
Cause (what I could confirm in code)
xchain-encoder already documents the rule, and has a test for it on outputs it authors itself:
SOFT_DUST_FLOOR_BY_COINcomment: Dogecoin relays an output under its 0.01 DOGE soft dust limit only if the whole limit is added to the fee. (src/XChainEncoder/constants.jsL54-L59)test/unit/xchain_encoder/xchain_encoder_soft_dust_floor.test.js: "...per output".
But the native-fee output is not an output the encoder authors with that floor:
- It is appended as a custom output:
customOutputs.push({ address: feeQuote.address, value: feeQuote.amount })(src/XChainEncoder/build_transaction/payload_checks.jsL82). - Custom outputs are only rejected when
value <= 0, deliberately, so that "sub-dust-but-positive DOGE fee values keep working" (src/common/validator/fee_and_utxo_checks.jsL225-L229,src/XChainEncoder/build_transaction/data_outputs.jsL302-L303). - The fee-rate resolution only uses the node estimate (clamped on test chains) and never adds anything per soft-dust output (
src/XChainEncoder/build_transaction/fee_rates.js).
So the output value is allowed to be sub-dust (it has to match the indexer's quoted fee), but the miner fee is never raised by 0.01 DOGE to pay for it.
Reproduction (encoder alone, no chain)
xchain-encoder at origin/master 8033833 (v0.23.0), network dogecoin-testnet, one 0.9676 DOGE input, node estimate 0.01 DOGE/kB, 296-byte tx, createTransaction(...) with feeQuote = { address, amount }:
feeOutput=986772 sat outs=[0,986772,95478248] minerFee=296000 sat
feeOutput=1000000 sat outs=[0,1000000,95465020] minerFee=296000 sat
feeOutput=1500000 sat outs=[0,1500000,94965020] minerFee=296000 sat
The miner fee is the same whether the fee output is 0.00987, 0.01 or 0.015 DOGE. Nothing is added for the sub-0.01 one.
Not confirmed
- I did not capture which node or relay hop drops it. Our own node (
/Shibetoshi:1.14.9/, testnet) accepts it into its mempool; the rule above is the one the encoder's own comment and test describe. - The mined control differs from the stuck tx in miner fee by about 40x, not only by the 0.01 DOGE; I did not isolate "exactly 0.01 DOGE more" from "a higher rate". The market-creation tx above is the evidence that the rate alone is not the cause.
Impact
Any DOGE action paid with a native-coin protocol fee under 0.01 DOGE (cheap actions at current prices; the BET above was 0.0099) can end up stuck, and the wallet gives no sign of it beyond "waiting for a block". Fee-bearing actions on LTC/DOGE are native-fee only (see also xchain-sdk #22 / #25).
Related, wallet side (separate issue if you prefer)
BetFeedDetail.jsx and CreateBetFeedForm.jsx have no FeeSelector, and betActions.js only forwards fee/feePerKb if given, so BET place / create always use the encoder default (0.0275 and 0.0124 DOGE/kB observed here, vs 1 DOGE/kB on forms with the selector). Adding the missing 0.01 DOGE in the encoder would also make those forms' txs mineable.
Activity
javiervarona commentedon Oct 19, 2026
Follow-up with a control case, same action and same form, on DOGE testnet.
On 2026-10-19 I placed another BET from the wallet's BET form (market #5584, BET #5585). It differs from the stuck one in the protocol-fee output, which this time was above 0.01 DOGE:
6f5cd285f5ab63de40c8947c0e72a8738d182d79fb96523b3cd9344e58000eb5a058e41e30eee3121e4cabe44332ccd6fff0292dea87b63ba07bc8a9f565f1e0The control pays about half the rate per kB of the stuck one and was mined, so the rate alone does not explain the stuck tx. The only thing that differs between the two is which side of 0.01 DOGE the fee output falls on. This is consistent with the soft-dust rule described in the issue. It still does not isolate "the missing 0.01 DOGE" as the exact cause: I could not force the protocol fee below 0.01 DOGE to repeat the stuck case, because the quote follows the DOGE price (0.0099 on 10-15, 0.0101 on 10-19).
One more data point on the wallet side, since this affects whether users can work around it: forms without a fee selector (BET, create market, LIST rename, and others) all send at about 0.013 DOGE/kB, so the missing soft-dust fee and the low default rate add up. That part is in XChain-Platform/xchain-wallet#89.