diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59fb52d8e9..66c85da473 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -136,6 +136,7 @@ /packages/profile-metrics-controller @MetaMask/mobile-platform @MetaMask/extension-platform ## Initialization +/packages/wallet/src/initialization/instances/ramps/ @MetaMask/money-movement /packages/wallet/src/initialization/instances/accounts-controller/ @MetaMask/accounts-engineers /packages/wallet/src/initialization/instances/approval-controller/ @MetaMask/confirmations /packages/wallet/src/initialization/instances/connectivity-controller/ @MetaMask/core-platform diff --git a/README.md b/README.md index 31241932b0..b67b27f59f 100644 --- a/README.md +++ b/README.md @@ -619,6 +619,7 @@ linkStyle default opacity:0.5 wallet --> keyring_controller; wallet --> messenger; wallet --> network_controller; + wallet --> ramps_controller; wallet --> remote_feature_flag_controller; wallet --> storage_service; wallet_cli --> base_controller; diff --git a/package.json b/package.json index 8f7ef1c68c..4a20fd6078 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/core-monorepo", - "version": "1077.0.0", + "version": "1078.0.0", "private": true, "description": "Monorepo for packages shared between MetaMask clients", "repository": { diff --git a/packages/wallet-cli/CHANGELOG.md b/packages/wallet-cli/CHANGELOG.md index 9058dc2005..1dd2d6219c 100644 --- a/packages/wallet-cli/CHANGELOG.md +++ b/packages/wallet-cli/CHANGELOG.md @@ -16,6 +16,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Bump `@metamask/wallet` from `^3.0.0` to `^5.0.0` ([#9218](https://github.com/MetaMask/core/pull/9218), [#9263](https://github.com/MetaMask/core/pull/9263)) +- Bump `@metamask/wallet` from `^3.0.0` to `^5.1.0` ([#9218](https://github.com/MetaMask/core/pull/9218), [#9263](https://github.com/MetaMask/core/pull/9263), [#9290](https://github.com/MetaMask/core/pull/9290)) [Unreleased]: https://github.com/MetaMask/core/ diff --git a/packages/wallet-cli/package.json b/packages/wallet-cli/package.json index 749220d8f5..547171bbdf 100644 --- a/packages/wallet-cli/package.json +++ b/packages/wallet-cli/package.json @@ -49,7 +49,7 @@ "@metamask/rpc-errors": "^7.0.2", "@metamask/storage-service": "^1.0.2", "@metamask/utils": "^11.11.0", - "@metamask/wallet": "^5.0.0", + "@metamask/wallet": "^5.1.0", "@oclif/core": "^4.10.5", "better-sqlite3": "^12.9.0", "immer": "^9.0.6" diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 9afc460900..0e6dc7d0b6 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.1.0] + +### Added + +- Export opt-in `RampsService`, `RampsController`, and `TransakService` initialization configurations from `@metamask/wallet/initialization/instances/ramps` for aggregator and native on-ramp flows. These are not included in default wallet instances; consumers pass them via `initializationConfigurations`. ([#9290](https://github.com/MetaMask/core/pull/9290)) + +### Changed + +- Bump `@metamask/ramps-controller` from `^14.3.0` to `^15.0.0` ([#9290](https://github.com/MetaMask/core/pull/9290)) + ## [5.0.0] ### Added @@ -73,7 +83,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial release ([#8838](https://github.com/MetaMask/core/pull/8838)) -[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/wallet@5.0.0...HEAD +[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/wallet@5.1.0...HEAD +[5.1.0]: https://github.com/MetaMask/core/compare/@metamask/wallet@5.0.0...@metamask/wallet@5.1.0 [5.0.0]: https://github.com/MetaMask/core/compare/@metamask/wallet@4.0.0...@metamask/wallet@5.0.0 [4.0.0]: https://github.com/MetaMask/core/compare/@metamask/wallet@3.0.0...@metamask/wallet@4.0.0 [3.0.0]: https://github.com/MetaMask/core/compare/@metamask/wallet@2.0.0...@metamask/wallet@3.0.0 diff --git a/packages/wallet/package.json b/packages/wallet/package.json index b2935ac95e..822f6e51da 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/wallet", - "version": "5.0.0", + "version": "5.1.0", "description": "Provides a shared framework for building MetaMask wallets", "keywords": [ "Ethereum", @@ -32,6 +32,16 @@ "default": "./dist/index.cjs" } }, + "./initialization/instances/ramps": { + "import": { + "types": "./dist/initialization/instances/ramps/index.d.mts", + "default": "./dist/initialization/instances/ramps/index.mjs" + }, + "require": { + "types": "./dist/initialization/instances/ramps/index.d.cts", + "default": "./dist/initialization/instances/ramps/index.cjs" + } + }, "./package.json": "./package.json" }, "publishConfig": { @@ -62,6 +72,7 @@ "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^1.2.0", "@metamask/network-controller": "^33.0.0", + "@metamask/ramps-controller": "^15.0.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/storage-service": "^1.0.2", diff --git a/packages/wallet/src/initialization/instances/ramps/index.ts b/packages/wallet/src/initialization/instances/ramps/index.ts new file mode 100644 index 0000000000..5bbe8bfb95 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/index.ts @@ -0,0 +1,6 @@ +export { rampsService } from './ramps-service/ramps-service'; +export type { RampsServiceInstanceOptions } from './ramps-service/types'; +export { rampsController } from './ramps-controller/ramps-controller'; +export type { RampsControllerInstanceOptions } from './ramps-controller/types'; +export { transakService } from './transak-service/transak-service'; +export type { TransakServiceInstanceOptions } from './transak-service/types'; diff --git a/packages/wallet/src/initialization/instances/ramps/ramps-controller/ramps-controller.test.ts b/packages/wallet/src/initialization/instances/ramps/ramps-controller/ramps-controller.test.ts new file mode 100644 index 0000000000..53cb573cdd --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/ramps-controller/ramps-controller.test.ts @@ -0,0 +1,26 @@ +import { Messenger } from '@metamask/messenger'; + +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../../defaults'; +import { rampsController } from './ramps-controller'; + +describe('rampsController', () => { + it('constructs a RampsController with default state', () => { + const parent: RootMessenger = new Messenger({ + namespace: 'Root', + }); + const messenger = rampsController.getMessenger(parent); + + const controller = rampsController.init({ + messenger, + state: undefined, + options: {}, + }); + + expect(controller.name).toBe('RampsController'); + expect(controller.state.userRegion).toBeNull(); + }); +}); diff --git a/packages/wallet/src/initialization/instances/ramps/ramps-controller/ramps-controller.ts b/packages/wallet/src/initialization/instances/ramps/ramps-controller/ramps-controller.ts new file mode 100644 index 0000000000..f004705bd3 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/ramps-controller/ramps-controller.ts @@ -0,0 +1,40 @@ +import { Messenger } from '@metamask/messenger'; +import { + RampsController, + RampsControllerMessenger, + RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS, +} from '@metamask/ramps-controller'; + +import { InitializationConfiguration } from '../../../types'; + +export const rampsController: InitializationConfiguration< + RampsController, + RampsControllerMessenger +> = { + name: 'RampsController', + init: ({ state, messenger, options }) => + new RampsController({ + messenger, + state: state ?? {}, + requestCacheTTL: options.requestCacheTTL, + requestCacheMaxSize: options.requestCacheMaxSize, + }), + getMessenger: (parent) => { + // Type cast required: RampsControllerMessenger's parent constraint includes + // RampsService/TransakService actions that are not in DefaultActions (since + // these are opt-in configs, not default wallet instances). + + const messenger: RampsControllerMessenger = new Messenger({ + namespace: 'RampsController', + parent: parent as never, + }); + + parent.delegate({ + messenger, + actions: [...RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS] as never[], + events: [], + }); + + return messenger; + }, +}; diff --git a/packages/wallet/src/initialization/instances/ramps/ramps-controller/types.ts b/packages/wallet/src/initialization/instances/ramps/ramps-controller/types.ts new file mode 100644 index 0000000000..57d608c8b8 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/ramps-controller/types.ts @@ -0,0 +1,9 @@ +import type { RampsControllerOptions } from '@metamask/ramps-controller'; + +/** + * Per-instance options for the wallet's `RampsController`. + */ +export type RampsControllerInstanceOptions = { + requestCacheTTL?: RampsControllerOptions['requestCacheTTL']; + requestCacheMaxSize?: RampsControllerOptions['requestCacheMaxSize']; +}; diff --git a/packages/wallet/src/initialization/instances/ramps/ramps-service/ramps-service.test.ts b/packages/wallet/src/initialization/instances/ramps/ramps-service/ramps-service.test.ts new file mode 100644 index 0000000000..7bca914db2 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/ramps-service/ramps-service.test.ts @@ -0,0 +1,31 @@ +import { Messenger } from '@metamask/messenger'; +import { RampsEnvironment } from '@metamask/ramps-controller'; + +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../../defaults'; +import { rampsService } from './ramps-service'; + +describe('rampsService', () => { + it('constructs a RampsService with injected options', () => { + const parent: RootMessenger = new Messenger({ + namespace: 'Root', + }); + const messenger = rampsService.getMessenger(parent); + const fetchMock = jest.fn(); + + const service = rampsService.init({ + messenger, + state: undefined, + options: { + environment: RampsEnvironment.Staging, + context: 'test', + fetch: fetchMock as unknown as typeof fetch, + }, + }); + + expect(service.name).toBe('RampsService'); + }); +}); diff --git a/packages/wallet/src/initialization/instances/ramps/ramps-service/ramps-service.ts b/packages/wallet/src/initialization/instances/ramps/ramps-service/ramps-service.ts new file mode 100644 index 0000000000..af1f80861a --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/ramps-service/ramps-service.ts @@ -0,0 +1,49 @@ +import { Messenger } from '@metamask/messenger'; +import { + RampsService, + RampsServiceMessenger, +} from '@metamask/ramps-controller'; + +import { InitializationConfiguration } from '../../../types'; + +/** + * Opt-in initialization config for `RampsService`. + * + * **Prerequisite:** `AuthenticationController` must be registered on the root + * messenger before this config is used — `RampsService` delegates + * `AuthenticationController:getBearerToken` and will throw at runtime if that + * action is not available. + */ +export const rampsService: InitializationConfiguration< + RampsService, + RampsServiceMessenger +> = { + name: 'RampsService', + init: ({ messenger, options }) => + new RampsService({ + messenger, + environment: options.environment, + context: options.context, + fetch: options.fetch, + policyOptions: options.policyOptions, + baseUrlOverride: options.baseUrlOverride, + }), + getMessenger: (parent) => { + // Type cast required: RampsServiceMessenger's parent constraint includes + // AuthenticationController:getBearerToken which is not in DefaultActions + // (these are opt-in configs, not default wallet instances). + + const messenger: RampsServiceMessenger = new Messenger({ + namespace: 'RampsService', + parent: parent as never, + }); + + parent.delegate({ + messenger, + actions: ['AuthenticationController:getBearerToken'] as never[], + events: [], + }); + + return messenger; + }, +}; diff --git a/packages/wallet/src/initialization/instances/ramps/ramps-service/types.ts b/packages/wallet/src/initialization/instances/ramps/ramps-service/types.ts new file mode 100644 index 0000000000..7d60b85fe5 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/ramps-service/types.ts @@ -0,0 +1,13 @@ +import type { CreateServicePolicyOptions } from '@metamask/controller-utils'; +import type { RampsEnvironment } from '@metamask/ramps-controller'; + +/** + * Per-instance options for the wallet's `RampsService`. + */ +export type RampsServiceInstanceOptions = { + environment?: RampsEnvironment; + context: string; + fetch: typeof fetch; + policyOptions?: CreateServicePolicyOptions; + baseUrlOverride?: string; +}; diff --git a/packages/wallet/src/initialization/instances/ramps/transak-service/transak-service.test.ts b/packages/wallet/src/initialization/instances/ramps/transak-service/transak-service.test.ts new file mode 100644 index 0000000000..b7c132e61f --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/transak-service/transak-service.test.ts @@ -0,0 +1,31 @@ +import { Messenger } from '@metamask/messenger'; +import { TransakEnvironment } from '@metamask/ramps-controller'; + +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../../defaults'; +import { transakService } from './transak-service'; + +describe('transakService', () => { + it('constructs a TransakService with injected options', () => { + const parent: RootMessenger = new Messenger({ + namespace: 'Root', + }); + const messenger = transakService.getMessenger(parent); + const fetchMock = jest.fn(); + + const service = transakService.init({ + messenger, + state: undefined, + options: { + environment: TransakEnvironment.Staging, + context: 'test', + fetch: fetchMock as unknown as typeof fetch, + }, + }); + + expect(service.name).toBe('TransakService'); + }); +}); diff --git a/packages/wallet/src/initialization/instances/ramps/transak-service/transak-service.ts b/packages/wallet/src/initialization/instances/ramps/transak-service/transak-service.ts new file mode 100644 index 0000000000..d039cf9656 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/transak-service/transak-service.ts @@ -0,0 +1,32 @@ +import { Messenger } from '@metamask/messenger'; +import { + TransakService, + TransakServiceMessenger, +} from '@metamask/ramps-controller'; + +import { InitializationConfiguration } from '../../../types'; + +export const transakService: InitializationConfiguration< + TransakService, + TransakServiceMessenger +> = { + name: 'TransakService', + init: ({ messenger, options }) => + new TransakService({ + messenger, + environment: options.environment, + context: options.context, + fetch: options.fetch, + apiKey: options.apiKey, + policyOptions: options.policyOptions, + orderRetryDelayMs: options.orderRetryDelayMs, + }), + getMessenger: (parent) => + // Type cast required: TransakServiceMessenger's parent type constraint + // does not extend DefaultActions (opt-in config, not a default instance). + + new Messenger({ + namespace: 'TransakService', + parent: parent as never, + }), +}; diff --git a/packages/wallet/src/initialization/instances/ramps/transak-service/types.ts b/packages/wallet/src/initialization/instances/ramps/transak-service/types.ts new file mode 100644 index 0000000000..3254d078a5 --- /dev/null +++ b/packages/wallet/src/initialization/instances/ramps/transak-service/types.ts @@ -0,0 +1,14 @@ +import type { CreateServicePolicyOptions } from '@metamask/controller-utils'; +import type { TransakEnvironment } from '@metamask/ramps-controller'; + +/** + * Per-instance options for the wallet's `TransakService`. + */ +export type TransakServiceInstanceOptions = { + environment?: TransakEnvironment; + context: string; + fetch: typeof fetch; + apiKey?: string; + policyOptions?: CreateServicePolicyOptions; + orderRetryDelayMs?: number; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 6a66903dde..99f224cc36 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -9,6 +9,9 @@ import type { ApprovalControllerInstanceOptions } from './initialization/instanc import type { ConnectivityControllerInstanceOptions } from './initialization/instances/connectivity-controller/types'; import type { KeyringControllerInstanceOptions } from './initialization/instances/keyring-controller/types'; import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; +import type { RampsControllerInstanceOptions } from './initialization/instances/ramps/ramps-controller/types'; +import type { RampsServiceInstanceOptions } from './initialization/instances/ramps/ramps-service/types'; +import type { TransakServiceInstanceOptions } from './initialization/instances/ramps/transak-service/types'; import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; import type { InitializationConfiguration } from './initialization/types'; @@ -28,6 +31,9 @@ export type InstanceSpecificOptions = { connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; networkController: NetworkControllerInstanceOptions; + rampsController?: RampsControllerInstanceOptions; + rampsService?: RampsServiceInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; storageService: StorageServiceInstanceOptions; + transakService?: TransakServiceInstanceOptions; }; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index fcf397e033..1880b6ec94 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -14,6 +14,7 @@ { "path": "../keyring-controller/tsconfig.build.json" }, { "path": "../messenger/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../ramps-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, { "path": "../storage-service/tsconfig.build.json" } ], diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index a206b1baba..4341735328 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -12,6 +12,7 @@ { "path": "../keyring-controller/tsconfig.json" }, { "path": "../messenger/tsconfig.json" }, { "path": "../network-controller/tsconfig.json" }, + { "path": "../ramps-controller/tsconfig.json" }, { "path": "../remote-feature-flag-controller/tsconfig.json" }, { "path": "../storage-service/tsconfig.json" } ],