feat(kyc-controller): Iron Money KYC path + status polling - #9852
feat(kyc-controller): Iron Money KYC path + status polling#9852amitabh94 wants to merge 62 commits into
Conversation
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
…b flow opening Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
… been reset Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Signed-off-by: Sébastien Van Eyck <sebastien.vaneyck@consensys.net>
Introduce vendor:'iron' flow (no MoonPay Check/Auth frames) with KycService clients for Iron customers/disclaimers/consents and user-keyed GET /kyc/status, so Money can drive toast state against the planned Milestone 1 API contract.
Add PR link and formatting for changelog, fix JSDoc/restricted-syntax/jest matcher lint errors, and apply Prettier to the package. Co-authored-by: Cursor <cursoragent@cursor.com>
Use oxfmt (not Prettier) so import order and markdown alignment match the monorepo misc formatter. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Demo topology update (for reviewers): Iron feature commit This PR (#9852) should stay targeting |
## Summary - `neobank-demo` already includes Sébastien’s `feat/kyc-controller` stack and the Iron feature commit from #9852 (`a40fa2fc3f`, merged by George). - This PR cherry-picks the remaining #9852 CI fixes (`lint` / changelog PR link / `oxfmt`) so the demo branch matches the Iron tip for package publish/preview and mobile consumption. - Keeps #9852 targeting `feat/kyc-controller` for the eventual mainline path via #9615. Does **not** merge to `main`. ## Context (demo topology) - Integration branch: `neobank-demo` (owner: @georgeweiler) — umbrella PR #9853 → `main` - Iron feature PR (mainline stack): #9852 → `feat/kyc-controller` - KYC controller PR: #9615 → `main` ## Test plan - [ ] Confirm diff is formatting/lint/changelog only (no Iron behavior change) - [ ] CI green on this PR - [ ] After merge, `@metamaskbot publish-preview` on #9853 (or this PR) if mobile needs a preview pin of `@metamask/kyc-controller` Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
| // Ignore; disclaimers loading will surface a country error if needed. | ||
| } | ||
|
|
||
| // Iron: create the empty-shell customer before T&C (offsite decision). |
There was a problem hiding this comment.
what is the reasoning for the KycController to explicitly handle this bit of the vendor onboarding lifecycle?
There was a problem hiding this comment.
We need to be able to create a customer before signing the T&C.
Where do you think this logic is best placed?
There was a problem hiding this comment.
Sorry, I meant more that it is odd for the KycController to have to concern itself with specifics of Iron onboarding, in this case creation of a customer with the user's email. Previously this was combined with the Moonpay flow under the notion of a "session". I agree that this step still must happen before Iron signings can be submitted, but i think we should abstract this into the same idea of a "session", hiding the fact that that really just means a customer must be created/exist to the KycController itself
| sumsubTncSigned: params?.sumsubTncSigned ?? true, | ||
| idosTncSigned: params?.idosTncSigned ?? true, |
There was a problem hiding this comment.
defaulting to true seems to make it more possible that the disclaimers are not actually shown to the user
There was a problem hiding this comment.
Yes that sounds correct. Should we default it to false then ?
| vendorId: isIron ? 'iron' : 'moonpay', | ||
| ...(isIron | ||
| ? {} | ||
| : { | ||
| vendorMetadata: { | ||
| moonPayAccessToken: this.state.accessToken, | ||
| moonPayUserId: this.state.moonpayCustomerId, | ||
| }, | ||
| }), |
There was a problem hiding this comment.
feels written a little oddly. Perhaps we just pass state.activeVendor straight through to vendorId and then add a helper to generate the extra vendorMetadata bit
There was a problem hiding this comment.
That is a definitely a clean and better approach.
| return result; | ||
| } catch (error) { | ||
| // Applicant already finished KYC — treat as completed for Money toast. | ||
| if (String(error).includes(SESSION_NOT_IN_VALID_STATE)) { |
There was a problem hiding this comment.
would be nice to abstract this into a helper
| this.#stopPolling(); | ||
| this.#stopUserStatusPolling(); |
There was a problem hiding this comment.
TODO(jiexi): verify the difference between these two poll loops
There was a problem hiding this comment.
not certain we need both polling loops
| /** | ||
| * Creates (or resumes) an Iron empty-shell customer for the authenticated | ||
| * canonical user. Must run before showing Iron T&C so the customer exists in | ||
| * `SigningsRequired` and resume logic can key off Iron status. |
There was a problem hiding this comment.
doesn't need to exist before showing T&C, only before submitting signing contentIds to Iron
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 47840d7. Configure here.
|
An architectural suggestion: right now Iron Money onboarding is exposed as public KYC APIs (createIronCustomer, Iron-named KycService methods, vendor: 'iron' on the consumer surface). It is a bit awkward IMO. I am imagining something like this instead, where ramps-controller can pass a "vendor" (like iron) to some generic methods: |

Summary
vendor: 'iron'KYC flow for MetaMask Money:idle → terms → session → submit(SumSub) → done/errorwith no MoonPay Check/Auth frames.KycServicewith Milestone 1 contract clients (mocked in tests):POST /vendors/iron/customers,GET /vendors/iron/disclaimers,POST /vendors/iron/kyc-required,POST /consents,GET /kyc/status, pluscreateUkycSession({ vendorId: 'iron' })without MoonPay metadata.createIronCustomer,refreshKycStatus,KycController:statusChanged, persisteduserStatus*fields, and mapssession_not_in_valid_state→completed.Stacking
feat/kyc-controller(feat: KYC controller #9615)va-mmcx-kyc-api(not merged yet). Clients are implemented against the planned contract; tests mock fetch.Deferred (follow-ups)
need-more-information/terminal-failure/ retryable SumSub session id)Test plan
yarn workspace @metamask/kyc-controller run test(100% coverage)yarn workspace @metamask/kyc-controller run messenger-action-types:check/consents+/kyc/statusexistMade with Cursor