Repository navigation
feat(ramps-controller): create an autoramp only for a chosen fiat - #10743
Conversation
hydrateVbaOnboarding still registers the wallet and refreshes autoramps after KYC approval, then stops unless a source currency is passed. A passed code creates one mUSD autoramp for that fiat, with an idempotency key that varies by code. Co-authored-by: George Weiler <georgejweiler@gmail.com>
Co-authored-by: George Weiler <georgejweiler@gmail.com>
wenfix
left a comment
There was a problem hiding this comment.
Non-blocking review — no blocking issues found. Logic checks out: the needs_source_currency early return sits after wallet registration and the autoramp refresh, whitespace-only codes are treated as omitted, the idempotency key change only alters the prefix, and nothing iterates VBA_AUTORAMP_STATUSES positionally. Verified: all 1052 tests in @metamask/ramps-controller pass and yarn changelog:validate passes. Three minor observations below.
| buildBrazilMusdAutorampRequest(trimmedWalletAddress), | ||
| buildMusdAutorampRequest( | ||
| trimmedWalletAddress, | ||
| trimmedSourceCurrencyCode, |
There was a problem hiding this comment.
Non-blocking: the code is normalized (trimmed + lowercased) for the idempotency key but goes into source_currencies[0].code verbatim. So sourceCurrencyCode: 'usd' and 'USD' produce the same idempotency key with different bodies — the first create wins, and a later correctly-cased retry would be deduplicated against it. This only matters if a caller passes non-canonical casing (every current call site and doc example uses uppercase), and I can't tell from this repo whether MoonPay is case-sensitive on source_currencies[].code. Normalizing to uppercase (or validating against a supported list) at this boundary would remove the ambiguity.
| * Overlapping calls share one run so polling cannot trigger duplicate wallet | ||
| * signatures or autoramp creation. | ||
| * signatures or autoramp creation. A second call does not apply its own | ||
| * `sourceCurrencyCode`; it receives the in-flight result. |
There was a problem hiding this comment.
Non-blocking (and already documented here) — flagging so the consuming host behavior gets confirmed: when an in-flight run started without a code, a call that passes sourceCurrencyCode joins that run, receives needs_source_currency, and no create is scheduled. If a host polls this on a timer without a code, a user action that passes a code can silently no-op and must be retried. The host has to treat needs_source_currency after passing a code as a retry signal, since it cannot distinguish this case from the legitimate nothing-passed case.
| ); | ||
| const idempotencyKey = | ||
| createAutoramp.mock.calls[0]?.[1]?.idempotencyKey; | ||
| expect(idempotencyKey).not.toBe('brl-musd-monad:customer-1:0xabc'); |
There was a problem hiding this comment.
Non-blocking: this not.toBe assertion is strictly weaker than the toHaveBeenCalledWith above it, which already asserts the full usd-musd-monad:customer-1:0xabc key — so this line adds no coverage. If you want the test tighter, both it and the idempotencyKey extraction can be dropped.
Release 1318 added a profile-sync bump in the same Unreleased section. The hydrate read replaces the sourceCurrencyCode note from #10743. Co-authored-by: George Weiler <georgejweiler@gmail.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Explanation `hydrateVbaOnboarding` is a read. Signing a Money Account wallet and creating an autoramp stay public methods, and callers invoke them only from VBA onboarding. MetaMask#10743 is merged. That change created an autoramp inside hydrate when `sourceCurrencyCode` was passed. This PR removes that create, and the wallet signature, from hydrate. `buildMusdAutorampRequest` from that pull request stays. `createAutoramp` still posts the fiat body the caller passes. Hydrate may: - Read the persisted KYC session (`KycController.readSessionStatus`). - When `refreshKyc` is set and a non-terminal session id is already stored, perform one `GET /sessions/{id}/status` (`fetchSessionStatusOnce`) and write it onto KYC state. It does not start the 15-second poll. `refreshSessionStatus` is unchanged and may still start that poll; hydrate does not call it. - When `refreshAutoramps` is set, KYC is approved, and this wallet has no usable autoramp, `GET /neobank/autoramps` and replace the cursor. `vbaAutorampListFetchedAt` is set only when that GET succeeds. - Return `needs_wallet_registration` when KYC is approved and there is no persisted registration row, or `needs_source_currency` when the registration is `active` or `disabled` and there is no usable autoramp. A usable autoramp (not `Rejected` or `Cancelled`) skips the autoramp GET even if refresh was asked. Hydrate must not `personal_sign`, `POST /neobank/addresses/crypto/selfhosted`, `POST /neobank/autoramps`, call `registerMoneyAccountWallet`, call `createAutoramp`, or start session-status polling. `registerMoneyAccountWallet` still signs and posts the address. It writes `moneyAccountWalletRegistrations` (`walletAddress`, `status: 'active' | 'disabled'`, `updatedAt`) only after `alreadyRegistered`, `registered`, or `registeredDisabled`. A lookup failure or a thrown sign writes nothing. The signature and ownership message are not stored. `createAutoramp` still posts and updates the cursor. It uses the fiat body the caller passes. The idempotency key is `brl-musd-monad` or `usd-musd-monad` plus customer, wallet, and terminal autoramp ids. Hydrate does not default a BRL create. Mobile callers (`useVbaHomeCard`, onboarding hooks, selectors, the navigator, and `kyc-controller-init.ts`) are intentionally not in this PR. ## References - Follows the merged MetaMask#10743 - No mobile follow-up is included here ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_AGENT_PR_BODY_END --> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
## Explanation Scoped release so clients can pick up MetaMask#10749. Monorepo release **1320.0.0**, following merged [Release/1319.0.0](MetaMask#10756). - **`@metamask/ramps-controller` 26.3.0 → 27.0.0** (major). `hydrateVbaOnboarding` is a read: it no longer signs a wallet, posts a self-hosted address, creates an autoramp, or starts the KYC poll. Callers create an autoramp with `createAutoramp(buildMusdAutorampRequest(...))`. Messenger now requires `KycController:readSessionStatus` and `KycController:fetchSessionStatusOnce`. Also includes the unpublished MetaMask#10743 additions (`needs_source_currency`, `buildMusdAutorampRequest`) and the profile-sync range bump to `^34.0.2`. - **`@metamask/kyc-controller` 0.6.1 → 0.7.0** (minor, matching earlier 0.x breaking bumps). Publishes `readSessionStatus` and `fetchSessionStatusOnce` from MetaMask#10749. The same version also ships the unpublished breaking `aal2Token` requirement on `startSession` / `createUkycSession` (MetaMask#10632) and the 72-hour capability token (MetaMask#10679). - **`@metamask/transaction-pay-controller` 30.0.1 → 30.0.2** (patch). Published because ramps-controller is a major and this is its only dependent. The range moves to `^27.0.0`. This version also includes the unpublished gas-fee fix from MetaMask#10404. Other packages with unreleased changes are deferred. `ramps-controller` keeps its existing dependency ranges, including `@metamask/profile-sync-controller` at `^34.0.2` from MetaMask#10756. ## References - Ships MetaMask#10749 - Also publishes unpublished changes already on main: MetaMask#10743, MetaMask#10632, MetaMask#10679, MetaMask#10404, MetaMask#10752, MetaMask#10756 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > The release ships major ramps-controller and breaking KYC/VBA onboarding API changes; this PR only versions and documents them, but client upgrades must adopt new messenger delegations and onboarding call patterns. > > **Overview** > Cuts **monorepo release 1320.0.0** by moving unreleased changelog notes into versioned sections and bumping published package versions—no new runtime code in this diff. > > **`@metamask/ramps-controller` 27.0.0** (major) documents shipping MetaMask#10749/MetaMask#10743: `hydrateVbaOnboarding` becomes a read-only snapshot (wallet sign, registration POST, autoramp creation, and KYC polling move to callers), new persisted wallet-registration state and `needs_wallet_registration` / `needs_source_currency` statuses, and messenger now requires `KycController:readSessionStatus` and `KycController:fetchSessionStatusOnce`. > > **`@metamask/kyc-controller` 0.7.0** publishes those KYC actions plus breaking **`aal2Token`** requirements on UKYC session creation/`startSession`. > > **`@metamask/transaction-pay-controller` 30.0.2** updates its dependency range to **`@metamask/ramps-controller` ^27.0.0** and records the ramps bump in its changelog (along with fixes already on main such as MetaMask#10404). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 6cb8531. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…ask#37412) ## **Description** <!-- mms-check: type=text required=true --> <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> Adds the `useVbaHomeCard` hook (plus the `KycController` Redux selectors it reads) for the Money home VBA identity-status card. Money home renders **at most one** VBA identity-status card for a user who has started identity verification. This PR is pure card-selection logic plus a focus-time, one-shot KYC session refresh — no UI is wired yet (`MoneyHomeView` integration is a follow-up): - `useVbaHomeCard` maps VBA eligibility, the selected Money account, and KYC session/provider-flow status onto a single `VbaHomeCard` variant: `none`, `get_started`, `account_is_ready`, `kyc_pending`, `finish_verification`, or `kyc_decision` (rejected/retry stub — no Figma card exists for that state yet; hosts render it as `none` until design answers, pending in TRAM-4099). - The decision order follows George Weiler's TRAM-4099 comment 466556. - `useVbaHomeCardRefresh` implements the focus-time one-shot KYC refresh for the hosting screen: when a KYC session exists and its `finalStatus` is non-terminal, it calls `RampsController.hydrateVbaOnboarding({ walletAddress, refreshKyc: true })` — a single GET, never `refreshSessionStatus` and never the 15s poll. State writes never re-arm the refresh mid-focus. - New selectors `selectKycProviderFlowStatus` / `selectKycSessionStatus` expose `KycController` state to Redux. The hook names no destination and renders nothing; hosts map each variant onto Figma. Also bumps `@metamask/ramps-controller` to `^27.0.0` and `@metamask/kyc-controller` to `^0.7.0`. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Added a Money home hook that chooses the VBA identity-status card (TRAM-4099) ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [TRAM-4099](https://consensyssoftware.atlassian.net/browse/TRAM-4099) Refs: MetaMask/core#10743 (ramps-controller: create an autoramp only for a chosen fiat) Refs: MetaMask/core#10749 (make VBA hydrate a read) Refs: MetaMask#37374 (choose-currency screen) — composes with this hook; that PR remains the follow-up dependency. ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> N/A — the hook is not yet wired into `MoneyHomeView`, so there is no user-visible flow to exercise manually yet. This PR is unit-level only; manual testing steps will land with the `MoneyHomeView` integration follow-up. Automated coverage running in CI: ```bash yarn jest app/components/UI/Money/hooks/useVbaHomeCard.test.ts yarn jest app/selectors/kycController.test.ts ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** N/A — no UI change; the hook is not rendered anywhere yet. ### **After** N/A — no UI change; the hook is not rendered anywhere yet. ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [TRAM-4099]: https://consensyssoftware.atlassian.net/browse/TRAM-4099?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches KYC/VBA onboarding state and triggers hydrate on screen focus; logic is isolated and well-tested but wrong card choice could mislead users once Money home is wired. > > **Overview** > Introduces **card-selection logic** for the single Virtual Bank Account (VBA) identity-status card on Money home, with **no UI wired yet** (hosts map variants later). > > Adds **`useVbaHomeCard`** and the pure **`getVbaHomeCard`** helper, which combine VBA eligibility, the selected Money wallet, KYC session/provider-flow state, ramps autoramp/registration evidence, and async-storage vendor terms into one variant: `none`, `get_started`, `account_is_ready`, `kyc_pending`, `finish_verification`, or a **`kyc_decision` stub** (rejected/retry — intended to render as `none` until design lands). Decision order follows TRAM-4099 and mirrors ramps-controller semantics. > > Adds **`useVbaHomeCardRefresh`** for the hosting screen: on focus, a **one-shot** `hydrateVbaOnboarding({ refreshKyc: true })` when a non-terminal KYC session exists (no polling on load). > > New **`app/selectors/kycController`** exposes `KycController` Redux state (`sessionStatus`, `providerFlowStatus`, vendor disclaimers). **CODEOWNERS** assigns that path to `@MetaMask/universal-kyc`. Unit tests cover the decision table, terms loading/wallet switches, and focus refresh behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3368297. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Amitabh Aggarwal <aggarwal.amitabh@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Explanation
hydrateVbaOnboardingregistered the Money Account wallet and then always created a BRL → mUSD autoramp. After the standing BRL route, hosts need to finish signing and refresh autoramps without creating, then create one autoramp only for a fiat the caller passes in.hydrateVbaOnboardingnow takes an optionalsourceCurrencyCode:autorampStatus: 'needs_source_currency'and do not POST.source_currencies[0].codeis that fiat. Destination stays mUSD on Monad. The idempotency key prefix follows the code (brl-musd-monad/usd-musd-monad), so a USD create does not replay the BRL route.readyand do not create, even if a code was passed.buildMusdAutorampRequestbuilds that body for an explicit code.buildBrazilMusdAutorampRequestremains the BRL wrapper.NeoBankService.createAutorampis unchanged; it already forwards the body.This is a behavior change for callers that relied on hydrate to create the BRL autoramp. Those callers must pass
sourceCurrencyCode: 'BRL'on the call that should create. Mobile adoption is not in this pull request.References
Checklist