Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/kyc-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add `KycController.getCustomerIdentity()` method and the `KycController:getCustomerIdentity` messenger action (plus the exported `KycControllerGetCustomerIdentityAction` and `KycCustomerIdentity` types). Returns the vendor-scoped `{ vendor, id }` for the currently authenticated customer, or `null` before authentication and after `reset()`. Lets consumers (e.g. ramps autoramp creation) attach the vendor customer id to downstream calls without reading the full KYC state, which also holds session/access tokens. The id is session-scoped and never persisted.
- Add Iron (Money/VBA) KYC path to `@metamask/kyc-controller`: `vendor: 'iron'` skips MoonPay Check/Auth frames; `KycService` clients for `/vendors/iron/*`, `POST /consents`, and `GET /kyc/status`; `refreshKycStatus` + `statusChanged` for Money toast state ([#9852](https://github.com/MetaMask/core/pull/9852))
- Initial release of the `@metamask/kyc-controller` package for managing KYC / identity verification state across MetaMask clients ([#9781](https://github.com/MetaMask/core/pull/9781))
- Add `KycController` and `KycService` for managing KYC / identity verification state across MetaMask clients ([#9615](https://github.com/MetaMask/core/pull/9615))
- Add `KycController.getCustomerIdentity()` method and the `KycController:getCustomerIdentity` messenger action (plus the exported `KycControllerGetCustomerIdentityAction` and `KycCustomerIdentity` types). Returns the vendor-scoped `{ vendor, id }` for the currently authenticated customer, or `null` before authentication and after `reset()`. Lets consumers (e.g. ramps autoramp creation) attach the vendor customer id to downstream calls without reading the full KYC state, which also holds session/access tokens. The id is session-scoped and never persisted. ([#9853](https://github.com/MetaMask/core/pull/9853))
- Add Iron (Money/VBA) KYC path to `@metamask/kyc-controller`: `vendor: 'iron'` skips MoonPay Check/Auth frames; `KycService` clients for `/vendors/iron/*`, `POST /consents`, and `GET /kyc/status`; `refreshKycStatus` + `statusChanged` for Money toast state ([#9852](https://github.com/MetaMask/core/pull/9852), [#9853](https://github.com/MetaMask/core/pull/9853))
- Initial release of the `@metamask/kyc-controller` package for managing KYC / identity verification state across MetaMask clients ([#9781](https://github.com/MetaMask/core/pull/9781), [#9853](https://github.com/MetaMask/core/pull/9853))
- Add `KycController` and `KycService` for managing KYC / identity verification state across MetaMask clients ([#9615](https://github.com/MetaMask/core/pull/9615), [#9853](https://github.com/MetaMask/core/pull/9853))
- `KycController` (`BaseController`) owns the flow state machine, the Check/Auth frame message protocol, X25519 credential decryption, and SumSub orchestration via an injected `KycSumSubLauncher` adapter.
- `KycService` extends `BaseDataService` and performs the Universal KYC (UKYC) HTTP calls via an injected `fetch`, sourcing the auth bearer token and geolocation through the messenger.
- Exposes a vendor-neutral, per-product surface (`ramps`, `card`) plus reselect selectors.
Expand All @@ -23,6 +23,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

- Move Money Account wallet registration to `@metamask/ramps-controller`: removes `KycController.registerMoneyAccountWallet`, the `KycService` wallet-registration methods (`getMoonpayCustomerId`, `getWalletRegistrationStatus`, `registerSelfHostedWallet`), the `neobankBaseUrl` service option, and the wallet registration exports (`WalletRegistrationError`, `SelfHostedRegistration`, `MoneyAccountWalletRegistrationResult`, and related types). Wallet ownership signing is a Money Movement (neobank-proxy) concern, so it now lives on `RampsController` / `NeoBankService`.
- Move Money Account wallet registration to `@metamask/ramps-controller`: removes `KycController.registerMoneyAccountWallet`, the `KycService` wallet-registration methods (`getMoonpayCustomerId`, `getWalletRegistrationStatus`, `registerSelfHostedWallet`), the `neobankBaseUrl` service option, and the wallet registration exports (`WalletRegistrationError`, `SelfHostedRegistration`, `MoneyAccountWalletRegistrationResult`, and related types). Wallet ownership signing is a Money Movement (neobank-proxy) concern, so it now lives on `RampsController` / `NeoBankService`. ([#9853](https://github.com/MetaMask/core/pull/9853))

[Unreleased]: https://github.com/MetaMask/core/
4 changes: 2 additions & 2 deletions packages/profile-sync-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Resolve HD entropy source IDs from `KeyringController` instead of the message-signing snap (`getBearerToken` primary ID, `performSignIn` SRP enumeration) ([#9794](https://github.com/MetaMask/core/pull/9794))
- Bump `@metamask/keyring-controller` from `^27.1.0` to `^27.1.1` ([#9791](https://github.com/MetaMask/core/pull/9791))
- Resolve HD entropy source IDs from `KeyringController` instead of the message-signing snap (`getBearerToken` primary ID, `performSignIn` SRP enumeration) ([#9794](https://github.com/MetaMask/core/pull/9794), [#9853](https://github.com/MetaMask/core/pull/9853))
- Bump `@metamask/keyring-controller` from `^27.1.0` to `^27.1.1` ([#9791](https://github.com/MetaMask/core/pull/9791), [#9853](https://github.com/MetaMask/core/pull/9853))

## [29.0.0]

Expand Down
18 changes: 9 additions & 9 deletions packages/ramps-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add `RampsController.createAutoramp(request, options?)` method and the `RampsController:createAutoramp` messenger action (plus the exported `RampsControllerCreateAutorampAction` and `CreateAutorampRequest` types). It resolves the MoonPay `customer_id` from Profile Sync (`AuthenticationController:getSessionProfile`) via `NeoBankService:getCustomerByExternalId`, injects it into the request (overwriting any caller-supplied `customer_id`), forwards the body to `NeoBankService:createAutoramp`, and applies the returned snapshot to local state. Throws when the wallet is not signed in or no MoonPay customer is mapped to the external id.
- Add the exported `RAMPS_CONTROLLER_REQUIRED_CONTROLLER_ACTIONS` constant listing the other-controller actions (`AuthenticationController:getSessionProfile`, `KeyringController:signPersonalMessage`) that hosts must delegate to the `RampsController` messenger to enable autoramp creation and Money Account wallet registration.
- Add NeoBankService Pix / autoramp quote client methods and messenger actions, targeting the neobank-proxy `/neobank` prefix on the Ramp API host: `registerPixAddress`, `getAutorampQuote`, `createAutoramp`, `getAutorampQuoteForAutoramp`, `attachAutorampQuote`, and `getCustomerByExternalId`. Pix/quote helpers return parsed proxy JSON; `createAutoramp` maps autoramp-shaped responses via `mapNeoBankAutorampToRemoteSnapshot` (same as `getAutoramp`). Optional `Idempotency-Key` is supported on mutating calls.
- Export `TERMINAL_ORDER_STATUSES` and `isTerminalOrderStatus()` so consuming clients can share the controller's terminal order status set instead of maintaining duplicate copies. ([#9679](https://github.com/MetaMask/core/pull/9679))
- Add `RampsController.registerMoneyAccountWallet({ address })` method and the `RampsController:registerMoneyAccountWallet` messenger action (moved from `@metamask/kyc-controller`). Resolves the MoonPay Iron customer id via Profile Sync → neobank-proxy external-id lookup, signs the Monad ownership message via `KeyringController:signPersonalMessage`, and registers the self-hosted wallet through the neobank-proxy — including `409` disambiguation, transient-failure reconciliation, and UTC date rollover re-signing ([#9850](https://github.com/MetaMask/core/pull/9850), [#9847](https://github.com/MetaMask/core/pull/9847))
- Add `NeoBankService.getMoonpayCustomerId`, `NeoBankService.getWalletRegistrationStatus`, and `NeoBankService.registerSelfHostedWallet` methods and messenger actions, targeting the transparent neobank routes (`GET /neobank/customers/{external_id}/external`, `GET /neobank/addresses/crypto/{customer_id}`, `POST /neobank/addresses/crypto/selfhosted`) with client-side Monad filtering, `Idempotency-Key` support, and upstream error bodies mirrored 1:1.
- Export the wallet registration types (`SelfHostedRegistration`, `RegistrationStatus`, `RegistrationOutcome`, `WalletRegistrationError`, `WalletRegistrationErrorKind`, `MoneyAccountWalletRegistrationResult`) and `buildOwnershipMessage` (moved from `@metamask/kyc-controller`).
- Add `RampsController.createAutoramp(request, options?)` method and the `RampsController:createAutoramp` messenger action (plus the exported `RampsControllerCreateAutorampAction` and `CreateAutorampRequest` types). It resolves the MoonPay `customer_id` from Profile Sync (`AuthenticationController:getSessionProfile`) via `NeoBankService:getCustomerByExternalId`, injects it into the request (overwriting any caller-supplied `customer_id`), forwards the body to `NeoBankService:createAutoramp`, and applies the returned snapshot to local state. Throws when the wallet is not signed in or no MoonPay customer is mapped to the external id. ([#9853](https://github.com/MetaMask/core/pull/9853))
- Add the exported `RAMPS_CONTROLLER_REQUIRED_CONTROLLER_ACTIONS` constant listing the other-controller actions (`AuthenticationController:getSessionProfile`, `KeyringController:signPersonalMessage`) that hosts must delegate to the `RampsController` messenger to enable autoramp creation and Money Account wallet registration. ([#9853](https://github.com/MetaMask/core/pull/9853))
- Add NeoBankService Pix / autoramp quote client methods and messenger actions, targeting the neobank-proxy `/neobank` prefix on the Ramp API host: `registerPixAddress`, `getAutorampQuote`, `createAutoramp`, `getAutorampQuoteForAutoramp`, `attachAutorampQuote`, and `getCustomerByExternalId`. Pix/quote helpers return parsed proxy JSON; `createAutoramp` maps autoramp-shaped responses via `mapNeoBankAutorampToRemoteSnapshot` (same as `getAutoramp`). Optional `Idempotency-Key` is supported on mutating calls. ([#9853](https://github.com/MetaMask/core/pull/9853))
- Export `TERMINAL_ORDER_STATUSES` and `isTerminalOrderStatus()` so consuming clients can share the controller's terminal order status set instead of maintaining duplicate copies. ([#9679](https://github.com/MetaMask/core/pull/9679), [#9853](https://github.com/MetaMask/core/pull/9853))
- Add `RampsController.registerMoneyAccountWallet({ address })` method and the `RampsController:registerMoneyAccountWallet` messenger action (moved from `@metamask/kyc-controller`). Resolves the MoonPay Iron customer id via Profile Sync → neobank-proxy external-id lookup, signs the Monad ownership message via `KeyringController:signPersonalMessage`, and registers the self-hosted wallet through the neobank-proxy — including `409` disambiguation, transient-failure reconciliation, and UTC date rollover re-signing ([#9850](https://github.com/MetaMask/core/pull/9850), [#9847](https://github.com/MetaMask/core/pull/9847), [#9853](https://github.com/MetaMask/core/pull/9853))
- Add `NeoBankService.getMoonpayCustomerId`, `NeoBankService.getWalletRegistrationStatus`, and `NeoBankService.registerSelfHostedWallet` methods and messenger actions, targeting the transparent neobank routes (`GET /neobank/customers/{external_id}/external`, `GET /neobank/addresses/crypto/{customer_id}`, `POST /neobank/addresses/crypto/selfhosted`) with client-side Monad filtering, `Idempotency-Key` support, and upstream error bodies mirrored 1:1. ([#9853](https://github.com/MetaMask/core/pull/9853))
- Export the wallet registration types (`SelfHostedRegistration`, `RegistrationStatus`, `RegistrationOutcome`, `WalletRegistrationError`, `WalletRegistrationErrorKind`, `MoneyAccountWalletRegistrationResult`) and `buildOwnershipMessage` (moved from `@metamask/kyc-controller`). ([#9853](https://github.com/MetaMask/core/pull/9853))

### Changed

- Resolve autoramp / Money Account wallet-registration customer id only via Profile Sync + `NeoBankService:getCustomerByExternalId` (prefer `canonicalProfileId`, else `profileId`). Stop calling `KycController:getCustomerIdentity` from ramps; remove the local `KycControllerGetCustomerIdentityAction` type and drop that action from `RAMPS_CONTROLLER_REQUIRED_CONTROLLER_ACTIONS`.
- Point `NeoBankService.getAutoramp` at `GET /neobank/autoramps/{id}` (neobank-proxy global `/neobank` prefix) instead of `/api/v2/autoramps/{id}`, so Core matches the proxy that ships.
- Resolve autoramp / Money Account wallet-registration customer id only via Profile Sync + `NeoBankService:getCustomerByExternalId` (prefer `canonicalProfileId`, else `profileId`). Stop calling `KycController:getCustomerIdentity` from ramps; remove the local `KycControllerGetCustomerIdentityAction` type and drop that action from `RAMPS_CONTROLLER_REQUIRED_CONTROLLER_ACTIONS`. ([#9859](https://github.com/MetaMask/core/pull/9859), [#9853](https://github.com/MetaMask/core/pull/9853))
- Point `NeoBankService.getAutoramp` at `GET /neobank/autoramps/{id}` (neobank-proxy global `/neobank` prefix) instead of `/api/v2/autoramps/{id}`, so Core matches the proxy that ships. ([#9853](https://github.com/MetaMask/core/pull/9853))

## [20.0.0]

Expand Down
26 changes: 14 additions & 12 deletions packages/ramps-controller/src/NeoBankService.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import nock from 'nock';
import { Messenger, MOCK_ANY_NAMESPACE } from '@metamask/messenger';
import type { MockAnyNamespace } from '@metamask/messenger';
import nock, { cleanAll } from 'nock';

import {
mapNeoBankAutorampToRemoteSnapshot,
NeoBankService,
} from './NeoBankService.js';
import type { NeoBankServiceMessenger } from './NeoBankService.js';
import { RampsEnvironment } from './RampsService.js';
import { Messenger, MOCK_ANY_NAMESPACE } from '@metamask/messenger';
import type { MockAnyNamespace } from '@metamask/messenger';

const STAGING_BASE = 'https://on-ramp.uat-api.cx.metamask.io';

Expand Down Expand Up @@ -82,7 +82,7 @@ function createService(options?: {

describe('NeoBankService', () => {
afterEach(() => {
nock.cleanAll();
cleanAll();
});

describe('mapNeoBankAutorampToRemoteSnapshot', () => {
Expand Down Expand Up @@ -132,7 +132,7 @@ describe('NeoBankService', () => {
});

describe('getAutoramp', () => {
it('GETs /neobank/autoramps/{id} with bearer auth', async () => {
it('gets /neobank/autoramps/{id} with bearer auth', async () => {
const scope = nock(STAGING_BASE)
.get(/\/neobank\/autoramps\/ar-1/u)
.matchHeader('Authorization', 'Bearer test-token')
Expand All @@ -156,7 +156,9 @@ describe('NeoBankService', () => {
});

it('throws HttpError when the proxy returns a non-2xx status', async () => {
nock(STAGING_BASE).get(/\/neobank\/autoramps\/missing/u).reply(404);
nock(STAGING_BASE)
.get(/\/neobank\/autoramps\/missing/u)
.reply(404);

const service = createService();
await expect(service.getAutoramp('missing')).rejects.toThrow(
Expand All @@ -177,7 +179,7 @@ describe('NeoBankService', () => {
});

describe('registerPixAddress', () => {
it('POSTs /neobank/addresses/pix with JSON body and bearer auth', async () => {
it('posts /neobank/addresses/pix with JSON body and bearer auth', async () => {
const body = {
type: 'Pix',
pix_key: 'user@example.com',
Expand Down Expand Up @@ -216,7 +218,7 @@ describe('NeoBankService', () => {
});

describe('getAutorampQuote', () => {
it('GETs /neobank/autoramps/quote with query params', async () => {
it('gets /neobank/autoramps/quote with query params', async () => {
const scope = nock(STAGING_BASE)
.get('/neobank/autoramps/quote')
.query((query) => {
Expand All @@ -243,7 +245,7 @@ describe('NeoBankService', () => {
});

describe('createAutoramp', () => {
it('POSTs /neobank/autoramps and maps the Autoramp response', async () => {
it('posts /neobank/autoramps and maps the Autoramp response', async () => {
const body = {
signed_quote: 'sig',
customer_id: 'cust-1',
Expand Down Expand Up @@ -309,7 +311,7 @@ describe('NeoBankService', () => {
});

describe('getAutorampQuoteForAutoramp', () => {
it('GETs /neobank/autoramps/{id}/quote with query params', async () => {
it('gets /neobank/autoramps/{id}/quote with query params', async () => {
const scope = nock(STAGING_BASE)
.get('/neobank/autoramps/ar-1/quote')
.query((query) => {
Expand All @@ -329,7 +331,7 @@ describe('NeoBankService', () => {
});

describe('attachAutorampQuote', () => {
it('POSTs /neobank/autoramps/{id}/quotes with JSON body', async () => {
it('posts /neobank/autoramps/{id}/quotes with JSON body', async () => {
const body = { signed_quote: 'attach-sig' };

const scope = nock(STAGING_BASE)
Expand All @@ -348,7 +350,7 @@ describe('NeoBankService', () => {
});

describe('getCustomerByExternalId', () => {
it('GETs /neobank/customers/{external_id}/external', async () => {
it('gets /neobank/customers/{external_id}/external', async () => {
const scope = nock(STAGING_BASE)
.get('/neobank/customers/ext-1/external')
.query(true)
Expand Down
23 changes: 15 additions & 8 deletions packages/ramps-controller/src/NeoBankService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,20 @@ export const serviceName = 'NeoBankService';
*/
export type NeoBankAutorampResponse = {
id: string;
// eslint-disable-next-line @typescript-eslint/naming-convention -- MoonPay API field
customer_id: string;
status: string;
/**
* Destination wallet when present on the proxy response.
* Field name may evolve with the Ramp API contract.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention -- MoonPay API field
wallet_address?: string;
// eslint-disable-next-line @typescript-eslint/naming-convention -- MoonPay API field
recipient_account?: {
address?: string;
};
// eslint-disable-next-line @typescript-eslint/naming-convention -- MoonPay API field
deposit_rails?: unknown[];
};

Expand Down Expand Up @@ -162,7 +166,8 @@ export function mapNeoBankAutorampToRemoteSnapshot(
response: NeoBankAutorampResponse,
): AutorampRemoteSnapshot {
const depositRails = response.deposit_rails;
const hasDepositRails = Array.isArray(depositRails) && depositRails.length > 0;
const hasDepositRails =
Array.isArray(depositRails) && depositRails.length > 0;
const depositRailsSummary: AutorampDepositRailsSummary | undefined =
hasDepositRails || response.status === 'Approved'
? {
Expand Down Expand Up @@ -291,10 +296,10 @@ export class NeoBankService {
return url;
}

async #getJson<T>(
async #getJson<ResponseBody>(
path: string,
query?: NeoBankQueryParams,
): Promise<T> {
): Promise<ResponseBody> {
const url = this.#buildUrl(path, query);
return this.#policy.execute(async () => {
const headers = await this.#getRequestHeaders();
Expand All @@ -305,15 +310,15 @@ export class NeoBankService {
`Fetching '${url.toString()}' failed with status '${fetchResponse.status}'`,
);
}
return fetchResponse.json() as Promise<T>;
return fetchResponse.json() as Promise<ResponseBody>;
});
}

async #postJson<T>(
async #postJson<ResponseBody>(
path: string,
body: Record<string, unknown>,
options: NeoBankRequestOptions,
): Promise<T> {
): Promise<ResponseBody> {
const url = this.#buildUrl(path);
return this.#policy.execute(async () => {
const headers = await this.#getRequestHeaders(options);
Expand All @@ -329,11 +334,13 @@ export class NeoBankService {
`Fetching '${url.toString()}' failed with status '${fetchResponse.status}'`,
);
}
return fetchResponse.json() as Promise<T>;
return fetchResponse.json() as Promise<ResponseBody>;
});
}

#mapAutorampResponse(response: NeoBankAutorampResponse): AutorampRemoteSnapshot {
#mapAutorampResponse(
response: NeoBankAutorampResponse,
): AutorampRemoteSnapshot {
if (!response || typeof response !== 'object' || !response.id) {
throw new Error('Malformed response received from neo-bank autoramp API');
}
Expand Down
Loading
Loading