Skip to content

iOS native host: signRawWithLegacyAccount returns Rejected for every failure, even when no sheet was shown #1329

Description

@tommyldev

Problem

In the Polkadot iOS app with the truAPI runtime switch off (the default outside nightly builds), every failure of signRawWithLegacyAccount reaches the product as SigningErr.Rejected. That is the same answer the product gets when the user taps Decline. It happens even when no sheet was ever shown, for example when the requested account isn't the app's main wallet, or when the signing view can't be built or presented.

With the runtime switch on, the same wrong-account case returns an error with a reason ("Account is not available in the active session"). So the product sees a different answer depending on a debug switch.

Where it hurts: t3ams publishes its identity attestation with signRawWithLegacyAccount. On iOS the user sees "Signing was declined." without having declined anything, and release webviews aren't inspectable, so nobody can find the real cause (paritytech/t3ams-spa#524).

Repro (worked out from the code, not run on a device): in a product inside the iOS app (runtime switch off), call getLegacyAccountSigner({ publicKey: <any account that isn't the main wallet> }).signBytes(...). No sheet appears and the call fails with Rejected. Do the same with the runtime switch on and you get the "not available" reason.

Root cause

Refs are at 63a450d, under hosts/ios:

  • polkadot-app/Modules/Products/ProductsNativeApi+Signing.swift:139-153: errors from accountResolver.resolveWallet (IdentityAccountResolverError.accountMismatch) and from sponsorAndPresent (signingUnavailable, parse errors) all go to context.rejectRequest(). That throws ProductNativeApiError.signingRejected, the same error a user decline produces.
  • Packages/Products/product-container/src/index.ts:456-465: catch { return err(new SigningErr.Rejected()); } throws away whatever native returned. handleSignRaw, handleSignPayload and handleSignPayloadWithLegacyAccount do the same (:410, :422, :452). handleCreateTransaction does pass the reason through (:441).
  • The runtime path is correct: rust/crates/truapi/src/runtime/capabilities/signing.rs:512-519 maps an account it can't serve to LEGACY_ACCOUNT_UNAVAILABLE_REASON (runtime.rs:161).

Potential fix

  • Native: use Rejected only when the user actually declines. Return a wrong-account request with the same reason the runtime uses, and return view or presentation failures as an error with their reason.
  • Container: pass the native error's reason through for the signing handlers, as handleCreateTransaction already does.
  • Add a test that a wrong legacy account doesn't come back as Rejected on either path.

Owner

Platform: the iOS host's native signing path.

Activity

  1. added
    bugSomething isn't working
    host-iosTouches the iOS host tree
    on Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghost-iosTouches the iOS host tree

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions