XChain Platform Action - COLLECT
This action collects all accrued validator rewards to the broadcasting address.
PARAMS
| Name | Type | Description |
|---|---|---|
VERSION |
String | Format Version |
AMOUNT |
String | Optional trailing partial-claim amount; absent = claim the full unclaimed total |
Formats
Version 0
VERSION[|AMOUNT]
Examples
COLLECT|0
Collect all accrued validator rewards for the broadcasting address
COLLECT|0|25
Collect 25 XCHAIN of the accrued rewards; the remainder stays pending and collectible later
Partial Claim (optional AMOUNT)
The trailing AMOUNT is activated by the PARTIAL_UNSTAKE_COLLECT protocol change (mainnet: the coordinated contract-era flag day; testnet/regtest: genesis). Semantics:
- Absent: claim the full unclaimed total, byte-identical to the historical behavior
- Present (at/after the flag-day): claim only
AMOUNT; the remainder stays pending - Present (below the flag-day): ignored (full claim), matching what a pre-upgrade node parses
- An
AMOUNTequal to the full unclaimed total is treated exactly as absent - An
AMOUNTof zero, malformed, finer than 8 decimals, or greater than the unclaimed total is rejected; over-asks are never clamped - The reward-pool coverage check applies to the claimed amount, so a partial claim can succeed while the pool cannot yet cover the full pending total
Rules
- BTC chain only
- Broadcasting address must have an active stake (gated by the 6-block activation delay)
- Broadcasting address must have unclaimed rewards greater than 0
- Rewards are paid by debiting the reward pool address and crediting the broadcasting address; XCHAIN is never minted by
COLLECT - The reward pool must hold enough XCHAIN to cover the full claim, or the
COLLECTis rejected (see Reward Funding)
Reward Sources
Rewards accumulate from multiple validator activities, all stored in the indexer’s validator_rewards table:
| Reward Type | Earned By | Trigger |
|---|---|---|
oracle_round |
Validator with price capability |
Legacy label (used when FULLNODE.REWARD_SHARE is 0): signature included in the on-chain PRICE v0 action of a finalized price round; the full per-round budget is split equally across all qualified signers |
oracle_base |
Validator with price capability |
Active-regime label (used when FULLNODE.REWARD_SHARE > 0): the base tranche of the per-round budget, split equally across all qualified signers; replaces oracle_round once the full-node reward tier is activated |
oracle_full_node |
Validator with price and full_node capabilities |
Active-regime only: the full-node tranche of the per-round budget, split equally across verified full-node sources that signed the round and met the trailing MIN_PASS_RATE_BPS participation threshold |
attest_fee |
Validator with attestation capability |
Share of the request fee for a fulfilled ATTEST request |
anchor_<chain> |
Validator with oracle_publish capability |
Publishing a per-chain ANCHOR v0 checkpoint |
anchor_archive |
Validator with oracle_publish capability |
Publishing an ANCHOR v1 archive batch |
Reward Population Path
Reward rows reach the indexer’s validator_rewards table on two rails:
- Derived (replayable):
oracle_round/oracle_base/oracle_full_nodeandattest_feeare computed by the indexer itself during block processing, as deterministic functions of on-chain actions. The oracle reward type used depends on whether the full-node reward tier is active (FULLNODE.REWARD_SHARE> 0): when inactive the full per-round budget is credited asoracle_round; when active it is split into anoracle_basetranche (all qualified signers) and anoracle_full_nodetranche (verified full-node sources that met the participation threshold).attest_feesplits a fulfilled request’s fee across its responsible set. A reindex reproduces these rows exactly. - Pushed (archived):
anchor_<chain>/anchor_archiveare recorded by the hub federation when an anchor publishes and pushed via thepushvalidatorrewardsJSON-RPC endpoint (which rejects any non-anchor type). Because a chain parse cannot re-derive them, they ride the ANCHOR v1 archive and are restored by full-parse recovery (see ANCHOR.
COLLECT queries the indexer’s validator_rewards table directly. No hub round-trip during transaction processing.
Replayability
COLLECT validation sums unclaimed rewards earned at or before the COLLECT’s own block (validator_rewards.block_index <= BLOCK_INDEX). The scope is a no-op live (rows never carry a future block), but it makes every historical claim replay identically on a reindex or ANCHOR full-parse recovery, bulk-restored reward rows can never become visible to an earlier COLLECT than they were when it confirmed.
Reward Funding
XCHAIN is a fixed-supply token (minted once at genesis, then locked (see GAS). Rewards are therefore not minted; they are paid out of a dedicated reward pool address (config['ADDRESS']['REWARD'], BTC only). A valid COLLECT debits the pool for the reward amount and credits the broadcasting address, leaving total XCHAIN supply unchanged.
The pool is seeded at genesis and topped up manually (an ordinary XCHAIN SEND to the pool address) by the operator. Because the balance check reads the pool at the action’s block/action index, every validator computes the same accept/reject outcome.
If the pool cannot cover the full pending reward, the COLLECT is rejected with invalid: insufficient reward pool. The claim is recorded as invalid, so the reward remains unclaimed and fully collectible later; the validator simply re-broadcasts COLLECT once the pool has been replenished. No rewards are lost or partially paid.
flowchart TD
D1["Indexer computes oracle_round / oracle_base /<br>oracle_full_node / attest_fee during block processing"]
P1["Hub federation records anchor_CHAIN / anchor_archive<br>reward on publish"]
P2["Pushed via pushvalidatorrewards JSON-RPC"]
VR[("validator_rewards table")]
C1["COLLECT sums unclaimed rewards<br>at or before its own block"]
C2{"Reward pool holds<br>enough XCHAIN?"}
C3["Debit reward pool address,<br>credit broadcasting address"]
C4["Rejected: insufficient reward pool<br>(reward stays unclaimed, collectible later)"]
D1 -->|"derived, replayable"| VR
P1 --> P2
P2 -->|"pushed, archived via ANCHOR v1"| VR
VR --> C1
C1 --> C2
C2 -->|"yes"| C3
C2 -->|"no"| C4
Notes
- Rewards accrue continuously while the address holds an active stake
- By default all pending rewards are collected in a single action; a partial claim is available via the optional trailing
AMOUNT(see Partial Claim above) - Rewards may be collected at any time while a stake is active
- Rewards can also be collected after initiating
UNSTAKEduring the cooldown period - A single pubkey can earn from multiple capabilities in the same round, e.g. a validator with both
priceandoracle_publishcapabilities can earn the per-round consensus reward AND the per-publish broadcast reward in the same round
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.