paritytech / paritytech/host-rust-core

Re-introduce RFC-006 payment.topUp / balanceSubscribe on the TrUAPI runtime (iOS + Android)

Open
#778 0 comments 0 reactions 1 assignee View on GitHub

@pgherveou is already working on this.

Since Sep 17, 2026.

Coinage R2 host-work Humanity R2 blocker
Dominant language
Swift
Stars
10
Forks
3
Avg merge
1d 10h
Merged PRs (30d)
148

Description

Background

Product top-up used to work. Both phones implemented paymentTopUp and the balance subscription behind the native product container: iOS ProductsNativeApi+Payment.swiftIncomingPaymentService (polkadot-ios-community#85 "Top up durability"), Android HostApiInteractor.resolveTopUpSourceTopUpSource.Onboard (polkadot-app-android-v2#1002). A product handed over ProductAccount { derivationIndex }, the host watched the account, signed Coinage.load_recycler_with_external_asset_unpaid from it, and the money showed up in the user's coinage balance.

TrUAPI is now the standard product runtime (iOS Nightly defaults to it; Android is landing its binding). On that runtime the payment surface was never carried over: truapi-server's Payment::top_up answers Unknown("Payments are not supported in dot.li") and balance_subscribe answers PermissionDenied (runtime/capabilities/payment.rs), and the iOS RustHostRuntimeBridge exposes no payment hooks that could override it. The working implementation is still in the apps, one runtime over, unreachable from a TrUAPI product.

Why it matters now

Humanity's Daily Dollar pays each winner's prize on chain into the product account //product//peopl.<tld>/0 as a pUSD balance. Coinage has no deposit address, so the only way that prize becomes spendable is the host's payment.topUp({ source: ProductAccount { derivationIndex: 0 } }). Humanity calls it right after a claim and again at every app start (humanity-spa#134, #136, #137) — and every call dies at the stub.

On paseo-next-v2 the test identity's prize account 5CXjDEKmQhsRKPwbR8eCjBio8Bt4Gv6J9UkkA7uHCkE9hGG7 holds 4 pUSD from four claimed draws and has never signed anything. Every dollar a user wins is sitting on an account no wallet shows.

Ask

Re-introduce on the TrUAPI runtime, on iOS and Android, routed to the top-up the apps already have:

  • payment.topUp with the ProductAccount source
  • payment.balanceSubscribe

payment.request / statusSubscribe are not needed for this.

To decide along the way

  • The native container's paymentTopUp took a 32-byte top-up id and offered topUpStatusSubscribe (Detecting → Claimed / ClaimedPartially / NotClaimed). HostPaymentTopUpRequest in @parity/truapi has neither. Either the bridge makes up an id per call, or RFC-0006 grows id + a status subscription so a product can learn a top-up ended NotClaimed.
  • ProductAccount must resolve to the same account truapi-server/src/host_logic/product_account.rs derives (soft junction u32 LE ++ blake2("product-account-index")[..28]). iOS's ProductAccountId.derivationPath() already matches.

Related

  • #334 — iOS integration tracker; the unchecked line "Route payment functions and subscriptions for products … through the native/shared core" is this.
  • paritytech/humanity-spa#134, #136, #137 — the product side.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.