Skip to content

The airtime flow sends people to a screen that does not exist #1200

Open

Description

@Miracle656

Background

frontend/mobile/app/airtime.tsx:256:

const continueFromForm = useCallback(async () => {
  if (await isNairaVerified()) {
    setStep('confirm');
    return;
  }
  router.push({
    pathname: '/verify',
    params: { returnTo: '/airtime', label: 'Airtime', summary: `Airtime · ₦${ngn} to ${phone}` },
  });
}, [router, ngn, phone]);

There is no verify screen in frontend/mobile/app. The web wallet has
app/verify/; the app does not, anywhere, including inside route groups — I
checked every navigation target in the app against the files that exist and
this is the only one with nothing behind it.

So an unverified user who fills in the airtime form and presses continue is
pushed to an unmatched route. They have done nothing wrong, the app is working
as designed up to that line, and the KYC step they need is unreachable.

It fires at the worst possible moment: the first time someone tries to spend.

What to build

  • A verify screen in the app that accepts the returnTo, label and
    summary params this call already passes, and returns the user to where they
    were on success.
  • The same flow is needed by every rail that requires verification, so build it
    as the shared destination rather than an airtime-specific screen. Check which
    other screens need it before choosing the shape.
  • Handle the reachable failures honestly: verification declined, already
    verified, and the provider being unreachable are three different things.

The NIN is personal data. Submit it and forget it — do not store it, do not
log it, do not keep it in component state longer than the request needs.

Acceptance criteria

  • /verify resolves to a real screen in the app
  • returnTo, label and summary are honoured, and success returns the
    user to the flow they left
  • Declined, already-verified and provider-unreachable are distinguished
  • No NIN is logged, persisted or left in storage
  • A test asserts the navigation target exists, so this cannot regress

Drips Wave · Complexity: Intermediate · 150 points


Repo: Miracle656/veil. The web wallet is frontend/wallet (Next.js), the
app is frontend/mobile (Expo / React Native), the SDK is sdk/, contracts are
contracts/ (Rust / Soroban). The backend is the sibling repo
Miracle656/wraith.

Run things: npm test in frontend/wallet or frontend/mobile (jest; the
app uses jest-expo), cargo test in contracts/. Install the app with plain
npm install
— never --legacy-peer-deps, which corrupts that lockfile. The
SDK needs npm install --legacy-peer-deps, because of a pre-existing
react18/react-dom19 mismatch on main.

This wallet holds real money on mainnet. Keys derive from a passkey, so a
mis-derivation loses funds. Never weaken a test to make a signing path pass.

Never elide an address, a contract id or a key — not in code, not in a
comment, not in sample output. A previous batch shortened a contract id and five
contributors invented plausible ones to fill the gap. Write them in full or not
at all. This applies to a 56-character Stellar public key in particular: it is
easy to truncate one by accident and hard to notice.

A NIN, a phone number and a meter number are personal data under Nigeria's
NDPA. They are never stored, never logged, and never put in a fixture.

Write tests that say what they defend. A one-line comment naming what breaks
if the assertion fails is what stops the next person deleting it.



Required: Before submitting, join the contributor Telegram so your work can be tracked and counted toward the Stellar Wave: https://t.me/+fxHXq8f1SwlkZDBk

Activity

  1. Osaodion commented on Oct 21, 2026

    @Osaodion

    @Osaodion has applied to work on this issue as part of the Stellar Wave Program's 10th wave.

    Hi maintainer, I would love to work on this issue. Assign me please

    ℹ️ Repo Maintainers: To accept this application, review their application or assign @Osaodion to this issue.

  2. samkay1277 commented on Oct 21, 2026

    @samkay1277

    @samkay1277 has applied to work on this issue as part of the Stellar Wave Program's 10th wave.

    Hi team,
    I'm applying to work on this issue.
    I work across multiple stacks (TypeScript/Node, Rust, Python, and related tooling) and focus on clean, testable changes that match the acceptance criteria. I will keep the PR scoped to this issue only and ready for review.
    Ready to start immediately.

    ℹ️ Repo Maintainers: To accept this application, review their application or assign @samkay1277 to this issue.

  3. YoungBoss04 commented on Oct 22, 2026

    @YoungBoss04

    @YoungBoss04 has applied to work on this issue as part of the Stellar Wave Program's 10th wave.

    Having gone over the task and its requirements, I'm sure I can build a solution that matches the intended design and acceptance criteria.

    ℹ️ Repo Maintainers: To accept this application, review their application or assign @YoungBoss04 to this issue.

  4. Victor123-star commented on Oct 22, 2026

    @Victor123-star

    @Victor123-star has applied to work on this issue as part of the Stellar Wave Program's 10th wave.

    I'm interested in working on this could you assign this issue to me

    ℹ️ Repo Maintainers: To accept this application, review their application or assign @Victor123-star to this issue.

  5. esanbasirat commented on Oct 22, 2026

    @esanbasirat

    @esanbasirat has applied to work on this issue as part of the Stellar Wave Program's 10th wave.

    hi maintainerA

    ℹ️ Repo Maintainers: To accept this application, review their application or assign @esanbasirat to this issue.

  6. AGWAM001 commented on Oct 22, 2026

    @AGWAM001

    @AGWAM001 has applied to work on this issue as part of the Stellar Wave Program's 10th wave.

    Approach: Build /verify as the shared KYC destination, not an airtime-specific screen. First I’ll grep the app for every isNairaVerified() gate and pathname: '/verify' push to confirm which rails need it.

    Changes:

    New frontend/mobile/app/verify.tsx (or verify/index.tsx, matching the app’s routing convention). It reads returnTo, label, and summary from the params and shows the summary so the user knows what they’re verifying for.
    On success it returns to returnTo, so the user lands back in the flow they left. returnTo is validated against an allowlist of in-app routes, so a crafted param can’t redirect anywhere else.
    Three distinct outcomes: declined (clear message, retry allowed), already verified (skip straight back to returnTo), and provider unreachable (retryable error, clearly not a rejection).
    NIN handling: held in a ref or local state only for the request, cleared in a finally, never logged, persisted, or placed in route params, analytics, or error messages. Error paths log status codes only.

    Tests:

    A route-existence test asserting that every pathname pushed in the app resolves to a real screen, so this can’t regress.
    Params honoured, and success navigates to returnTo; an invalid returnTo falls back to a safe default.
    Declined, already-verified, and unreachable each render distinctly.
    No NIN in logs, storage, or navigation params. Fixtures use obviously fake placeholder values, never a real-looking NIN, phone, or meter number.

    Compatibility: purely additive. The airtime call site is unchanged.

    Estimate:

    I’ll wait for assignment before starting.

    ℹ️ Repo Maintainers: To accept this application, review their application or assign @AGWAM001 to this issue.

  7. drips-wave commented on Oct 22, 2026

    @drips-wave

    Congratulations, @AGWAM001! 🎉 Your application was accepted by the repo's maintainers, and the issue is due on October 22, 2026.

    🧑‍💻 @AGWAM001: Please resolve the issue such that the repo's maintainers have enough time to review your contribution before the due date. You'll earn Points for completing the issue on-time, which will make you eligible for a share of the Stellar Wave Program's reward pool.

    Warning

    When opening a PR, please link it to this issue to ensure it gets tracked accurately. Points are awarded when this issue is marked as completed by the maintainer.

    🤠 Repo maintainers: Please keep an eye on the contributor's progress and review their work before the due date. You can manage this issue, including adjusting its complexity and points, here.

    🌊 Happy Wave 🌊

  8. added 3 commits that reference this issue on Oct 22, 2026
  9. Miracle656 commented on Oct 22, 2026

    @Miracle656
    OwnerAuthor

    Escalating this. As of 900fc07b it is a dead end on two money screens, not one.

    frontend/mobile/app/electricity.tsx:417 now does the same thing airtime.tsx:256 does:

    router.push({ pathname: '/verify', params: { returnTo: …, label: …, summary: … } })

    and there is still no verify screen anywhere in frontend/mobile/app.

    The electricity screen copied the pattern from airtime, which is exactly what will keep happening — every bill rail needs a KYC step, so every new bill screen will reach for the same navigation. There are more coming: #1158 and #1159 are open for per-category identifiers and bill history, and the data bundle screen in #1144 is waiting on wraith#267.

    So this has changed character. It was a bug on one screen; it is now the shape every new bill screen inherits. Each one that lands before this is fixed is another place a user hits an unmatched route at the moment they are trying to spend money for the first time.

    Two consequences for whoever picks this up:

    • Build it as the shared verification destination, not an airtime-specific screen. It already takes returnTo, label and summary from two callers with identical shapes, which tells you the contract.
    • The fix is now worth more than the 150 points on it. If you are choosing between issues today, choose this one.

    I have not raised the points mid-Wave because that would be unfair to work already claimed at the stated value, but I am saying plainly that this is the highest-value open issue in the mobile app right now.

  10. drips-wave commented on Oct 22, 2026

    @drips-wave

    This issue has been completed by @AGWAM001 as part of the Stellar Wave Program's 10th Wave 🥳

    😎 @AGWAM001: You earned 150 Points for completing this issue! Once the Wave's rewards are finalized, you'll be eligible for a percentage of the Wave's reward pool based on the percentage of total points you've earned. Learn more here. You can also Leave a review to share your experience working on this issue.

    🧑‍💻 Repo maintainers: How'd the contributor do? Leave a review to share your experience working with them.

  11. Miracle656 commented on Oct 22, 2026

    @Miracle656
    OwnerAuthor

    Reopening — this was closed by my own mistake, and the bug is not fixed.

    frontend/mobile/app/ still has no verify screen. airtime.tsx:256 and electricity.tsx:417 both still navigate to /verify, and the mobile typecheck still reports it.

    What happened. In the merge commit for #1245 I was explaining that #1242 was mislabelled — it claimed to fix this issue while containing only swap-fee changes. To show that, I quoted its body verbatim, including the words Closes #1200. GitHub parsed the quote as a directive and closed this issue.

    So the thing I was writing a paragraph to prevent is the thing I then did, by quoting it.

    Noted for anyone writing commit messages here: a closing keyword is parsed anywhere in the message, including inside quotation marks and code fences. Refer to it as "the body claimed it closed this issue" rather than reproducing the phrase.

    Nothing has changed about the issue itself. It is still unassigned work, still the highest-value open item in the mobile app, and still gets worse with each new bill screen — every one of them needs the KYC step and copies the same navigation. Build it as the shared destination; there are already two callers passing an identical { returnTo, label, summary } shape.

  12. added a commit that references this issue on Oct 22, 2026
    83241f6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions