@metamask-previews/money-account-upgrade-controller 3.0.2-preview-6b7c25c → 3.0.2-preview-00a181c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +0 -11
  2. package/dist/MoneyAccountUpgradeController-method-action-types.cjs.map +1 -1
  3. package/dist/MoneyAccountUpgradeController-method-action-types.d.cts +0 -4
  4. package/dist/MoneyAccountUpgradeController-method-action-types.d.cts.map +1 -1
  5. package/dist/MoneyAccountUpgradeController-method-action-types.d.mts +0 -4
  6. package/dist/MoneyAccountUpgradeController-method-action-types.d.mts.map +1 -1
  7. package/dist/MoneyAccountUpgradeController-method-action-types.mjs.map +1 -1
  8. package/dist/MoneyAccountUpgradeController.cjs +40 -178
  9. package/dist/MoneyAccountUpgradeController.cjs.map +1 -1
  10. package/dist/MoneyAccountUpgradeController.d.cts +19 -88
  11. package/dist/MoneyAccountUpgradeController.d.cts.map +1 -1
  12. package/dist/MoneyAccountUpgradeController.d.mts +19 -88
  13. package/dist/MoneyAccountUpgradeController.d.mts.map +1 -1
  14. package/dist/MoneyAccountUpgradeController.mjs +41 -179
  15. package/dist/MoneyAccountUpgradeController.mjs.map +1 -1
  16. package/dist/errors.cjs +1 -14
  17. package/dist/errors.cjs.map +1 -1
  18. package/dist/errors.d.cts +0 -9
  19. package/dist/errors.d.cts.map +1 -1
  20. package/dist/errors.d.mts +0 -9
  21. package/dist/errors.d.mts.map +1 -1
  22. package/dist/errors.mjs +0 -12
  23. package/dist/errors.mjs.map +1 -1
  24. package/dist/index.cjs +1 -2
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +2 -2
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.mts +2 -2
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.mjs +1 -1
  31. package/dist/index.mjs.map +1 -1
  32. package/package.json +1 -3
package/CHANGELOG.md CHANGED
@@ -7,19 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Added
11
-
12
- - Add a public `sync()` method that re-evaluates the bootstrap gates against live state, for clients whose `isEnabled` hook reads client-only signals (onboarding, preferences) that should also re-trigger the bootstrap ([#10072](https://github.com/MetaMask/core/pull/10072))
13
- - Add `MissingMoneyAccountVaultConfigError`, reported through the `onBootstrapError` hook (once per controller lifetime) when the enable flag is on but `moneyAccountVaultConfig` is unserved or malformed ([#10072](https://github.com/MetaMask/core/pull/10072))
14
-
15
10
  ### Changed
16
11
 
17
- - **BREAKING:** The controller now owns its bootstrap: it subscribes to `RemoteFeatureFlagController:stateChanged` and `KeyringController:stateChanged`, gates on an unlocked wallet with an HD keyring, parses the `moneyAccountVaultConfig` remote feature flag, and runs a serialized bootstrap that re-checks its gates across `await` points and re-runs when the vault config changes ([#10072](https://github.com/MetaMask/core/pull/10072))
18
- - `init()` is now the no-argument lifecycle entry point (subscribe and first sync), to be called once after all controllers and services the messenger reaches are constructed. The former `init({ chainId, boringVaultAddress })` config-arming routine is internal and reads the chain id and boring vault from the flag; clients that called it must remove that orchestration.
19
- - The constructor requires a `hooks` option carrying the client-specific parts of the bootstrap: `isEnabled(remoteFeatureFlags)` (required — version-gated flag evaluation depends on the client version, and clients may add gates such as a basic-functionality toggle), plus optional `isEligible()` (an async pre-bootstrap gate, e.g. fail-closed geolocation), `ensureChainConfigured(vaultConfig)` (client-specific network adding), and `onBootstrapError(error)`.
20
- - The messenger must now allow the `RemoteFeatureFlagController:getState` and `KeyringController:getState` actions and the `RemoteFeatureFlagController:stateChanged` and `KeyringController:stateChanged` events.
21
- - **BREAKING:** `upgradeAccount()` now waits for an in-flight bootstrap instead of throwing, throws a new not-bootstrapped error message when no bootstrap has armed a config, and is disarmed when `isEnabled` flips off so it cannot run against a config armed while the feature was still enabled ([#10072](https://github.com/MetaMask/core/pull/10072))
22
- - Add `@metamask/money-account-utils` and `@metamask/remote-feature-flag-controller` as dependencies ([#10072](https://github.com/MetaMask/core/pull/10072))
23
12
  - Bump `@metamask/authenticated-user-storage` from `^3.0.1` to `^3.0.2` ([#9972](https://github.com/MetaMask/core/pull/9972))
24
13
  - Bump `@metamask/chomp-api-service` from `^4.0.0` to `^4.0.1` ([#9972](https://github.com/MetaMask/core/pull/9972))
25
14
 
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.cjs","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { MoneyAccountUpgradeController } from './MoneyAccountUpgradeController.js';\n\n/**\n * Runs each step in the upgrade sequence in order. A step that reports\n * `'already-done'` is skipped without performing any action; a step that\n * reports `'completed'` has performed its action. An error thrown by any\n * step halts the sequence and is re-thrown wrapped in a\n * {@link MoneyAccountUpgradeStepError} that records which step failed (the\n * original error is preserved as `cause`).\n *\n * A run that completes is recorded in state (keyed by lowercased address,\n * fingerprinted against the active config); subsequent calls for a\n * recorded account return immediately without running any steps. If the\n * active config no longer matches the recorded fingerprint, the sequence\n * re-runs.\n *\n * A call that arrives while the bootstrap is still in flight waits for it\n * rather than failing; it only throws when no bootstrap has armed a config\n * (feature disabled, wallet locked, or the last bootstrap failed).\n *\n * @param address - The Money Account address to upgrade.\n */\nexport type MoneyAccountUpgradeControllerUpgradeAccountAction = {\n type: `MoneyAccountUpgradeController:upgradeAccount`;\n handler: MoneyAccountUpgradeController['upgradeAccount'];\n};\n\n/**\n * Union of all MoneyAccountUpgradeController action types.\n */\nexport type MoneyAccountUpgradeControllerMethodActions =\n MoneyAccountUpgradeControllerUpgradeAccountAction;\n"]}
1
+ {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.cjs","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { MoneyAccountUpgradeController } from './MoneyAccountUpgradeController.js';\n\n/**\n * Runs each step in the upgrade sequence in order. A step that reports\n * `'already-done'` is skipped without performing any action; a step that\n * reports `'completed'` has performed its action. An error thrown by any\n * step halts the sequence and is re-thrown wrapped in a\n * {@link MoneyAccountUpgradeStepError} that records which step failed (the\n * original error is preserved as `cause`).\n *\n * A run that completes is recorded in state (keyed by lowercased address,\n * fingerprinted against the active config); subsequent calls for a\n * recorded account return immediately without running any steps. If the\n * active config no longer matches the recorded fingerprint, the sequence\n * re-runs.\n *\n * @param address - The Money Account address to upgrade.\n */\nexport type MoneyAccountUpgradeControllerUpgradeAccountAction = {\n type: `MoneyAccountUpgradeController:upgradeAccount`;\n handler: MoneyAccountUpgradeController['upgradeAccount'];\n};\n\n/**\n * Union of all MoneyAccountUpgradeController action types.\n */\nexport type MoneyAccountUpgradeControllerMethodActions =\n MoneyAccountUpgradeControllerUpgradeAccountAction;\n"]}
@@ -17,10 +17,6 @@ import type { MoneyAccountUpgradeController } from "./MoneyAccountUpgradeControl
17
17
  * active config no longer matches the recorded fingerprint, the sequence
18
18
  * re-runs.
19
19
  *
20
- * A call that arrives while the bootstrap is still in flight waits for it
21
- * rather than failing; it only throws when no bootstrap has armed a config
22
- * (feature disabled, wallet locked, or the last bootstrap failed).
23
- *
24
20
  * @param address - The Money Account address to upgrade.
25
21
  */
26
22
  export type MoneyAccountUpgradeControllerUpgradeAccountAction = {
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.d.cts","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,6BAA6B,EAAE,4CAA2C;AAExF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,6BAA6B,CAAC,gBAAgB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0CAA0C,GACpD,iDAAiD,CAAC"}
1
+ {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.d.cts","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,6BAA6B,EAAE,4CAA2C;AAExF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,6BAA6B,CAAC,gBAAgB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0CAA0C,GACpD,iDAAiD,CAAC"}
@@ -17,10 +17,6 @@ import type { MoneyAccountUpgradeController } from "./MoneyAccountUpgradeControl
17
17
  * active config no longer matches the recorded fingerprint, the sequence
18
18
  * re-runs.
19
19
  *
20
- * A call that arrives while the bootstrap is still in flight waits for it
21
- * rather than failing; it only throws when no bootstrap has armed a config
22
- * (feature disabled, wallet locked, or the last bootstrap failed).
23
- *
24
20
  * @param address - The Money Account address to upgrade.
25
21
  */
26
22
  export type MoneyAccountUpgradeControllerUpgradeAccountAction = {
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.d.mts","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,6BAA6B,EAAE,4CAA2C;AAExF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,6BAA6B,CAAC,gBAAgB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0CAA0C,GACpD,iDAAiD,CAAC"}
1
+ {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.d.mts","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,6BAA6B,EAAE,4CAA2C;AAExF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,6BAA6B,CAAC,gBAAgB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0CAA0C,GACpD,iDAAiD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.mjs","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { MoneyAccountUpgradeController } from './MoneyAccountUpgradeController.js';\n\n/**\n * Runs each step in the upgrade sequence in order. A step that reports\n * `'already-done'` is skipped without performing any action; a step that\n * reports `'completed'` has performed its action. An error thrown by any\n * step halts the sequence and is re-thrown wrapped in a\n * {@link MoneyAccountUpgradeStepError} that records which step failed (the\n * original error is preserved as `cause`).\n *\n * A run that completes is recorded in state (keyed by lowercased address,\n * fingerprinted against the active config); subsequent calls for a\n * recorded account return immediately without running any steps. If the\n * active config no longer matches the recorded fingerprint, the sequence\n * re-runs.\n *\n * A call that arrives while the bootstrap is still in flight waits for it\n * rather than failing; it only throws when no bootstrap has armed a config\n * (feature disabled, wallet locked, or the last bootstrap failed).\n *\n * @param address - The Money Account address to upgrade.\n */\nexport type MoneyAccountUpgradeControllerUpgradeAccountAction = {\n type: `MoneyAccountUpgradeController:upgradeAccount`;\n handler: MoneyAccountUpgradeController['upgradeAccount'];\n};\n\n/**\n * Union of all MoneyAccountUpgradeController action types.\n */\nexport type MoneyAccountUpgradeControllerMethodActions =\n MoneyAccountUpgradeControllerUpgradeAccountAction;\n"]}
1
+ {"version":3,"file":"MoneyAccountUpgradeController-method-action-types.mjs","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { MoneyAccountUpgradeController } from './MoneyAccountUpgradeController.js';\n\n/**\n * Runs each step in the upgrade sequence in order. A step that reports\n * `'already-done'` is skipped without performing any action; a step that\n * reports `'completed'` has performed its action. An error thrown by any\n * step halts the sequence and is re-thrown wrapped in a\n * {@link MoneyAccountUpgradeStepError} that records which step failed (the\n * original error is preserved as `cause`).\n *\n * A run that completes is recorded in state (keyed by lowercased address,\n * fingerprinted against the active config); subsequent calls for a\n * recorded account return immediately without running any steps. If the\n * active config no longer matches the recorded fingerprint, the sequence\n * re-runs.\n *\n * @param address - The Money Account address to upgrade.\n */\nexport type MoneyAccountUpgradeControllerUpgradeAccountAction = {\n type: `MoneyAccountUpgradeController:upgradeAccount`;\n handler: MoneyAccountUpgradeController['upgradeAccount'];\n};\n\n/**\n * Union of all MoneyAccountUpgradeController action types.\n */\nexport type MoneyAccountUpgradeControllerMethodActions =\n MoneyAccountUpgradeControllerUpgradeAccountAction;\n"]}
@@ -10,13 +10,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _MoneyAccountUpgradeController_instances, _MoneyAccountUpgradeController_config, _MoneyAccountUpgradeController_isEnabled, _MoneyAccountUpgradeController_isEligible, _MoneyAccountUpgradeController_ensureChainConfigured, _MoneyAccountUpgradeController_onBootstrapError, _MoneyAccountUpgradeController_initialized, _MoneyAccountUpgradeController_bootstrap, _MoneyAccountUpgradeController_bootstrappedConfig, _MoneyAccountUpgradeController_missingConfigReported, _MoneyAccountUpgradeController_steps, _MoneyAccountUpgradeController_isWalletReady, _MoneyAccountUpgradeController_areGatesOpen, _MoneyAccountUpgradeController_scheduleBootstrap, _MoneyAccountUpgradeController_forget, _MoneyAccountUpgradeController_reportMissingConfig, _MoneyAccountUpgradeController_applyVaultConfig;
13
+ var _MoneyAccountUpgradeController_config, _MoneyAccountUpgradeController_steps;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.MoneyAccountUpgradeController = exports.getDefaultMoneyAccountUpgradeControllerState = exports.controllerName = void 0;
16
16
  const base_controller_1 = require("@metamask/base-controller");
17
17
  const delegation_deployments_1 = require("@metamask/delegation-deployments");
18
- const keyring_controller_1 = require("@metamask/keyring-controller");
19
- const money_account_utils_1 = require("@metamask/money-account-utils");
20
18
  const utils_1 = require("@metamask/utils");
21
19
  const errors_js_1 = require("./errors.cjs");
22
20
  const associate_address_js_1 = require("./steps/associate-address.cjs");
@@ -53,26 +51,7 @@ function getDefaultMoneyAccountUpgradeControllerState() {
53
51
  exports.getDefaultMoneyAccountUpgradeControllerState = getDefaultMoneyAccountUpgradeControllerState;
54
52
  const MESSENGER_EXPOSED_METHODS = ['upgradeAccount'];
55
53
  /**
56
- * Controller that owns the Money Account upgrade sequence and its own
57
- * bootstrap.
58
- *
59
- * After {@link MoneyAccountUpgradeController.init} is called (once, after all
60
- * controllers are constructed), the controller watches
61
- * `RemoteFeatureFlagController` and `KeyringController` state and bootstraps
62
- * itself when every gate is open:
63
- *
64
- * 1. the client's `isEnabled` hook returns `true` for the current flags,
65
- * 2. the wallet is unlocked with an HD keyring,
66
- * 3. the client's `isEligible` hook (if any) resolves `true`, and
67
- * 4. the `moneyAccountVaultConfig` flag parses.
68
- *
69
- * The bootstrap awaits the client's `ensureChainConfigured` hook and then
70
- * fetches CHOMP service details to arm the upgrade config. Gates 1–2 are
71
- * re-checked across the `await` points so a lock or an `isEnabled` flip
72
- * mid-bootstrap cannot still produce an external call; a skipped or failed
73
- * run is forgotten so the next trigger retries it. The bootstrap re-runs
74
- * whenever the vault config changes, and `isEnabled` going `false` disarms
75
- * the controller entirely.
54
+ * Controller that orchestrates the Money Account upgrade sequence.
76
55
  */
77
56
  class MoneyAccountUpgradeController extends base_controller_1.BaseController {
78
57
  /**
@@ -81,10 +60,8 @@ class MoneyAccountUpgradeController extends base_controller_1.BaseController {
81
60
  * @param options - The options for constructing the controller.
82
61
  * @param options.messenger - The messenger to use for inter-controller communication.
83
62
  * @param options.state - The initial state, merged with the defaults.
84
- * @param options.hooks - The client hooks for the bootstrap; see
85
- * {@link MoneyAccountUpgradeControllerHooks}.
86
63
  */
87
- constructor({ messenger, state, hooks, }) {
64
+ constructor({ messenger, state, }) {
88
65
  super({
89
66
  messenger,
90
67
  metadata: moneyAccountUpgradeControllerMetadata,
@@ -94,80 +71,55 @@ class MoneyAccountUpgradeController extends base_controller_1.BaseController {
94
71
  ...state,
95
72
  },
96
73
  });
97
- _MoneyAccountUpgradeController_instances.add(this);
98
74
  _MoneyAccountUpgradeController_config.set(this, void 0);
99
- _MoneyAccountUpgradeController_isEnabled.set(this, void 0);
100
- _MoneyAccountUpgradeController_isEligible.set(this, void 0);
101
- _MoneyAccountUpgradeController_ensureChainConfigured.set(this, void 0);
102
- _MoneyAccountUpgradeController_onBootstrapError.set(this, void 0);
103
- _MoneyAccountUpgradeController_initialized.set(this, false);
104
- _MoneyAccountUpgradeController_bootstrap.set(this, void 0);
105
- _MoneyAccountUpgradeController_bootstrappedConfig.set(this, void 0);
106
- _MoneyAccountUpgradeController_missingConfigReported.set(this, false);
107
75
  _MoneyAccountUpgradeController_steps.set(this, [
108
76
  associate_address_js_1.associateAddressStep,
109
77
  eip_7702_authorization_js_1.eip7702AuthorizationStep,
110
78
  build_delegations_js_1.buildDelegationStep,
111
79
  register_intents_js_1.registerIntentsStep,
112
80
  ]);
113
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_isEnabled, hooks.isEnabled, "f");
114
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_isEligible, hooks.isEligible ?? (async () => true), "f");
115
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_ensureChainConfigured, hooks.ensureChainConfigured ?? (async () => undefined), "f");
116
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_onBootstrapError, hooks.onBootstrapError ?? (() => undefined), "f");
117
81
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
118
82
  }
119
83
  /**
120
- * Start the controller's bootstrap: subscribe to the feature-flag and
121
- * keyring triggers and run an initial sync. Call once, after all
122
- * controllers and services the messenger reaches are constructed — this is
123
- * the only method that may be called before the controller is bootstrapped,
124
- * and it is the reason the constructor performs no messenger calls.
84
+ * Fetches service details and validates the controller can operate on the
85
+ * given chain. Resolves the Delegation Framework contract addresses for the
86
+ * chain from `@metamask/delegation-deployments`.
87
+ *
88
+ * @param params - The parameters for initialization.
89
+ * @param params.chainId - The chain to initialize for.
90
+ * @param params.boringVaultAddress - The Veda boring vault contract
91
+ * (vmUSD) for the given chain. Used as the withdrawal-side delegation
92
+ * token. Supplied by the consumer until the CHOMP service-details API
93
+ * exposes it.
125
94
  */
126
- init() {
127
- if (__classPrivateFieldGet(this, _MoneyAccountUpgradeController_initialized, "f")) {
128
- return;
95
+ async init({ chainId, boringVaultAddress, }) {
96
+ const contracts = delegation_deployments_1.DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION][(0, utils_1.hexToNumber)(chainId)];
97
+ if (!contracts) {
98
+ throw new Error(`Delegation Framework ${DELEGATION_FRAMEWORK_VERSION} is not deployed on chain ${chainId}`);
129
99
  }
130
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_initialized, true, "f");
131
- this.messenger.subscribe('RemoteFeatureFlagController:stateChanged', () => this.sync());
132
- this.messenger.subscribe('KeyringController:stateChanged', () => this.sync());
133
- this.sync();
134
- }
135
- /**
136
- * Re-evaluate the bootstrap gates against live state and schedule a
137
- * bootstrap run when they are open and the vault config is new. Runs on
138
- * every feature-flag and keyring state change; clients with additional
139
- * gates read inside their `isEnabled` hook (onboarding, preferences)
140
- * should call this when those change too. Never throws: a failure is
141
- * reported through `onBootstrapError` and retried on the next trigger.
142
- */
143
- sync() {
144
- try {
145
- const { remoteFeatureFlags } = this.messenger.call('RemoteFeatureFlagController:getState');
146
- if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_isEnabled, "f").call(this, remoteFeatureFlags)) {
147
- // Disarm so a later `upgradeAccount` cannot run against a config
148
- // armed while the feature was still enabled. Re-enabling re-runs the
149
- // bootstrap from scratch.
150
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_config, undefined, "f");
151
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_bootstrappedConfig, undefined, "f");
152
- return;
153
- }
154
- if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_isWalletReady).call(this)) {
155
- return;
156
- }
157
- const vaultConfig = (0, money_account_utils_1.getMoneyAccountVaultConfig)(remoteFeatureFlags);
158
- if (!vaultConfig) {
159
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_reportMissingConfig).call(this);
160
- return;
161
- }
162
- if (__classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrappedConfig, "f") &&
163
- (0, money_account_utils_1.areMoneyAccountVaultConfigsEqual)(vaultConfig, __classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrappedConfig, "f"))) {
164
- return;
165
- }
166
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_scheduleBootstrap).call(this, vaultConfig);
100
+ const response = await this.messenger.call('ChompApiService:getServiceDetails', [chainId]);
101
+ const chain = response.chains[chainId];
102
+ if (!chain) {
103
+ throw new Error(`Chain ${chainId} not found in service details response`);
167
104
  }
168
- catch (error) {
169
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_onBootstrapError, "f").call(this, error);
105
+ const { vedaProtocol } = chain.protocol;
106
+ if (!vedaProtocol) {
107
+ throw new Error(`vedaProtocol not found for chain ${chainId} in service details response`);
170
108
  }
109
+ if (vedaProtocol.supportedTokens.length === 0) {
110
+ throw new Error(`No supported tokens found for vedaProtocol on chain ${chainId}`);
111
+ }
112
+ __classPrivateFieldSet(this, _MoneyAccountUpgradeController_config, {
113
+ chainId,
114
+ delegateAddress: chain.autoDepositDelegate,
115
+ musdTokenAddress: vedaProtocol.supportedTokens[0].tokenAddress,
116
+ boringVaultAddress,
117
+ vedaVaultAdapterAddress: vedaProtocol.adapterAddress,
118
+ delegatorImplAddress: contracts.EIP7702StatelessDeleGatorImpl,
119
+ erc20TransferAmountEnforcer: contracts.ERC20TransferAmountEnforcer,
120
+ redeemerEnforcer: contracts.RedeemerEnforcer,
121
+ valueLteEnforcer: contracts.ValueLteEnforcer,
122
+ }, "f");
171
123
  }
172
124
  /**
173
125
  * Runs each step in the upgrade sequence in order. A step that reports
@@ -183,18 +135,11 @@ class MoneyAccountUpgradeController extends base_controller_1.BaseController {
183
135
  * active config no longer matches the recorded fingerprint, the sequence
184
136
  * re-runs.
185
137
  *
186
- * A call that arrives while the bootstrap is still in flight waits for it
187
- * rather than failing; it only throws when no bootstrap has armed a config
188
- * (feature disabled, wallet locked, or the last bootstrap failed).
189
- *
190
138
  * @param address - The Money Account address to upgrade.
191
139
  */
192
140
  async upgradeAccount(address) {
193
- if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_config, "f") && __classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrap, "f")) {
194
- await __classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrap, "f").catch(() => undefined);
195
- }
196
141
  if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_config, "f")) {
197
- throw new Error('MoneyAccountUpgradeController is not bootstrapped: upgradeAccount() requires the feature flag on, the wallet unlocked, and a successful bootstrap');
142
+ throw new Error('MoneyAccountUpgradeController must be initialized via init() before upgradeAccount() can be called');
198
143
  }
199
144
  const config = __classPrivateFieldGet(this, _MoneyAccountUpgradeController_config, "f");
200
145
  const accountKey = address.toLowerCase();
@@ -224,90 +169,7 @@ class MoneyAccountUpgradeController extends base_controller_1.BaseController {
224
169
  }
225
170
  }
226
171
  exports.MoneyAccountUpgradeController = MoneyAccountUpgradeController;
227
- _MoneyAccountUpgradeController_config = new WeakMap(), _MoneyAccountUpgradeController_isEnabled = new WeakMap(), _MoneyAccountUpgradeController_isEligible = new WeakMap(), _MoneyAccountUpgradeController_ensureChainConfigured = new WeakMap(), _MoneyAccountUpgradeController_onBootstrapError = new WeakMap(), _MoneyAccountUpgradeController_initialized = new WeakMap(), _MoneyAccountUpgradeController_bootstrap = new WeakMap(), _MoneyAccountUpgradeController_bootstrappedConfig = new WeakMap(), _MoneyAccountUpgradeController_missingConfigReported = new WeakMap(), _MoneyAccountUpgradeController_steps = new WeakMap(), _MoneyAccountUpgradeController_instances = new WeakSet(), _MoneyAccountUpgradeController_isWalletReady = function _MoneyAccountUpgradeController_isWalletReady() {
228
- const { isUnlocked, keyrings } = this.messenger.call('KeyringController:getState');
229
- return (isUnlocked && keyrings.some((keyring) => keyring.type === keyring_controller_1.KeyringTypes.hd));
230
- }, _MoneyAccountUpgradeController_areGatesOpen = function _MoneyAccountUpgradeController_areGatesOpen() {
231
- const { remoteFeatureFlags } = this.messenger.call('RemoteFeatureFlagController:getState');
232
- return __classPrivateFieldGet(this, _MoneyAccountUpgradeController_isEnabled, "f").call(this, remoteFeatureFlags) && __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_isWalletReady).call(this);
233
- }, _MoneyAccountUpgradeController_scheduleBootstrap = function _MoneyAccountUpgradeController_scheduleBootstrap(vaultConfig) {
234
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_bootstrappedConfig, vaultConfig, "f");
235
- const run = async () => {
236
- // The gates were checked when this run was scheduled, but it may start
237
- // much later, chained behind an in-flight bootstrap. Eligibility comes
238
- // after the gates so a disabled client also skips the (possibly
239
- // external) eligibility lookup.
240
- if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_areGatesOpen).call(this) || !(await __classPrivateFieldGet(this, _MoneyAccountUpgradeController_isEligible, "f").call(this))) {
241
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_forget).call(this, vaultConfig);
242
- return;
243
- }
244
- await __classPrivateFieldGet(this, _MoneyAccountUpgradeController_ensureChainConfigured, "f").call(this, vaultConfig);
245
- // Configuring the chain can suspend for a while, so re-check the gates
246
- // before the external CHOMP call.
247
- if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_areGatesOpen).call(this)) {
248
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_forget).call(this, vaultConfig);
249
- return;
250
- }
251
- await __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_applyVaultConfig).call(this, vaultConfig);
252
- };
253
- const bootstrap = __classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrap, "f")
254
- ? __classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrap, "f").catch(() => undefined).then(run)
255
- : run();
256
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_bootstrap, bootstrap, "f");
257
- bootstrap.catch((error) => {
258
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_onBootstrapError, "f").call(this, error);
259
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_instances, "m", _MoneyAccountUpgradeController_forget).call(this, vaultConfig);
260
- });
261
- }, _MoneyAccountUpgradeController_forget = function _MoneyAccountUpgradeController_forget(vaultConfig) {
262
- if (__classPrivateFieldGet(this, _MoneyAccountUpgradeController_bootstrappedConfig, "f") === vaultConfig) {
263
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_bootstrappedConfig, undefined, "f");
264
- }
265
- }, _MoneyAccountUpgradeController_reportMissingConfig = function _MoneyAccountUpgradeController_reportMissingConfig() {
266
- if (!__classPrivateFieldGet(this, _MoneyAccountUpgradeController_missingConfigReported, "f")) {
267
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_missingConfigReported, true, "f");
268
- __classPrivateFieldGet(this, _MoneyAccountUpgradeController_onBootstrapError, "f").call(this, new errors_js_1.MissingMoneyAccountVaultConfigError());
269
- }
270
- }, _MoneyAccountUpgradeController_applyVaultConfig =
271
- /**
272
- * Fetches service details and validates the controller can operate on the
273
- * vault's chain, arming the upgrade config `upgradeAccount` runs against.
274
- * Resolves the Delegation Framework contract addresses for the chain from
275
- * `@metamask/delegation-deployments`.
276
- *
277
- * @param vaultConfig - The vault config to arm; its `boringVault` is the
278
- * withdrawal-side delegation token (vmUSD), supplied via the flag until
279
- * the CHOMP service-details API exposes it.
280
- */
281
- async function _MoneyAccountUpgradeController_applyVaultConfig(vaultConfig) {
282
- const { chainId, boringVault: boringVaultAddress } = vaultConfig;
283
- const contracts = delegation_deployments_1.DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION][(0, utils_1.hexToNumber)(chainId)];
284
- if (!contracts) {
285
- throw new Error(`Delegation Framework ${DELEGATION_FRAMEWORK_VERSION} is not deployed on chain ${chainId}`);
286
- }
287
- const response = await this.messenger.call('ChompApiService:getServiceDetails', [chainId]);
288
- const chain = response.chains[chainId];
289
- if (!chain) {
290
- throw new Error(`Chain ${chainId} not found in service details response`);
291
- }
292
- const { vedaProtocol } = chain.protocol;
293
- if (!vedaProtocol) {
294
- throw new Error(`vedaProtocol not found for chain ${chainId} in service details response`);
295
- }
296
- if (vedaProtocol.supportedTokens.length === 0) {
297
- throw new Error(`No supported tokens found for vedaProtocol on chain ${chainId}`);
298
- }
299
- __classPrivateFieldSet(this, _MoneyAccountUpgradeController_config, {
300
- chainId,
301
- delegateAddress: chain.autoDepositDelegate,
302
- musdTokenAddress: vedaProtocol.supportedTokens[0].tokenAddress,
303
- boringVaultAddress,
304
- vedaVaultAdapterAddress: vedaProtocol.adapterAddress,
305
- delegatorImplAddress: contracts.EIP7702StatelessDeleGatorImpl,
306
- erc20TransferAmountEnforcer: contracts.ERC20TransferAmountEnforcer,
307
- redeemerEnforcer: contracts.RedeemerEnforcer,
308
- valueLteEnforcer: contracts.ValueLteEnforcer,
309
- }, "f");
310
- };
172
+ _MoneyAccountUpgradeController_config = new WeakMap(), _MoneyAccountUpgradeController_steps = new WeakMap();
311
173
  /**
312
174
  * Derives a stable fingerprint of the config fields that define what
313
175
  * "upgraded" means for an account. A recorded upgrade is only trusted while
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyAccountUpgradeController.cjs","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AASA,+DAA2D;AAW3D,6EAAuE;AACvE,qEAA4D;AAQ5D,uEAGuC;AAWvC,2CAA8C;AAG9C,4CAGqB;AAErB,wEAAoE;AACpE,wEAAmE;AACnE,kFAA6E;AAC7E,sEAAkE;AAIlE;;;GAGG;AACH,MAAM,4BAA4B,GAAG,OAAO,CAAC;AAEhC,QAAA,cAAc,GAAG,+BAA+B,CAAC;AAyB9D,MAAM,qCAAqC,GAAG;IAC5C,gBAAgB,EAAE;QAChB,sBAAsB,EAAE,KAAK;QAC7B,kBAAkB,EAAE,KAAK;QACzB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,KAAK;KAChB;CAC0D,CAAC;AAE9D;;;;;;;GAOG;AACH,SAAgB,4CAA4C;IAC1D,OAAO;QACL,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC;AAJD,oGAIC;AAED,MAAM,yBAAyB,GAAG,CAAC,gBAAgB,CAAU,CAAC;AAkG9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAa,6BAA8B,SAAQ,gCAIlD;IAgCC;;;;;;;;OAQG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,KAAK,GAKN;QACC,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE,qCAAqC;YAC/C,IAAI,EAAE,sBAAc;YACpB,KAAK,EAAE;gBACL,GAAG,4CAA4C,EAAE;gBACjD,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QAzDL,wDAA2C;QAElC,2DAA4D;QAE5D,4DAEP;QAEO,uEAEP;QAEO,kEAEP;QAEF,qDAAe,KAAK,EAAC;QAErB,2DAA2B;QAE3B,oEAA8C;QAE9C,+DAAyB,KAAK,EAAC;QAEtB,+CAAiB;YACxB,2CAAoB;YACpB,oDAAwB;YACxB,0CAAmB;YACnB,yCAAmB;SACpB,EAAC;QA8BA,uBAAA,IAAI,4CAAc,KAAK,CAAC,SAAS,MAAA,CAAC;QAClC,uBAAA,IAAI,6CAAe,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,IAAsB,EAAE,CAAC,IAAI,CAAC,MAAA,CAAC;QAC5E,uBAAA,IAAI,wDACF,KAAK,CAAC,qBAAqB,IAAI,CAAC,KAAK,IAAmB,EAAE,CAAC,SAAS,CAAC,MAAA,CAAC;QACxE,uBAAA,IAAI,mDAAqB,KAAK,CAAC,gBAAgB,IAAI,CAAC,GAAS,EAAE,CAAC,SAAS,CAAC,MAAA,CAAC;QAE3E,IAAI,CAAC,SAAS,CAAC,4BAA4B,CACzC,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,IAAI;QACF,IAAI,uBAAA,IAAI,kDAAa,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QACD,uBAAA,IAAI,8CAAgB,IAAI,MAAA,CAAC;QAEzB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,0CAA0C,EAAE,GAAG,EAAE,CACxE,IAAI,CAAC,IAAI,EAAE,CACZ,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,gCAAgC,EAAE,GAAG,EAAE,CAC9D,IAAI,CAAC,IAAI,EAAE,CACZ,CAAC;QACF,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,IAAI;QACF,IAAI,CAAC;YACH,MAAM,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAChD,sCAAsC,CACvC,CAAC;YAEF,IAAI,CAAC,uBAAA,IAAI,gDAAW,MAAf,IAAI,EAAY,kBAAkB,CAAC,EAAE,CAAC;gBACzC,iEAAiE;gBACjE,qEAAqE;gBACrE,0BAA0B;gBAC1B,uBAAA,IAAI,yCAAW,SAAS,MAAA,CAAC;gBACzB,uBAAA,IAAI,qDAAuB,SAAS,MAAA,CAAC;gBACrC,OAAO;YACT,CAAC;YAED,IAAI,CAAC,uBAAA,IAAI,8FAAe,MAAnB,IAAI,CAAiB,EAAE,CAAC;gBAC3B,OAAO;YACT,CAAC;YAED,MAAM,WAAW,GAAG,IAAA,gDAA0B,EAAC,kBAAkB,CAAC,CAAC;YACnE,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,uBAAA,IAAI,oGAAqB,MAAzB,IAAI,CAAuB,CAAC;gBAC5B,OAAO;YACT,CAAC;YAED,IACE,uBAAA,IAAI,yDAAoB;gBACxB,IAAA,sDAAgC,EAAC,WAAW,EAAE,uBAAA,IAAI,yDAAoB,CAAC,EACvE,CAAC;gBACD,OAAO;YACT,CAAC;YAED,uBAAA,IAAI,kGAAmB,MAAvB,IAAI,EAAoB,WAAW,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uBAAA,IAAI,uDAAkB,MAAtB,IAAI,EAAmB,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAuJD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,CAAC,cAAc,CAAC,OAAY;QAC/B,IAAI,CAAC,uBAAA,IAAI,6CAAQ,IAAI,uBAAA,IAAI,gDAAW,EAAE,CAAC;YACrC,MAAM,uBAAA,IAAI,gDAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC,uBAAA,IAAI,6CAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,mJAAmJ,CACpJ,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,uBAAA,IAAI,6CAAQ,CAAC;QAE5B,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAS,CAAC;QAChD,MAAM,iBAAiB,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;QAC3D,IACE,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,iBAAiB;YAC1D,iBAAiB,EACjB,CAAC;YACD,OAAO;QACT,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,uBAAA,IAAI,4CAAO,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,GAAG,CAAC;oBACb,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,OAAO;oBACP,GAAG,MAAM;iBACV,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,wCAA4B,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,GAAG;gBACnC,iBAAiB;gBACjB,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;aACxB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAhWD,sEAgWC;;IAxMG,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAClD,4BAA4B,CAC7B,CAAC;IACF,OAAO,CACL,UAAU,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,iCAAY,CAAC,EAAE,CAAC,CAC3E,CAAC;AACJ,CAAC;IAUC,MAAM,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAChD,sCAAsC,CACvC,CAAC;IACF,OAAO,uBAAA,IAAI,gDAAW,MAAf,IAAI,EAAY,kBAAkB,CAAC,IAAI,uBAAA,IAAI,8FAAe,MAAnB,IAAI,CAAiB,CAAC;AACtE,CAAC,+GAEkB,WAAoC;IACrD,uBAAA,IAAI,qDAAuB,WAAW,MAAA,CAAC;IAEvC,MAAM,GAAG,GAAG,KAAK,IAAmB,EAAE;QACpC,uEAAuE;QACvE,uEAAuE;QACvE,gEAAgE;QAChE,gCAAgC;QAChC,IAAI,CAAC,uBAAA,IAAI,6FAAc,MAAlB,IAAI,CAAgB,IAAI,CAAC,CAAC,MAAM,uBAAA,IAAI,iDAAY,MAAhB,IAAI,CAAc,CAAC,EAAE,CAAC;YACzD,uBAAA,IAAI,uFAAQ,MAAZ,IAAI,EAAS,WAAW,CAAC,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,uBAAA,IAAI,4DAAuB,MAA3B,IAAI,EAAwB,WAAW,CAAC,CAAC;QAE/C,uEAAuE;QACvE,kCAAkC;QAClC,IAAI,CAAC,uBAAA,IAAI,6FAAc,MAAlB,IAAI,CAAgB,EAAE,CAAC;YAC1B,uBAAA,IAAI,uFAAQ,MAAZ,IAAI,EAAS,WAAW,CAAC,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,uBAAA,IAAI,iGAAkB,MAAtB,IAAI,EAAmB,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,uBAAA,IAAI,gDAAW;QAC/B,CAAC,CAAC,uBAAA,IAAI,gDAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAClD,CAAC,CAAC,GAAG,EAAE,CAAC;IACV,uBAAA,IAAI,4CAAc,SAAS,MAAA,CAAC;IAE5B,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACxB,uBAAA,IAAI,uDAAkB,MAAtB,IAAI,EAAmB,KAAK,CAAC,CAAC;QAC9B,uBAAA,IAAI,uFAAQ,MAAZ,IAAI,EAAS,WAAW,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC,yFASO,WAAoC;IAC1C,IAAI,uBAAA,IAAI,yDAAoB,KAAK,WAAW,EAAE,CAAC;QAC7C,uBAAA,IAAI,qDAAuB,SAAS,MAAA,CAAC;IACvC,CAAC;AACH,CAAC;IASC,IAAI,CAAC,uBAAA,IAAI,4DAAuB,EAAE,CAAC;QACjC,uBAAA,IAAI,wDAA0B,IAAI,MAAA,CAAC;QACnC,uBAAA,IAAI,uDAAkB,MAAtB,IAAI,EAAmB,IAAI,+CAAmC,EAAE,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,0DAAmB,WAAoC;IAC1D,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,GAAG,WAAW,CAAC;IAEjE,MAAM,SAAS,GACb,4CAAmB,CAAC,4BAA4B,CAAC,CAAC,IAAA,mBAAW,EAAC,OAAO,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,wBAAwB,4BAA4B,6BAA6B,OAAO,EAAE,CAC3F,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACxC,mCAAmC,EACnC,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,SAAS,OAAO,wCAAwC,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC;IACxC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,oCAAoC,OAAO,8BAA8B,CAC1E,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,uDAAuD,OAAO,EAAE,CACjE,CAAC;IACJ,CAAC;IAED,uBAAA,IAAI,yCAAW;QACb,OAAO;QACP,eAAe,EAAE,KAAK,CAAC,mBAAmB;QAC1C,gBAAgB,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,YAAY;QAC9D,kBAAkB;QAClB,uBAAuB,EAAE,YAAY,CAAC,cAAc;QACpD,oBAAoB,EAAE,SAAS,CAAC,6BAA6B;QAC7D,2BAA2B,EAAE,SAAS,CAAC,2BAA2B;QAClE,gBAAgB,EAAE,SAAS,CAAC,gBAAgB;QAC5C,gBAAgB,EAAE,SAAS,CAAC,gBAAgB;KAC7C,MAAA,CAAC;AACJ,CAAC;AA+DH;;;;;;;GAOG;AACH,SAAS,wBAAwB,CAC/B,MAAwC;IAExC,OAAO;QACL,4BAA4B;QAC5B,MAAM,CAAC,OAAO;QACd,MAAM,CAAC,eAAe;QACtB,MAAM,CAAC,gBAAgB;QACvB,MAAM,CAAC,kBAAkB;QACzB,MAAM,CAAC,uBAAuB;QAC9B,MAAM,CAAC,oBAAoB;QAC3B,MAAM,CAAC,2BAA2B;QAClC,MAAM,CAAC,gBAAgB;QACvB,MAAM,CAAC,gBAAgB;KACxB;SACE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;SACnC,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC","sourcesContent":["import type {\n AuthenticatedUserStorageServiceCreateDelegationAction,\n AuthenticatedUserStorageServiceListDelegationsAction,\n} from '@metamask/authenticated-user-storage';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ChompApiServiceAssociateAddressAction,\n ChompApiServiceCreateIntentsAction,\n ChompApiServiceCreateUpgradeAction,\n ChompApiServiceGetAssociatedAddressesAction,\n ChompApiServiceGetIntentsByAddressAction,\n ChompApiServiceGetServiceDetailsAction,\n ChompApiServiceVerifyDelegationAction,\n} from '@metamask/chomp-api-service';\nimport type { DelegationControllerSignDelegationAction } from '@metamask/delegation-controller';\nimport { DELEGATOR_CONTRACTS } from '@metamask/delegation-deployments';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerSignEip7702AuthorizationAction,\n KeyringControllerSignPersonalMessageAction,\n KeyringControllerState,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport {\n areMoneyAccountVaultConfigsEqual,\n getMoneyAccountVaultConfig,\n} from '@metamask/money-account-utils';\nimport type { MoneyAccountVaultConfig } from '@metamask/money-account-utils';\nimport type {\n NetworkControllerFindNetworkClientIdByChainIdAction,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport type {\n FeatureFlags,\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerState,\n} from '@metamask/remote-feature-flag-controller';\nimport { hexToNumber } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n MissingMoneyAccountVaultConfigError,\n MoneyAccountUpgradeStepError,\n} from './errors.js';\nimport type { MoneyAccountUpgradeControllerMethodActions } from './MoneyAccountUpgradeController-method-action-types.js';\nimport { associateAddressStep } from './steps/associate-address.js';\nimport { buildDelegationStep } from './steps/build-delegations.js';\nimport { eip7702AuthorizationStep } from './steps/eip-7702-authorization.js';\nimport { registerIntentsStep } from './steps/register-intents.js';\nimport type { Step } from './steps/step.js';\nimport type { UpgradeConfig } from './types.js';\n\n/**\n * The Delegation Framework deployment version we resolve contract addresses\n * against in `@metamask/delegation-deployments`.\n */\nconst DELEGATION_FRAMEWORK_VERSION = '1.3.0';\n\nexport const controllerName = 'MoneyAccountUpgradeController';\n\n/**\n * Record of a Money Account upgrade sequence that ran to completion.\n */\nexport type MoneyAccountUpgradeStatus = {\n /**\n * Fingerprint of the upgrade config the sequence completed under. The\n * record is only trusted while the active config produces the same\n * fingerprint — if the chain, CHOMP contracts, or Delegation Framework\n * version change, the sequence re-runs.\n */\n configFingerprint: string;\n /** Unix timestamp (in milliseconds) when the sequence completed. */\n completedAt: number;\n};\n\nexport type MoneyAccountUpgradeControllerState = {\n /**\n * Accounts whose upgrade sequence has fully completed, keyed by lowercased\n * account address.\n */\n upgradedAccounts: { [address: Hex]: MoneyAccountUpgradeStatus };\n};\n\nconst moneyAccountUpgradeControllerMetadata = {\n upgradedAccounts: {\n includeInDebugSnapshot: false,\n includeInStateLogs: false,\n persist: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<MoneyAccountUpgradeControllerState>;\n\n/**\n * Constructs the default {@link MoneyAccountUpgradeController} state. This\n * allows consumers to provide a partial state object when initializing the\n * controller and also helps in constructing complete state objects for this\n * controller in tests.\n *\n * @returns The default {@link MoneyAccountUpgradeController} state.\n */\nexport function getDefaultMoneyAccountUpgradeControllerState(): MoneyAccountUpgradeControllerState {\n return {\n upgradedAccounts: {},\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = ['upgradeAccount'] as const;\n\nexport type MoneyAccountUpgradeControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MoneyAccountUpgradeControllerState\n >;\n\nexport type MoneyAccountUpgradeControllerActions =\n | MoneyAccountUpgradeControllerGetStateAction\n | MoneyAccountUpgradeControllerMethodActions;\n\ntype AllowedActions =\n | AuthenticatedUserStorageServiceCreateDelegationAction\n | AuthenticatedUserStorageServiceListDelegationsAction\n | ChompApiServiceAssociateAddressAction\n | ChompApiServiceCreateIntentsAction\n | ChompApiServiceCreateUpgradeAction\n | ChompApiServiceGetAssociatedAddressesAction\n | ChompApiServiceGetIntentsByAddressAction\n | ChompApiServiceGetServiceDetailsAction\n | ChompApiServiceVerifyDelegationAction\n | DelegationControllerSignDelegationAction\n | KeyringControllerGetStateAction\n | KeyringControllerSignEip7702AuthorizationAction\n | KeyringControllerSignPersonalMessageAction\n | NetworkControllerFindNetworkClientIdByChainIdAction\n | NetworkControllerGetNetworkClientByIdAction\n | RemoteFeatureFlagControllerGetStateAction;\n\nexport type MoneyAccountUpgradeControllerStateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n MoneyAccountUpgradeControllerState\n >;\n\nexport type MoneyAccountUpgradeControllerEvents =\n MoneyAccountUpgradeControllerStateChangedEvent;\n\ntype AllowedEvents =\n | ControllerStateChangedEvent<'KeyringController', KeyringControllerState>\n | ControllerStateChangedEvent<\n 'RemoteFeatureFlagController',\n RemoteFeatureFlagControllerState\n >;\n\nexport type MoneyAccountUpgradeControllerMessenger = Messenger<\n typeof controllerName,\n MoneyAccountUpgradeControllerActions | AllowedActions,\n MoneyAccountUpgradeControllerEvents | AllowedEvents\n>;\n\n/**\n * The hooks a client supplies for the parts of the bootstrap that cannot be\n * decided in this package.\n */\nexport type MoneyAccountUpgradeControllerHooks = {\n /**\n * Whether the Money Account feature is enabled for this client. Called with\n * the current remote feature flags on every sync and re-checked across the\n * bootstrap's `await` points; it must re-read any client state it depends\n * on (e.g. a version-gated flag, a \"basic functionality\" toggle) rather\n * than caching it. Returning `false` after a successful bootstrap disarms\n * the controller: `upgradeAccount` refuses to run until a later sync\n * re-bootstraps.\n */\n isEnabled: (remoteFeatureFlags: FeatureFlags) => boolean;\n\n /**\n * An asynchronous client gate checked once per bootstrap run, before any\n * network is added or external service is called — e.g. a fail-closed\n * geolocation check. A run skipped here is forgotten and retried on the\n * next sync trigger. Defaults to always eligible.\n */\n isEligible?: () => Promise<boolean>;\n\n /**\n * Ensure the vault chain exists in the client's NetworkController before\n * the bootstrap validates it. Adding a network is client-specific (featured\n * network lists, enabled-network bookkeeping), so the controller only\n * promises to have awaited this before calling\n * `NetworkController:findNetworkClientIdByChainId` consumers. Defaults to a\n * no-op.\n */\n ensureChainConfigured?: (\n vaultConfig: MoneyAccountVaultConfig,\n ) => Promise<void>;\n\n /**\n * Called when a bootstrap run fails or cannot be scheduled. Receives a\n * {@link MissingMoneyAccountVaultConfigError} (once per controller\n * lifetime) when the enable flag is on but `moneyAccountVaultConfig` is\n * unserved or malformed. The controller never throws out of its\n * subscriptions; this hook is the only failure signal.\n */\n onBootstrapError?: (error: unknown) => void;\n};\n\n/**\n * Controller that owns the Money Account upgrade sequence and its own\n * bootstrap.\n *\n * After {@link MoneyAccountUpgradeController.init} is called (once, after all\n * controllers are constructed), the controller watches\n * `RemoteFeatureFlagController` and `KeyringController` state and bootstraps\n * itself when every gate is open:\n *\n * 1. the client's `isEnabled` hook returns `true` for the current flags,\n * 2. the wallet is unlocked with an HD keyring,\n * 3. the client's `isEligible` hook (if any) resolves `true`, and\n * 4. the `moneyAccountVaultConfig` flag parses.\n *\n * The bootstrap awaits the client's `ensureChainConfigured` hook and then\n * fetches CHOMP service details to arm the upgrade config. Gates 1–2 are\n * re-checked across the `await` points so a lock or an `isEnabled` flip\n * mid-bootstrap cannot still produce an external call; a skipped or failed\n * run is forgotten so the next trigger retries it. The bootstrap re-runs\n * whenever the vault config changes, and `isEnabled` going `false` disarms\n * the controller entirely.\n */\nexport class MoneyAccountUpgradeController extends BaseController<\n typeof controllerName,\n MoneyAccountUpgradeControllerState,\n MoneyAccountUpgradeControllerMessenger\n> {\n #config?: UpgradeConfig & { chainId: Hex };\n\n readonly #isEnabled: MoneyAccountUpgradeControllerHooks['isEnabled'];\n\n readonly #isEligible: NonNullable<\n MoneyAccountUpgradeControllerHooks['isEligible']\n >;\n\n readonly #ensureChainConfigured: NonNullable<\n MoneyAccountUpgradeControllerHooks['ensureChainConfigured']\n >;\n\n readonly #onBootstrapError: NonNullable<\n MoneyAccountUpgradeControllerHooks['onBootstrapError']\n >;\n\n #initialized = false;\n\n #bootstrap?: Promise<void>;\n\n #bootstrappedConfig?: MoneyAccountVaultConfig;\n\n #missingConfigReported = false;\n\n readonly #steps: Step[] = [\n associateAddressStep,\n eip7702AuthorizationStep,\n buildDelegationStep,\n registerIntentsStep,\n ];\n\n /**\n * Constructor for the MoneyAccountUpgradeController.\n *\n * @param options - The options for constructing the controller.\n * @param options.messenger - The messenger to use for inter-controller communication.\n * @param options.state - The initial state, merged with the defaults.\n * @param options.hooks - The client hooks for the bootstrap; see\n * {@link MoneyAccountUpgradeControllerHooks}.\n */\n constructor({\n messenger,\n state,\n hooks,\n }: {\n messenger: MoneyAccountUpgradeControllerMessenger;\n state?: Partial<MoneyAccountUpgradeControllerState>;\n hooks: MoneyAccountUpgradeControllerHooks;\n }) {\n super({\n messenger,\n metadata: moneyAccountUpgradeControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultMoneyAccountUpgradeControllerState(),\n ...state,\n },\n });\n\n this.#isEnabled = hooks.isEnabled;\n this.#isEligible = hooks.isEligible ?? (async (): Promise<boolean> => true);\n this.#ensureChainConfigured =\n hooks.ensureChainConfigured ?? (async (): Promise<void> => undefined);\n this.#onBootstrapError = hooks.onBootstrapError ?? ((): void => undefined);\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Start the controller's bootstrap: subscribe to the feature-flag and\n * keyring triggers and run an initial sync. Call once, after all\n * controllers and services the messenger reaches are constructed — this is\n * the only method that may be called before the controller is bootstrapped,\n * and it is the reason the constructor performs no messenger calls.\n */\n init(): void {\n if (this.#initialized) {\n return;\n }\n this.#initialized = true;\n\n this.messenger.subscribe('RemoteFeatureFlagController:stateChanged', () =>\n this.sync(),\n );\n this.messenger.subscribe('KeyringController:stateChanged', () =>\n this.sync(),\n );\n this.sync();\n }\n\n /**\n * Re-evaluate the bootstrap gates against live state and schedule a\n * bootstrap run when they are open and the vault config is new. Runs on\n * every feature-flag and keyring state change; clients with additional\n * gates read inside their `isEnabled` hook (onboarding, preferences)\n * should call this when those change too. Never throws: a failure is\n * reported through `onBootstrapError` and retried on the next trigger.\n */\n sync(): void {\n try {\n const { remoteFeatureFlags } = this.messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n\n if (!this.#isEnabled(remoteFeatureFlags)) {\n // Disarm so a later `upgradeAccount` cannot run against a config\n // armed while the feature was still enabled. Re-enabling re-runs the\n // bootstrap from scratch.\n this.#config = undefined;\n this.#bootstrappedConfig = undefined;\n return;\n }\n\n if (!this.#isWalletReady()) {\n return;\n }\n\n const vaultConfig = getMoneyAccountVaultConfig(remoteFeatureFlags);\n if (!vaultConfig) {\n this.#reportMissingConfig();\n return;\n }\n\n if (\n this.#bootstrappedConfig &&\n areMoneyAccountVaultConfigsEqual(vaultConfig, this.#bootstrappedConfig)\n ) {\n return;\n }\n\n this.#scheduleBootstrap(vaultConfig);\n } catch (error) {\n this.#onBootstrapError(error);\n }\n }\n\n /**\n * Whether the wallet can sign right now: unlocked with an HD keyring. The\n * keyring list matters because during a vault restore the unlock flips\n * before the keyrings land in state.\n *\n * @returns Whether the wallet is ready.\n */\n #isWalletReady(): boolean {\n const { isUnlocked, keyrings } = this.messenger.call(\n 'KeyringController:getState',\n );\n return (\n isUnlocked && keyrings.some((keyring) => keyring.type === KeyringTypes.hd)\n );\n }\n\n /**\n * Whether the synchronous bootstrap gates are open right now. Re-read from\n * live state on every call because the bootstrap re-validates them across\n * its `await` points.\n *\n * @returns Whether the bootstrap may proceed.\n */\n #areGatesOpen(): boolean {\n const { remoteFeatureFlags } = this.messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n return this.#isEnabled(remoteFeatureFlags) && this.#isWalletReady();\n }\n\n #scheduleBootstrap(vaultConfig: MoneyAccountVaultConfig): void {\n this.#bootstrappedConfig = vaultConfig;\n\n const run = async (): Promise<void> => {\n // The gates were checked when this run was scheduled, but it may start\n // much later, chained behind an in-flight bootstrap. Eligibility comes\n // after the gates so a disabled client also skips the (possibly\n // external) eligibility lookup.\n if (!this.#areGatesOpen() || !(await this.#isEligible())) {\n this.#forget(vaultConfig);\n return;\n }\n\n await this.#ensureChainConfigured(vaultConfig);\n\n // Configuring the chain can suspend for a while, so re-check the gates\n // before the external CHOMP call.\n if (!this.#areGatesOpen()) {\n this.#forget(vaultConfig);\n return;\n }\n\n await this.#applyVaultConfig(vaultConfig);\n };\n\n const bootstrap = this.#bootstrap\n ? this.#bootstrap.catch(() => undefined).then(run)\n : run();\n this.#bootstrap = bootstrap;\n\n bootstrap.catch((error) => {\n this.#onBootstrapError(error);\n this.#forget(vaultConfig);\n });\n }\n\n /**\n * Forget a scheduled bootstrap that was skipped or failed, so the next\n * trigger re-runs it — but only if no newer config has been scheduled\n * meanwhile: a newer config supersedes this run, success or failure.\n *\n * @param vaultConfig - The config the abandoned run was scheduled with.\n */\n #forget(vaultConfig: MoneyAccountVaultConfig): void {\n if (this.#bootstrappedConfig === vaultConfig) {\n this.#bootstrappedConfig = undefined;\n }\n }\n\n /**\n * Report a served enable flag without a usable `moneyAccountVaultConfig` —\n * a flag misconfiguration that silently disables upgrades. Reported once\n * per controller lifetime; flag refreshes arrive continuously and would\n * otherwise spam.\n */\n #reportMissingConfig(): void {\n if (!this.#missingConfigReported) {\n this.#missingConfigReported = true;\n this.#onBootstrapError(new MissingMoneyAccountVaultConfigError());\n }\n }\n\n /**\n * Fetches service details and validates the controller can operate on the\n * vault's chain, arming the upgrade config `upgradeAccount` runs against.\n * Resolves the Delegation Framework contract addresses for the chain from\n * `@metamask/delegation-deployments`.\n *\n * @param vaultConfig - The vault config to arm; its `boringVault` is the\n * withdrawal-side delegation token (vmUSD), supplied via the flag until\n * the CHOMP service-details API exposes it.\n */\n async #applyVaultConfig(vaultConfig: MoneyAccountVaultConfig): Promise<void> {\n const { chainId, boringVault: boringVaultAddress } = vaultConfig;\n\n const contracts =\n DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION][hexToNumber(chainId)];\n if (!contracts) {\n throw new Error(\n `Delegation Framework ${DELEGATION_FRAMEWORK_VERSION} is not deployed on chain ${chainId}`,\n );\n }\n\n const response = await this.messenger.call(\n 'ChompApiService:getServiceDetails',\n [chainId],\n );\n\n const chain = response.chains[chainId];\n if (!chain) {\n throw new Error(`Chain ${chainId} not found in service details response`);\n }\n\n const { vedaProtocol } = chain.protocol;\n if (!vedaProtocol) {\n throw new Error(\n `vedaProtocol not found for chain ${chainId} in service details response`,\n );\n }\n\n if (vedaProtocol.supportedTokens.length === 0) {\n throw new Error(\n `No supported tokens found for vedaProtocol on chain ${chainId}`,\n );\n }\n\n this.#config = {\n chainId,\n delegateAddress: chain.autoDepositDelegate,\n musdTokenAddress: vedaProtocol.supportedTokens[0].tokenAddress,\n boringVaultAddress,\n vedaVaultAdapterAddress: vedaProtocol.adapterAddress,\n delegatorImplAddress: contracts.EIP7702StatelessDeleGatorImpl,\n erc20TransferAmountEnforcer: contracts.ERC20TransferAmountEnforcer,\n redeemerEnforcer: contracts.RedeemerEnforcer,\n valueLteEnforcer: contracts.ValueLteEnforcer,\n };\n }\n\n /**\n * Runs each step in the upgrade sequence in order. A step that reports\n * `'already-done'` is skipped without performing any action; a step that\n * reports `'completed'` has performed its action. An error thrown by any\n * step halts the sequence and is re-thrown wrapped in a\n * {@link MoneyAccountUpgradeStepError} that records which step failed (the\n * original error is preserved as `cause`).\n *\n * A run that completes is recorded in state (keyed by lowercased address,\n * fingerprinted against the active config); subsequent calls for a\n * recorded account return immediately without running any steps. If the\n * active config no longer matches the recorded fingerprint, the sequence\n * re-runs.\n *\n * A call that arrives while the bootstrap is still in flight waits for it\n * rather than failing; it only throws when no bootstrap has armed a config\n * (feature disabled, wallet locked, or the last bootstrap failed).\n *\n * @param address - The Money Account address to upgrade.\n */\n async upgradeAccount(address: Hex): Promise<void> {\n if (!this.#config && this.#bootstrap) {\n await this.#bootstrap.catch(() => undefined);\n }\n if (!this.#config) {\n throw new Error(\n 'MoneyAccountUpgradeController is not bootstrapped: upgradeAccount() requires the feature flag on, the wallet unlocked, and a successful bootstrap',\n );\n }\n const config = this.#config;\n\n const accountKey = address.toLowerCase() as Hex;\n const configFingerprint = computeConfigFingerprint(config);\n if (\n this.state.upgradedAccounts[accountKey]?.configFingerprint ===\n configFingerprint\n ) {\n return;\n }\n\n for (const step of this.#steps) {\n try {\n await step.run({\n messenger: this.messenger,\n address,\n ...config,\n });\n } catch (error) {\n throw new MoneyAccountUpgradeStepError(step.name, error);\n }\n }\n\n this.update((state) => {\n state.upgradedAccounts[accountKey] = {\n configFingerprint,\n completedAt: Date.now(),\n };\n });\n }\n}\n\n/**\n * Derives a stable fingerprint of the config fields that define what\n * \"upgraded\" means for an account. A recorded upgrade is only trusted while\n * the active config produces the same fingerprint.\n *\n * @param config - The active upgrade config.\n * @returns A canonical string over the config's identifying fields.\n */\nfunction computeConfigFingerprint(\n config: UpgradeConfig & { chainId: Hex },\n): string {\n return [\n DELEGATION_FRAMEWORK_VERSION,\n config.chainId,\n config.delegateAddress,\n config.musdTokenAddress,\n config.boringVaultAddress,\n config.vedaVaultAdapterAddress,\n config.delegatorImplAddress,\n config.erc20TransferAmountEnforcer,\n config.redeemerEnforcer,\n config.valueLteEnforcer,\n ]\n .map((value) => value.toLowerCase())\n .join('|');\n}\n"]}
1
+ {"version":3,"file":"MoneyAccountUpgradeController.cjs","sourceRoot":"","sources":["../src/MoneyAccountUpgradeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AuthenticatedUserStorageServiceCreateDelegationAction,\n AuthenticatedUserStorageServiceListDelegationsAction,\n} from '@metamask/authenticated-user-storage';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ChompApiServiceAssociateAddressAction,\n ChompApiServiceCreateIntentsAction,\n ChompApiServiceCreateUpgradeAction,\n ChompApiServiceGetAssociatedAddressesAction,\n ChompApiServiceGetIntentsByAddressAction,\n ChompApiServiceGetServiceDetailsAction,\n ChompApiServiceVerifyDelegationAction,\n} from '@metamask/chomp-api-service';\nimport type { DelegationControllerSignDelegationAction } from '@metamask/delegation-controller';\nimport { DELEGATOR_CONTRACTS } from '@metamask/delegation-deployments';\nimport type {\n KeyringControllerSignEip7702AuthorizationAction,\n KeyringControllerSignPersonalMessageAction,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerFindNetworkClientIdByChainIdAction,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { hexToNumber } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport { MoneyAccountUpgradeStepError } from './errors.js';\nimport type { MoneyAccountUpgradeControllerMethodActions } from './MoneyAccountUpgradeController-method-action-types.js';\nimport { associateAddressStep } from './steps/associate-address.js';\nimport { buildDelegationStep } from './steps/build-delegations.js';\nimport { eip7702AuthorizationStep } from './steps/eip-7702-authorization.js';\nimport { registerIntentsStep } from './steps/register-intents.js';\nimport type { Step } from './steps/step.js';\nimport type { UpgradeConfig } from './types.js';\n\n/**\n * The Delegation Framework deployment version we resolve contract addresses\n * against in `@metamask/delegation-deployments`.\n */\nconst DELEGATION_FRAMEWORK_VERSION = '1.3.0';\n\nexport const controllerName = 'MoneyAccountUpgradeController';\n\n/**\n * Record of a Money Account upgrade sequence that ran to completion.\n */\nexport type MoneyAccountUpgradeStatus = {\n /**\n * Fingerprint of the upgrade config the sequence completed under. The\n * record is only trusted while the active config produces the same\n * fingerprint — if the chain, CHOMP contracts, or Delegation Framework\n * version change, the sequence re-runs.\n */\n configFingerprint: string;\n /** Unix timestamp (in milliseconds) when the sequence completed. */\n completedAt: number;\n};\n\nexport type MoneyAccountUpgradeControllerState = {\n /**\n * Accounts whose upgrade sequence has fully completed, keyed by lowercased\n * account address.\n */\n upgradedAccounts: { [address: Hex]: MoneyAccountUpgradeStatus };\n};\n\nconst moneyAccountUpgradeControllerMetadata = {\n upgradedAccounts: {\n includeInDebugSnapshot: false,\n includeInStateLogs: false,\n persist: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<MoneyAccountUpgradeControllerState>;\n\n/**\n * Constructs the default {@link MoneyAccountUpgradeController} state. This\n * allows consumers to provide a partial state object when initializing the\n * controller and also helps in constructing complete state objects for this\n * controller in tests.\n *\n * @returns The default {@link MoneyAccountUpgradeController} state.\n */\nexport function getDefaultMoneyAccountUpgradeControllerState(): MoneyAccountUpgradeControllerState {\n return {\n upgradedAccounts: {},\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = ['upgradeAccount'] as const;\n\nexport type MoneyAccountUpgradeControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MoneyAccountUpgradeControllerState\n >;\n\nexport type MoneyAccountUpgradeControllerActions =\n | MoneyAccountUpgradeControllerGetStateAction\n | MoneyAccountUpgradeControllerMethodActions;\n\ntype AllowedActions =\n | AuthenticatedUserStorageServiceCreateDelegationAction\n | AuthenticatedUserStorageServiceListDelegationsAction\n | ChompApiServiceAssociateAddressAction\n | ChompApiServiceCreateIntentsAction\n | ChompApiServiceCreateUpgradeAction\n | ChompApiServiceGetAssociatedAddressesAction\n | ChompApiServiceGetIntentsByAddressAction\n | ChompApiServiceGetServiceDetailsAction\n | ChompApiServiceVerifyDelegationAction\n | DelegationControllerSignDelegationAction\n | KeyringControllerSignEip7702AuthorizationAction\n | KeyringControllerSignPersonalMessageAction\n | NetworkControllerFindNetworkClientIdByChainIdAction\n | NetworkControllerGetNetworkClientByIdAction;\n\nexport type MoneyAccountUpgradeControllerStateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n MoneyAccountUpgradeControllerState\n >;\n\nexport type MoneyAccountUpgradeControllerEvents =\n MoneyAccountUpgradeControllerStateChangedEvent;\n\ntype AllowedEvents = never;\n\nexport type MoneyAccountUpgradeControllerMessenger = Messenger<\n typeof controllerName,\n MoneyAccountUpgradeControllerActions | AllowedActions,\n MoneyAccountUpgradeControllerEvents | AllowedEvents\n>;\n\n/**\n * Controller that orchestrates the Money Account upgrade sequence.\n */\nexport class MoneyAccountUpgradeController extends BaseController<\n typeof controllerName,\n MoneyAccountUpgradeControllerState,\n MoneyAccountUpgradeControllerMessenger\n> {\n #config?: UpgradeConfig & { chainId: Hex };\n\n readonly #steps: Step[] = [\n associateAddressStep,\n eip7702AuthorizationStep,\n buildDelegationStep,\n registerIntentsStep,\n ];\n\n /**\n * Constructor for the MoneyAccountUpgradeController.\n *\n * @param options - The options for constructing the controller.\n * @param options.messenger - The messenger to use for inter-controller communication.\n * @param options.state - The initial state, merged with the defaults.\n */\n constructor({\n messenger,\n state,\n }: {\n messenger: MoneyAccountUpgradeControllerMessenger;\n state?: Partial<MoneyAccountUpgradeControllerState>;\n }) {\n super({\n messenger,\n metadata: moneyAccountUpgradeControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultMoneyAccountUpgradeControllerState(),\n ...state,\n },\n });\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Fetches service details and validates the controller can operate on the\n * given chain. Resolves the Delegation Framework contract addresses for the\n * chain from `@metamask/delegation-deployments`.\n *\n * @param params - The parameters for initialization.\n * @param params.chainId - The chain to initialize for.\n * @param params.boringVaultAddress - The Veda boring vault contract\n * (vmUSD) for the given chain. Used as the withdrawal-side delegation\n * token. Supplied by the consumer until the CHOMP service-details API\n * exposes it.\n */\n async init({\n chainId,\n boringVaultAddress,\n }: {\n chainId: Hex;\n boringVaultAddress: Hex;\n }): Promise<void> {\n const contracts =\n DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION][hexToNumber(chainId)];\n if (!contracts) {\n throw new Error(\n `Delegation Framework ${DELEGATION_FRAMEWORK_VERSION} is not deployed on chain ${chainId}`,\n );\n }\n\n const response = await this.messenger.call(\n 'ChompApiService:getServiceDetails',\n [chainId],\n );\n\n const chain = response.chains[chainId];\n if (!chain) {\n throw new Error(`Chain ${chainId} not found in service details response`);\n }\n\n const { vedaProtocol } = chain.protocol;\n if (!vedaProtocol) {\n throw new Error(\n `vedaProtocol not found for chain ${chainId} in service details response`,\n );\n }\n\n if (vedaProtocol.supportedTokens.length === 0) {\n throw new Error(\n `No supported tokens found for vedaProtocol on chain ${chainId}`,\n );\n }\n\n this.#config = {\n chainId,\n delegateAddress: chain.autoDepositDelegate,\n musdTokenAddress: vedaProtocol.supportedTokens[0].tokenAddress,\n boringVaultAddress,\n vedaVaultAdapterAddress: vedaProtocol.adapterAddress,\n delegatorImplAddress: contracts.EIP7702StatelessDeleGatorImpl,\n erc20TransferAmountEnforcer: contracts.ERC20TransferAmountEnforcer,\n redeemerEnforcer: contracts.RedeemerEnforcer,\n valueLteEnforcer: contracts.ValueLteEnforcer,\n };\n }\n\n /**\n * Runs each step in the upgrade sequence in order. A step that reports\n * `'already-done'` is skipped without performing any action; a step that\n * reports `'completed'` has performed its action. An error thrown by any\n * step halts the sequence and is re-thrown wrapped in a\n * {@link MoneyAccountUpgradeStepError} that records which step failed (the\n * original error is preserved as `cause`).\n *\n * A run that completes is recorded in state (keyed by lowercased address,\n * fingerprinted against the active config); subsequent calls for a\n * recorded account return immediately without running any steps. If the\n * active config no longer matches the recorded fingerprint, the sequence\n * re-runs.\n *\n * @param address - The Money Account address to upgrade.\n */\n async upgradeAccount(address: Hex): Promise<void> {\n if (!this.#config) {\n throw new Error(\n 'MoneyAccountUpgradeController must be initialized via init() before upgradeAccount() can be called',\n );\n }\n const config = this.#config;\n\n const accountKey = address.toLowerCase() as Hex;\n const configFingerprint = computeConfigFingerprint(config);\n if (\n this.state.upgradedAccounts[accountKey]?.configFingerprint ===\n configFingerprint\n ) {\n return;\n }\n\n for (const step of this.#steps) {\n try {\n await step.run({\n messenger: this.messenger,\n address,\n ...config,\n });\n } catch (error) {\n throw new MoneyAccountUpgradeStepError(step.name, error);\n }\n }\n\n this.update((state) => {\n state.upgradedAccounts[accountKey] = {\n configFingerprint,\n completedAt: Date.now(),\n };\n });\n }\n}\n\n/**\n * Derives a stable fingerprint of the config fields that define what\n * \"upgraded\" means for an account. A recorded upgrade is only trusted while\n * the active config produces the same fingerprint.\n *\n * @param config - The active upgrade config.\n * @returns A canonical string over the config's identifying fields.\n */\nfunction computeConfigFingerprint(\n config: UpgradeConfig & { chainId: Hex },\n): string {\n return [\n DELEGATION_FRAMEWORK_VERSION,\n config.chainId,\n config.delegateAddress,\n config.musdTokenAddress,\n config.boringVaultAddress,\n config.vedaVaultAdapterAddress,\n config.delegatorImplAddress,\n config.erc20TransferAmountEnforcer,\n config.redeemerEnforcer,\n config.valueLteEnforcer,\n ]\n .map((value) => value.toLowerCase())\n .join('|');\n}\n"]}