Skip to content

feat(kyc-controller): register Money Account wallets - #9847

Closed
saustrie-consensys wants to merge 58 commits into
mainfrom
feat/moonpay-wallet-registration
Closed

feat(kyc-controller): register Money Account wallets#9847
saustrie-consensys wants to merge 58 commits into
mainfrom
feat/moonpay-wallet-registration

Conversation

@saustrie-consensys

@saustrie-consensys saustrie-consensys commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Explanation

This stacked PR adds MoonPay Iron self-hosted wallet registration to the unreleased @metamask/kyc-controller package from #9615. The POC is limited to Money Account on Monad.

Ramps and other consumers call one messenger action:

await messenger.call('KycController:registerMoneyAccountWallet', {
  address: moneyAccount.address,
});

The consumer does not pass an Iron customer ID, construct a registration service, build the ownership message, sign it, or manage retry state. KycController handles those details internally:

  1. Check whether the Monad address is already registered.
  2. Prefer the ephemeral this.state.moonpayCustomerId received from MoonPay's hosted frame.
  3. If that state is null, resolve Iron's internal customer ID through the authenticated GET /vendors/moonpay/customer proxy endpoint.
  4. Build the exact UTC-dated ownership message and sign it with KeyringController:signPersonalMessage.
  5. Submit through KycService and drive the internal state machine for conflict reconciliation, transient retries, and UTC rollover re-signing.

The low-level registration service, message builder, and state machine are implementation details. They are not exported as alternate consumer APIs. Typed registration errors remain exported so clients can classify failures if needed.

State machine
stateDiagram-v2
    [*] --> idle
    idle --> preparing: START

    preparing --> alreadyRegistered: LOOKUP_ACTIVE
    preparing --> registeredDisabled: LOOKUP_DISABLED
    preparing --> signing: LOOKUP_ABSENT
    preparing --> lookupUnavailable: LOOKUP_FAILED

    signing --> awaitingUnlock: WALLET_LOCKED
    awaitingUnlock --> signing: WALLET_UNLOCKED
    signing --> submitting: SIGN_OK
    signing --> cancelled: SIGN_REJECTED / CANCEL
    signing --> failedRetryable: SIGN_FAILED(retryable)
    signing --> failedTerminal: SIGN_FAILED(!retryable)

    submitting --> registered: SUBMIT_OK
    submitting --> disambiguate409: SUBMIT_CONFLICT
    submitting --> checkThenRetry: SUBMIT_TRANSIENT
    submitting --> signing: SUBMIT_VALIDATION(utcRollover & attempts < max)
    submitting --> failedTerminal: SUBMIT_VALIDATION(otherwise) / SUBMIT_TERMINAL
    submitting --> failedRetryable: SUBMIT_RATE_LIMITED

    disambiguate409 --> alreadyRegistered: LOOKUP_ACTIVE
    disambiguate409 --> registeredDisabled: LOOKUP_DISABLED
    disambiguate409 --> failedRetryable: LOOKUP_ABSENT
    disambiguate409 --> lookupUnavailable: LOOKUP_FAILED

    checkThenRetry --> alreadyRegistered: LOOKUP_ACTIVE
    checkThenRetry --> registeredDisabled: LOOKUP_DISABLED
    checkThenRetry --> signing: LOOKUP_ABSENT & attempts < max
    checkThenRetry --> failedRetryable: LOOKUP_ABSENT & attempts >= max
    checkThenRetry --> lookupUnavailable: LOOKUP_FAILED

    failedRetryable --> preparing: RETRY
    lookupUnavailable --> preparing: RETRY
    cancelled --> preparing: RETRY

    registered --> [*]
    alreadyRegistered --> [*]
    registeredDisabled --> [*]
    failedTerminal --> [*]
Loading

Contract confirmations from MoonPay/Iron

  • Money Account is an EIP-7702 upgraded EOA. Iron accepts its EIP-191 signature.
  • blockchain: "Monad" works in sandbox and is present in the pinned 2026-08-01 contract.
  • Address uniqueness is per customer per chain.
  • An existing SELF_ATTESTED address satisfies proof of ownership and is treated as registered.

Verification

  • Package tests pass with 100% statement, branch, function, and line coverage.
  • Package ESM and CommonJS builds pass.
  • ESLint, formatting, and generated messenger action checks pass.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Akaryatrh and others added 30 commits July 24, 2026 12:26
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>
Akaryatrh and others added 21 commits August 5, 2026 22:27
…ay session creation

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>
Registering a self-hosted wallet with MoonPay Iron requires signing a
proof-of-ownership sentence and POSTing it byte-identically. Three pieces
land here, each independently testable:

- buildOwnershipMessage: produces the exact sentence Iron expects, with
  the date always derived in UTC so a signature made just before midnight
  is not reused against a stale date after rollover.
- WalletRegistrationService: talks to the MetaMask proxy for status
  lookup and registration, and maps proxy/Iron failures onto typed
  WalletRegistrationError kinds so callers can branch without parsing
  strings.
- wallet-registration-machine: a pure transition reducer covering the
  signing step, including the ambiguous 409 (address already exists vs
  idempotency-key reuse) which is only resolvable by re-reading the
  address list, transient failures, and the UTC-rollover re-sign.

The machine follows the hand-rolled FSM convention already used in this
repo rather than pulling in a state-chart dependency.
Limit the public request contract to Monad and keep product eligibility
outside the Iron protocol machine. This removes speculative multi-chain,
conflict-classification, and prerequisite state surfaces while retaining
409 and transient-write reconciliation.
Expose an address-only controller action so Ramps can register a Monad wallet without handling Iron customer IDs, signing, or retry state.
@saustrie-consensys saustrie-consensys changed the title feat(kyc-controller): add MoonPay Iron self-hosted wallet registration feat(kyc-controller): register Money Account wallets Aug 12, 2026
@saustrie-consensys
saustrie-consensys changed the base branch from main to feat/kyc-controller August 12, 2026 18:31
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedchokidar-cli@​3.0.09910010083100

View full report

@socket-security

Copy link
Copy Markdown

Caution

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Block Medium
System shell access: npm chokidar-cli in module child_process

Module: child_process

Location: Package overview

From: packages/kyc-controller/package.jsonnpm/chokidar-cli@3.0.0

ℹ Read more on: This package | This alert | What is shell access?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should avoid accessing the shell which can reduce portability, and make it easier for malicious shell access to be introduced.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/chokidar-cli@3.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm chokidar-cli is 90.0% likely to have a medium risk anomaly

Notes: This module is a straightforward wrapper that spawns a shell to execute a provided command string. It is not itself malware, but it provides a high-impact sink: direct execution of arbitrary shell commands. If cmd is derived from untrusted input, this is a clear command-injection vulnerability. There is no obfuscation or evidence of deliberate malicious behavior in the file. Treat any use of run with untrusted input as dangerous and sanitize or avoid passing raw user input into the cmd parameter.

Confidence: 0.90

Severity: 0.60

From: packages/kyc-controller/package.jsonnpm/chokidar-cli@3.0.0

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/chokidar-cli@3.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm y18n is 68.0% likely to have a medium risk anomaly

Notes: The code is a standard local i18n helper that caches locale strings and writes updates to disk. It does not contain obvious malware or backdoors. However, there are security concerns related to unvalidated locale inputs, potential path traversal when locale is attacker-controlled, and race conditions in multi-process environments. If used in attack-prone contexts, these issues should be mitigated by validating locale values, constraining file paths within the intended directory, and optionally disabling auto-write-back (updateFiles=false) in high-risk deployments.

Confidence: 0.68

Severity: 0.58

From: ?npm/chokidar-cli@3.0.0npm/y18n@4.0.3

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/y18n@4.0.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm yargs is 61.0% likely to have a medium risk anomaly

Notes: The module implements a standard extends resolution with circular-extends protection and recursive merging. Primary security concerns are the potential execution of arbitrary code when extends points to a JS module and possible disclosure of local files via path-based extends. Treat untrusted inputs as a high-risk factor; consider enforcing whitelisting of allowed extends or sandboxing JS module extends. Overall risk is moderate due to code execution potential from untrusted configuration sources.

Confidence: 0.61

Severity: 0.58

From: ?npm/chokidar-cli@3.0.0npm/yargs@13.3.2

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yargs@13.3.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Low
Potential code anomaly (AI signal): npm yargs is 68.0% likely to have a medium risk anomaly

Notes: The provided code consists of harmless-looking shell completion templates used to generate Bash/Zsh completions by querying the application for possible commands via --get-yargs-completions. There is no malicious behavior evident in the template code itself. The main risk is dependency on the integrity of the app_path binary that provides completions; if that binary is compromised, it could influence completions or run unintended commands. Overall, low likelihood of malware in these templates themselves.

Confidence: 0.68

Severity: 0.60

From: ?npm/chokidar-cli@3.0.0npm/yargs@13.3.2

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yargs@13.3.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@saustrie-consensys
saustrie-consensys changed the base branch from feat/kyc-controller to main August 12, 2026 18:32
@saustrie-consensys

Copy link
Copy Markdown
Contributor Author

Closing in favor of a clean stacked replacement that targets feat/kyc-controller (#9615): the replacement PR.

This branch started from a newer main than #9615, so stacking it onto feat/kyc-controller pulled thousands of unrelated commits into the review diff.

@saustrie-consensys

Copy link
Copy Markdown
Contributor Author

Replacement draft: #9850

It targets feat/kyc-controller (#9615) and only contains the wallet-registration changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants