Skip to content

Incentive/staking rewards silently halted — RewardsSource vault (aca/urls) is depleted while emission keeps minting to the Treasury #2984

Description

@AntonioBurgos91

Summary

On Acala mainnet, module-incentives is no longer paying staking/LP rewards, even though the dApp still advertises a non-zero APY (~38% for ACA staking). The pool reward counters are frozen.

Root cause is not a logic bug in the pallet but an operational/funding gap: module_incentives::Config::RewardsSource resolves to the "unreleased native vault" account aca/urls, and that account is empty (~1 ACA). Every accumulate_incentives cycle therefore fails its internal transfer and rolls back silently, so no reward is ever accumulated. Meanwhile the recurring native emission (currencies.updateBalance, ~8.33M ACA / 30 days) is being minted into the Treasury (aca/trsy), not into the rewards vault.

Result: stakers/LPs accrue 0 rewards while the configured IncentiveRewardAmounts (and the UI APY derived from it) remain non-zero — a misleading "phantom APY".

Environment

  • Network: Acala mainnet
  • Observed around block: #11,439,600 (2026-06-28)
  • RPC used: wss://acala-rpc.aca-api.network/ws
  • Tooling: @polkadot/api / @polkadot/api-cli

Observed behavior

  1. rewards.poolInfos({ Earning: { Token: "ACA" } }).rewards.ACA.0 (total accumulated reward) does not change across many AccumulatePeriods (verified unchanged over ~15 min / ~75 blocks while the chain produced blocks normally at ~12s).
  2. incentives.incentiveRewardAmounts({ Earning: { Token: "ACA" } }, { Token: "ACA" }) = 96.45 ACA per 5 blocks — i.e. incentives are still configured as active.
  3. system.account(aca/urls) free balance ≈ 1.0456 ACA, reserved = 0, locks = [], vesting.vestingSchedules(aca/urls) = [].

Expected behavior

With a non-zero IncentiveRewardAmounts, each AccumulatePeriod should transfer the configured amount from RewardsSource to the incentives account and increase poolInfos.totalReward, so stakers' pending rewards grow.

Root cause analysis

RewardsSource is the unreleased native vault:

  • runtime/acala/src/lib.rs: UnreleasedNativeVaultAccountId = PalletId(*b"aca/urls").into_account_truncating();
  • runtime/acala/src/lib.rs: impl module_incentives::Config for Runtime { type RewardsSource = UnreleasedNativeVaultAccountId; ... type UpdateOrigin = EnsureRootOrThreeFourthsGeneralCouncil; ... }

In modules/incentives/src/lib.rs, accumulate_incentives calls transfer_rewards_and_update_records, which is #[transactional]:

T::Currency::transfer(
    reward_currency_id,
    &T::RewardsSource::get(),   // aca/urls
    &Self::account_id(),
    reward_amount,
    ExistenceRequirement::AllowDeath,
)?;
<orml_rewards::Pallet<T>>::accumulate_reward(&pool_id, reward_currency_id, reward_amount)?;

When aca/urls lacks balance, the transfer(...)? fails, the #[transactional] body rolls back, accumulate_reward is never reached, and the caller only logs a log::warn! ("…failed to accumulate…"). The module doc string even states the operational requirement explicitly:

"Rewards come from RewardsSource, please transfer enough tokens to RewardsSource before start incentive plan."

So the pallet behaves as written — the vault simply isn't funded.

Why the funds exist but don't reach the vault

The only forward-scheduled task on chain mints the native emission to the Treasury, not the rewards vault:

scheduler.agenda(#11,582,700) decodes (against runtime metadata) to:

currencies.updateBalance(
    who:      aca/trsy  (Treasury, 23M5ttkmR6KcoTAAE6gcmibnKFtVaTP5yxnY8HF1BmrJ2A1i),
    currency: ACA,
    amount:   +8,333,333.333333333333  ACA
)
origin:   Root
periodic: every 216,000 blocks (~30 days), 35 repetitions remaining

8,333,333.33 × 12 = 100,000,000 ACA/yr (the Exodus emission). Balances on chain:

  • Treasury aca/trsy: ~75.2M ACA (and growing monthly)
  • Incentives aca/inct: ~51.07M ACA (already-accrued/claimable)
  • Rewards vault aca/urls: ~1.05 ACA

So the budget exists; the link from Treasury → aca/urls is simply not being maintained.

Impact

  • Stakers/LPs earn nothing while the UI shows a positive APY (poor UX / misleading signal).
  • IncentiveRewardAmounts remains non-zero, so off-chain APY calculators report a yield that is not paid.

Suggested resolution (no runtime upgrade required)

Either of the following, via UpdateOrigin (Root or 3/4 General Council):

  1. Fund the vault: transfer/updateBalance ACA into aca/urls (one-off or a recurring schedule mirroring the Treasury one) so accrual resumes.
  2. Or wind down explicitly: set IncentiveRewardAmounts to 0 for the affected pools so the dApp stops displaying an unpaid APY.

A short note on whether the Treasury → aca/urls disconnect is intentional would also help clarify the incentive program's status for the community.

Reproduction (Polkadot.js Apps → Developer → Chain state, RPC wss://acala-rpc.aca-api.network/ws)

  • incentives.rewardsSource() → resolves to aca/urls
  • system.account(<aca/urls>) → free ≈ 1 ACA
  • incentives.incentiveRewardAmounts({Earning:{Token:ACA}}, {Token:ACA}) → 96.45 ACA / 5 blocks
  • rewards.poolInfos({Earning:{Token:ACA}}) → read totalReward, re-read a few minutes later → unchanged
  • scheduler.agenda(11582700) → decode inline call → currencies.updateBalance(aca/trsy, ACA, +8.33M), periodic, 35 left
  • system.account(<aca/trsy>) → ≈ 75.2M ACA

UPDATE (2026-07-03)

  • The Council proposal referenced above (0xdc3d2be9..., funding aca/urls with 4,244,541 ACA) expired without a 3rd vote (stuck at 2/3, threshold 3). It is no longer in generalCouncil.proposals.
  • @xlc mentioned a general referendum fallback if the council failed to vote in time. As of now, no such referendum or public proposal exists (democracy.publicProps = 0, active referenda = 0).
  • Re-measured the actual accrual rate over a clean 10-minute window: pending rewards grew by 0.0000 ACA (vs. an expected ~8,571 ACA/day pro-rata to my stake). So the situation is worse than initially described — rewards are not just throttled, they are fully frozen right now. aca/urls remains at ~1.05 ACA.

@xlc could you open the referendum, or is there another path forward?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions