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.
Problem
In the Polkadot iOS app with the truAPI runtime switch off (the default outside nightly builds), every failure of
signRawWithLegacyAccountreaches the product asSigningErr.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 withRejected. 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 fromaccountResolver.resolveWallet(IdentityAccountResolverError.accountMismatch) and fromsponsorAndPresent(signingUnavailable, parse errors) all go tocontext.rejectRequest(). That throwsProductNativeApiError.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,handleSignPayloadandhandleSignPayloadWithLegacyAccountdo the same (:410,:422,:452).handleCreateTransactiondoes pass the reason through (:441).rust/crates/truapi/src/runtime/capabilities/signing.rs:512-519maps an account it can't serve toLEGACY_ACCOUNT_UNAVAILABLE_REASON(runtime.rs:161).Potential fix
Rejectedonly 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.handleCreateTransactionalready does.Rejectedon either path.Owner
Platform: the iOS host's native signing path.