@metamask-previews/assets-controllers 109.2.2-preview-574b60e35 → 109.2.2-preview-6b1221978

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 (40) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/MultichainAssetsController/MultichainAssetsController.cjs +23 -6
  3. package/dist/MultichainAssetsController/MultichainAssetsController.cjs.map +1 -1
  4. package/dist/MultichainAssetsController/MultichainAssetsController.d.cts +16 -1
  5. package/dist/MultichainAssetsController/MultichainAssetsController.d.cts.map +1 -1
  6. package/dist/MultichainAssetsController/MultichainAssetsController.d.mts +16 -1
  7. package/dist/MultichainAssetsController/MultichainAssetsController.d.mts.map +1 -1
  8. package/dist/MultichainAssetsController/MultichainAssetsController.mjs +23 -6
  9. package/dist/MultichainAssetsController/MultichainAssetsController.mjs.map +1 -1
  10. package/dist/MultichainBalancesController/MultichainBalancesController.cjs +62 -16
  11. package/dist/MultichainBalancesController/MultichainBalancesController.cjs.map +1 -1
  12. package/dist/MultichainBalancesController/MultichainBalancesController.d.cts +3 -5
  13. package/dist/MultichainBalancesController/MultichainBalancesController.d.cts.map +1 -1
  14. package/dist/MultichainBalancesController/MultichainBalancesController.d.mts +3 -5
  15. package/dist/MultichainBalancesController/MultichainBalancesController.d.mts.map +1 -1
  16. package/dist/MultichainBalancesController/MultichainBalancesController.mjs +62 -16
  17. package/dist/MultichainBalancesController/MultichainBalancesController.mjs.map +1 -1
  18. package/dist/MultichainBalancesController/account-asset-info.cjs +118 -0
  19. package/dist/MultichainBalancesController/account-asset-info.cjs.map +1 -0
  20. package/dist/MultichainBalancesController/account-asset-info.d.cts +82 -0
  21. package/dist/MultichainBalancesController/account-asset-info.d.cts.map +1 -0
  22. package/dist/MultichainBalancesController/account-asset-info.d.mts +82 -0
  23. package/dist/MultichainBalancesController/account-asset-info.d.mts.map +1 -0
  24. package/dist/MultichainBalancesController/account-asset-info.mjs +110 -0
  25. package/dist/MultichainBalancesController/account-asset-info.mjs.map +1 -0
  26. package/dist/MultichainBalancesController/index.cjs.map +1 -1
  27. package/dist/MultichainBalancesController/index.d.cts +1 -1
  28. package/dist/MultichainBalancesController/index.d.cts.map +1 -1
  29. package/dist/MultichainBalancesController/index.d.mts +1 -1
  30. package/dist/MultichainBalancesController/index.d.mts.map +1 -1
  31. package/dist/MultichainBalancesController/index.mjs.map +1 -1
  32. package/dist/selectors/token-selectors.cjs +1 -0
  33. package/dist/selectors/token-selectors.cjs.map +1 -1
  34. package/dist/selectors/token-selectors.d.cts +10 -25
  35. package/dist/selectors/token-selectors.d.cts.map +1 -1
  36. package/dist/selectors/token-selectors.d.mts +10 -25
  37. package/dist/selectors/token-selectors.d.mts.map +1 -1
  38. package/dist/selectors/token-selectors.mjs +1 -0
  39. package/dist/selectors/token-selectors.mjs.map +1 -1
  40. package/package.json +1 -1
@@ -9,11 +9,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  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");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _MultichainBalancesController_instances, _MultichainBalancesController_isDeprecated, _MultichainBalancesController_enforceDisabledState, _MultichainBalancesController_handleOnAccountAssetListUpdated, _MultichainBalancesController_updateBalance, _MultichainBalancesController_listMultichainAccounts, _MultichainBalancesController_listAccounts, _MultichainBalancesController_listAccountAssets, _MultichainBalancesController_getAccount, _MultichainBalancesController_isNonEvmAccount, _MultichainBalancesController_handleOnAccountBalancesUpdated, _MultichainBalancesController_handleOnAccountRemoved, _MultichainBalancesController_getBalances, _MultichainBalancesController_getClient;
12
+ var _MultichainBalancesController_instances, _MultichainBalancesController_isDeprecated, _MultichainBalancesController_enforceDisabledState, _MultichainBalancesController_handleOnAccountAssetListUpdated, _MultichainBalancesController_updateBalance, _MultichainBalancesController_listMultichainAccounts, _MultichainBalancesController_listAccounts, _MultichainBalancesController_listAccountAssets, _MultichainBalancesController_getAccount, _MultichainBalancesController_isNonEvmAccount, _MultichainBalancesController_handleOnAccountBalancesUpdated, _MultichainBalancesController_fetchAccountAssetInfo, _MultichainBalancesController_handleOnAccountRemoved, _MultichainBalancesController_getBalances, _MultichainBalancesController_getClient;
13
13
  import { BaseController } from "@metamask/base-controller";
14
14
  import { isEvmAccountType } from "@metamask/keyring-api";
15
15
  import { KeyringClient } from "@metamask/keyring-snap-client";
16
16
  import { HandlerType } from "@metamask/snaps-utils";
17
+ import { buildBalanceRowsWithAccountAssetInfo, fetchAccountAssetInfoFromSnap, filterAssetsForAccountAssetEnrichment } from "./account-asset-info.mjs";
17
18
  const controllerName = 'MultichainBalancesController';
18
19
  /**
19
20
  * Constructs the default {@link MultichainBalancesController} state. This allows
@@ -88,10 +89,11 @@ export class MultichainBalancesController extends BaseController {
88
89
  this.messenger.subscribe('MultichainAssetsController:accountAssetListUpdated',
89
90
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
90
91
  async ({ assets }) => {
91
- const updatedAccountAssets = Object.entries(assets).map(([accountId, { added, removed }]) => ({
92
+ const updatedAccountAssets = Object.entries(assets).map(([accountId, { added, removed, refreshed }]) => ({
92
93
  accountId,
93
94
  added: [...added],
94
95
  removed: [...removed],
96
+ refreshed: refreshed ? [...refreshed] : [],
95
97
  }));
96
98
  await __classPrivateFieldGet(this, _MultichainBalancesController_instances, "m", _MultichainBalancesController_handleOnAccountAssetListUpdated).call(this, updatedAccountAssets);
97
99
  });
@@ -119,7 +121,7 @@ _MultichainBalancesController_isDeprecated = new WeakMap(), _MultichainBalancesC
119
121
  *
120
122
  * The event payload is treated as a delta:
121
123
  * - balances for `removed` assets are deleted so stale entries cannot remain
122
- * - balances for `added` assets are fetched from the snap and merged in
124
+ * - balances for `added` and `refreshed` assets are fetched from the snap and merged in
123
125
  * - if an added asset is not returned by the snap, a zero placeholder is stored
124
126
  * so the asset can still be represented in state
125
127
  *
@@ -135,21 +137,27 @@ async function _MultichainBalancesController_handleOnAccountAssetListUpdated(acc
135
137
  return;
136
138
  }
137
139
  const balancesToAdd = {};
138
- for (const { accountId, added } of accounts) {
139
- if (added.length === 0) {
140
+ for (const { accountId, added, refreshed } of accounts) {
141
+ const assetsToFetch = [...added, ...refreshed];
142
+ if (assetsToFetch.length === 0) {
140
143
  continue;
141
144
  }
142
145
  const account = __classPrivateFieldGet(this, _MultichainBalancesController_instances, "m", _MultichainBalancesController_getAccount).call(this, accountId);
143
- if (account.metadata.snap) {
144
- const accountBalance = await __classPrivateFieldGet(this, _MultichainBalancesController_instances, "m", _MultichainBalancesController_getBalances).call(this, account.id, account.metadata.snap.id, added);
145
- balancesToAdd[accountId] = accountBalance;
146
+ const snapId = account.metadata.snap?.id;
147
+ if (!snapId) {
148
+ continue;
146
149
  }
150
+ const accountBalance = await __classPrivateFieldGet(this, _MultichainBalancesController_instances, "m", _MultichainBalancesController_getBalances).call(this, account.id, snapId, assetsToFetch);
151
+ const chainId = account.scopes[0];
152
+ const enrichment = chainId
153
+ ? await __classPrivateFieldGet(this, _MultichainBalancesController_instances, "m", _MultichainBalancesController_fetchAccountAssetInfo).call(this, accountId, chainId, snapId, assetsToFetch).catch(() => undefined)
154
+ : undefined;
155
+ balancesToAdd[accountId] = buildBalanceRowsWithAccountAssetInfo(assetsToFetch, accountBalance, enrichment);
147
156
  }
148
157
  this.update((state) => {
149
158
  for (const { accountId, added, removed } of accounts) {
150
- const accountBalances = state.balances[accountId] ?? {};
151
159
  const addedBalances = balancesToAdd[accountId] ?? {};
152
- state.balances[accountId] = accountBalances;
160
+ state.balances[accountId] = state.balances[accountId] ?? {};
153
161
  // Remove balances for assets that disappeared from the account asset list
154
162
  // so stale entries cannot remain in state.
155
163
  for (const assetId of removed) {
@@ -157,7 +165,13 @@ async function _MultichainBalancesController_handleOnAccountAssetListUpdated(acc
157
165
  }
158
166
  // Merge the balances returned by the snap for the newly added assets.
159
167
  for (const [assetId, balance] of Object.entries(addedBalances)) {
160
- state.balances[accountId][assetId] = balance;
168
+ state.balances[accountId][assetId] = {
169
+ ...(state.balances[accountId][assetId] ?? {
170
+ amount: '0',
171
+ unit: '',
172
+ }),
173
+ ...balance,
174
+ };
161
175
  }
162
176
  // If the asset list was updated but the snap did not return a balance for
163
177
  // one of the added assets, keep the asset visible with an explicit zero.
@@ -190,7 +204,7 @@ async function _MultichainBalancesController_updateBalance(accountId, assets) {
190
204
  if (account.metadata.snap) {
191
205
  const accountBalance = await __classPrivateFieldGet(this, _MultichainBalancesController_instances, "m", _MultichainBalancesController_getBalances).call(this, account.id, account.metadata.snap.id, assets);
192
206
  this.update((state) => {
193
- state.balances[accountId] = accountBalance;
207
+ state.balances[accountId] = mergeAccountBalances(state.balances[accountId] ?? {}, accountBalance);
194
208
  });
195
209
  }
196
210
  }
@@ -225,11 +239,33 @@ async function _MultichainBalancesController_updateBalance(accountId, assets) {
225
239
  return;
226
240
  }
227
241
  this.update((state) => {
228
- Object.entries(balanceUpdate.balances).forEach(([accountId, assetBalances]) => {
229
- if (accountId in state.balances) {
230
- Object.assign(state.balances[accountId], assetBalances);
242
+ for (const [accountId, assetBalances] of Object.entries(balanceUpdate.balances)) {
243
+ if (!(accountId in state.balances)) {
244
+ continue;
231
245
  }
232
- });
246
+ state.balances[accountId] = mergeAccountBalances(state.balances[accountId], assetBalances);
247
+ }
248
+ });
249
+ }, _MultichainBalancesController_fetchAccountAssetInfo =
250
+ /**
251
+ * Fetches snap account-asset enrichment for the given assets.
252
+ *
253
+ * @param accountId - Account id.
254
+ * @param chainId - CAIP-2 chain id for enrichment.
255
+ * @param snapId - Wallet snap id.
256
+ * @param assetIds - Assets to enrich.
257
+ * @returns Per-asset enrichment fields, or undefined when unavailable.
258
+ */
259
+ async function _MultichainBalancesController_fetchAccountAssetInfo(accountId, chainId, snapId, assetIds) {
260
+ const enrichmentAssets = filterAssetsForAccountAssetEnrichment(assetIds, chainId);
261
+ if (enrichmentAssets.length === 0) {
262
+ return undefined;
263
+ }
264
+ return await fetchAccountAssetInfoFromSnap((params) => this.messenger.call('SnapController:handleRequest', params), {
265
+ accountId,
266
+ snapId,
267
+ chainId,
268
+ assets: enrichmentAssets,
233
269
  });
234
270
  }, _MultichainBalancesController_handleOnAccountRemoved =
235
271
  /**
@@ -268,4 +304,14 @@ async function _MultichainBalancesController_getBalances(accountId, snapId, asse
268
304
  })),
269
305
  });
270
306
  };
307
+ function mergeAccountBalances(previous, incoming) {
308
+ const merged = { ...previous };
309
+ for (const [assetId, balance] of Object.entries(incoming)) {
310
+ merged[assetId] = {
311
+ ...(merged[assetId] ?? { amount: '0', unit: '' }),
312
+ ...balance,
313
+ };
314
+ }
315
+ return merged;
316
+ }
271
317
  //# sourceMappingURL=MultichainBalancesController.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"MultichainBalancesController.mjs","sourceRoot":"","sources":["../../src/MultichainBalancesController/MultichainBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerAccountBalancesUpdatesEvent,\n} from '@metamask/accounts-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n StateMetadata,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n Balance,\n CaipAssetType,\n AccountBalancesUpdatedEventPayload,\n} from '@metamask/keyring-api';\nimport type { KeyringControllerGetStateAction } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\nimport type { Draft } from 'immer';\n\nimport type {\n MultichainAssetsControllerGetStateAction,\n MultichainAssetsControllerAccountAssetListUpdatedEvent,\n} from '../MultichainAssetsController';\n\nconst controllerName = 'MultichainBalancesController';\n\n/**\n * State used by the {@link MultichainBalancesController} to cache account balances.\n */\nexport type MultichainBalancesControllerState = {\n balances: {\n [account: string]: {\n [asset: string]: {\n amount: string;\n unit: string;\n };\n };\n };\n};\n\n/**\n * Constructs the default {@link MultichainBalancesController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainBalancesController} state.\n */\nexport function getDefaultMultichainBalancesControllerState(): MultichainBalancesControllerState {\n return { balances: {} };\n}\n\n/**\n * Returns the state of the {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MultichainBalancesControllerState\n >;\n\n/**\n * Event emitted when the state of the {@link MultichainBalancesController} changes.\n */\nexport type MultichainBalancesControllerStateChange =\n ControllerStateChangeEvent<\n typeof controllerName,\n MultichainBalancesControllerState\n >;\n\n/**\n * Actions exposed by the {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerActions =\n MultichainBalancesControllerGetStateAction;\n\n/**\n * Events emitted by {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerEvents =\n MultichainBalancesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\ntype AllowedActions =\n | SnapControllerHandleRequestAction\n | AccountsControllerListMultichainAccountsAction\n | MultichainAssetsControllerGetStateAction\n | KeyringControllerGetStateAction;\n\n/**\n * Events that this controller is allowed to subscribe.\n */\ntype AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | AccountsControllerAccountBalancesUpdatesEvent\n | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainBalancesController.\n */\nexport type MultichainBalancesControllerMessenger = Messenger<\n typeof controllerName,\n MultichainBalancesControllerActions | AllowedActions,\n MultichainBalancesControllerEvents | AllowedEvents\n>;\n\n/**\n * {@link MultichainBalancesController}'s metadata.\n *\n * This allows us to choose if fields of the state should be persisted or not\n * using the `persist` flag; and if they can be sent to Sentry or not, using\n * the `anonymous` flag.\n */\nconst balancesControllerMetadata: StateMetadata<MultichainBalancesControllerState> =\n {\n balances: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n };\n\n/**\n * The MultichainBalancesController is responsible for fetching and caching account\n * balances.\n */\nexport class MultichainBalancesController extends BaseController<\n typeof controllerName,\n MultichainBalancesControllerState,\n MultichainBalancesControllerMessenger\n> {\n readonly #isDeprecated: () => boolean;\n\n /**\n * Creates a MultichainBalancesController instance.\n *\n * @param options - Constructor options.\n * @param options.messenger - A reference to the messenger.\n * @param options.state - The initial state.\n * @param options.isDeprecated - Optional function that returns true to completely\n * disable this controller (no requests, no state updates). When it returns\n * `true`, `balances` is reset to `{}` at construction and at every entry point,\n * so no stale balances remain in state. The function is evaluated dynamically\n * on each entry point so it can be toggled at runtime. Intended for use when\n * a higher-level controller (e.g. AssetsController) supersedes this one.\n */\n constructor({\n messenger,\n state = {},\n isDeprecated = (): boolean => false,\n }: {\n messenger: MultichainBalancesControllerMessenger;\n state?: Partial<MultichainBalancesControllerState>;\n isDeprecated?: () => boolean;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: balancesControllerMetadata,\n state: {\n ...getDefaultMultichainBalancesControllerState(),\n ...state,\n },\n });\n\n this.#isDeprecated = isDeprecated;\n\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n } else {\n // Fetch initial balances for all non-EVM accounts\n for (const account of this.#listAccounts()) {\n // Fetching the balance is asynchronous and we cannot use `await` here.\n // eslint-disable-next-line no-void\n void this.updateBalance(account.id);\n }\n }\n\n this.messenger.subscribe(\n 'AccountsController:accountRemoved',\n (account: string) => this.#handleOnAccountRemoved(account),\n );\n this.messenger.subscribe(\n 'AccountsController:accountBalancesUpdated',\n (balanceUpdate: AccountBalancesUpdatedEventPayload) =>\n this.#handleOnAccountBalancesUpdated(balanceUpdate),\n );\n\n this.messenger.subscribe(\n 'MultichainAssetsController:accountAssetListUpdated',\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async ({ assets }) => {\n const updatedAccountAssets = Object.entries(assets).map(\n ([accountId, { added, removed }]) => ({\n accountId,\n added: [...added],\n removed: [...removed],\n }),\n );\n\n await this.#handleOnAccountAssetListUpdated(updatedAccountAssets);\n },\n );\n }\n\n /**\n * Clears all persisted `balances` so that no stale balances remain in state.\n *\n * Called from every entry point when `isDeprecated()` is true so that a\n * runtime toggle propagates to state immediately, even if the controller was\n * originally constructed while it was enabled. The update is skipped when\n * `balances` is already empty to avoid emitting redundant state changes.\n */\n #enforceDisabledState(): void {\n if (Object.keys(this.state.balances).length === 0) {\n return;\n }\n this.update((state) => {\n state.balances = {};\n });\n }\n\n /**\n * Reconciles cached balances after a multichain asset-list update event.\n *\n * The event payload is treated as a delta:\n * - balances for `removed` assets are deleted so stale entries cannot remain\n * - balances for `added` assets are fetched from the snap and merged in\n * - if an added asset is not returned by the snap, a zero placeholder is stored\n * so the asset can still be represented in state\n *\n * @param accounts - The per-account asset deltas from the asset-list update event.\n */\n async #handleOnAccountAssetListUpdated(\n accounts: {\n accountId: string;\n added: CaipAssetType[];\n removed: CaipAssetType[];\n }[],\n ): Promise<void> {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n const { isUnlocked } = this.messenger.call('KeyringController:getState');\n\n if (!isUnlocked) {\n return;\n }\n const balancesToAdd: MultichainBalancesControllerState['balances'] = {};\n\n for (const { accountId, added } of accounts) {\n if (added.length === 0) {\n continue;\n }\n\n const account = this.#getAccount(accountId);\n if (account.metadata.snap) {\n const accountBalance = await this.#getBalances(\n account.id,\n account.metadata.snap.id,\n added,\n );\n\n balancesToAdd[accountId] = accountBalance;\n }\n }\n\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n for (const { accountId, added, removed } of accounts) {\n const accountBalances = state.balances[accountId] ?? {};\n const addedBalances = balancesToAdd[accountId] ?? {};\n\n state.balances[accountId] = accountBalances;\n\n // Remove balances for assets that disappeared from the account asset list\n // so stale entries cannot remain in state.\n for (const assetId of removed) {\n delete state.balances[accountId][assetId];\n }\n\n // Merge the balances returned by the snap for the newly added assets.\n for (const [assetId, balance] of Object.entries(addedBalances)) {\n state.balances[accountId][assetId] = balance;\n }\n\n // If the asset list was updated but the snap did not return a balance for\n // one of the added assets, keep the asset visible with an explicit zero.\n for (const assetId of added) {\n if (!state.balances[accountId][assetId]) {\n state.balances[accountId][assetId] = { amount: '0', unit: '' };\n }\n }\n }\n });\n }\n\n /**\n * Updates the balances of one account. This method doesn't return\n * anything, but it updates the state of the controller.\n *\n * @param accountId - The account ID.\n * @param assets - The list of asset types for this account to upadte.\n */\n async #updateBalance(\n accountId: string,\n assets: CaipAssetType[],\n ): Promise<void> {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n const { isUnlocked } = this.messenger.call('KeyringController:getState');\n\n if (!isUnlocked) {\n return;\n }\n\n try {\n const account = this.#getAccount(accountId);\n\n if (account.metadata.snap) {\n const accountBalance = await this.#getBalances(\n account.id,\n account.metadata.snap.id,\n assets,\n );\n\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n state.balances[accountId] = accountBalance;\n });\n }\n } catch (error) {\n // FIXME: Maybe we shouldn't catch all errors here since this method is also being\n // used in the public methods. This means if something else uses `updateBalance` it\n // won't be able to catch and gets the error itself...\n console.error(\n `Failed to fetch balances for account ${accountId}:`,\n error,\n );\n }\n }\n\n /**\n * Updates the balances of one account. This method doesn't return\n * anything, but it updates the state of the controller.\n *\n * @param accountId - The account ID.\n */\n async updateBalance(accountId: string): Promise<void> {\n await this.#updateBalance(accountId, this.#listAccountAssets(accountId));\n }\n\n /**\n * Lists the multichain accounts coming from the `AccountsController`.\n *\n * @returns A list of multichain accounts.\n */\n #listMultichainAccounts(): InternalAccount[] {\n return this.messenger.call('AccountsController:listMultichainAccounts');\n }\n\n /**\n * Lists the accounts that we should get balances for.\n *\n * @returns A list of accounts that we should get balances for.\n */\n #listAccounts(): InternalAccount[] {\n const accounts = this.#listMultichainAccounts();\n return accounts.filter((account) => this.#isNonEvmAccount(account));\n }\n\n /**\n * Lists the accounts assets.\n *\n * @param accountId - The account ID.\n * @returns The list of assets for this account, returns an empty list if none.\n */\n #listAccountAssets(accountId: string): CaipAssetType[] {\n // TODO: Add an action `MultichainAssetsController:getAccountAssets` maybe?\n const assetsState = this.messenger.call(\n 'MultichainAssetsController:getState',\n );\n\n return assetsState.accountsAssets[accountId] ?? [];\n }\n\n /**\n * Get a non-EVM account from its ID.\n *\n * @param accountId - The account ID.\n * @returns The non-EVM account.\n */\n #getAccount(accountId: string): InternalAccount {\n const account: InternalAccount | undefined = this.#listAccounts().find(\n (multichainAccount) => multichainAccount.id === accountId,\n );\n\n if (!account) {\n throw new Error(`Unknown account: ${accountId}`);\n }\n\n return account;\n }\n\n /**\n * Checks for non-EVM accounts.\n *\n * @param account - The new account to be checked.\n * @returns True if the account is a non-EVM account, false otherwise.\n */\n #isNonEvmAccount(account: InternalAccount): boolean {\n return (\n !isEvmAccountType(account.type) &&\n // Non-EVM accounts are backed by a Snap for now\n account.metadata.snap !== undefined\n );\n }\n\n /**\n * Handles balance updates received from the AccountsController.\n *\n * @param balanceUpdate - The balance update event containing new balances.\n */\n #handleOnAccountBalancesUpdated(\n balanceUpdate: AccountBalancesUpdatedEventPayload,\n ): void {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n Object.entries(balanceUpdate.balances).forEach(\n ([accountId, assetBalances]) => {\n if (accountId in state.balances) {\n Object.assign(state.balances[accountId], assetBalances);\n }\n },\n );\n });\n }\n\n /**\n * Handles changes when a new account has been removed.\n *\n * @param accountId - The account ID being removed.\n */\n async #handleOnAccountRemoved(accountId: string): Promise<void> {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n if (accountId in this.state.balances) {\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n delete state.balances[accountId];\n });\n }\n }\n\n /**\n * Get the balances for an account.\n *\n * @param accountId - ID of the account to get balances for.\n * @param snapId - ID of the Snap which manages the account.\n * @param assetTypes - Array of asset types to get balances for.\n * @returns A map of asset types to balances.\n */\n async #getBalances(\n accountId: string,\n snapId: string,\n assetTypes: CaipAssetType[],\n ): Promise<Record<CaipAssetType, Balance>> {\n return await this.#getClient(snapId).getAccountBalances(\n accountId,\n assetTypes,\n );\n }\n\n /**\n * Gets a `KeyringClient` for a Snap.\n *\n * @param snapId - ID of the Snap to get the client for.\n * @returns A `KeyringClient` for the Snap.\n */\n #getClient(snapId: string): KeyringClient {\n return new KeyringClient({\n send: async (request: JsonRpcRequest) =>\n (await this.messenger.call('SnapController:handleRequest', {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n })) as Promise<Json>,\n });\n }\n}\n"]}
1
+ {"version":3,"file":"MultichainBalancesController.mjs","sourceRoot":"","sources":["../../src/MultichainBalancesController/MultichainBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerAccountBalancesUpdatesEvent,\n} from '@metamask/accounts-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n StateMetadata,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n Balance,\n CaipAssetType,\n AccountBalancesUpdatedEventPayload,\n} from '@metamask/keyring-api';\nimport type { KeyringControllerGetStateAction } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { CaipChainId, Json, JsonRpcRequest } from '@metamask/utils';\nimport type { Draft } from 'immer';\n\nimport type {\n MultichainAssetsControllerGetStateAction,\n MultichainAssetsControllerAccountAssetListUpdatedEvent,\n} from '../MultichainAssetsController';\nimport {\n buildBalanceRowsWithAccountAssetInfo,\n fetchAccountAssetInfoFromSnap,\n filterAssetsForAccountAssetEnrichment,\n} from './account-asset-info';\nimport type {\n GetAccountAssetInfoResponse,\n MultichainAccountBalance,\n} from './account-asset-info';\n\nexport type { AccountAssetInfo, MultichainAccountBalance } from './account-asset-info';\n\nconst controllerName = 'MultichainBalancesController';\n\n/**\n * State used by the {@link MultichainBalancesController} to cache account balances.\n */\nexport type MultichainBalancesControllerState = {\n balances: {\n [account: string]: {\n [asset: string]: MultichainAccountBalance;\n };\n };\n};\n\n/**\n * Constructs the default {@link MultichainBalancesController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainBalancesController} state.\n */\nexport function getDefaultMultichainBalancesControllerState(): MultichainBalancesControllerState {\n return { balances: {} };\n}\n\n/**\n * Returns the state of the {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MultichainBalancesControllerState\n >;\n\n/**\n * Event emitted when the state of the {@link MultichainBalancesController} changes.\n */\nexport type MultichainBalancesControllerStateChange =\n ControllerStateChangeEvent<\n typeof controllerName,\n MultichainBalancesControllerState\n >;\n\n/**\n * Actions exposed by the {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerActions =\n MultichainBalancesControllerGetStateAction;\n\n/**\n * Events emitted by {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerEvents =\n MultichainBalancesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\ntype AllowedActions =\n | SnapControllerHandleRequestAction\n | AccountsControllerListMultichainAccountsAction\n | MultichainAssetsControllerGetStateAction\n | KeyringControllerGetStateAction;\n\n/**\n * Events that this controller is allowed to subscribe.\n */\ntype AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | AccountsControllerAccountBalancesUpdatesEvent\n | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainBalancesController.\n */\nexport type MultichainBalancesControllerMessenger = Messenger<\n typeof controllerName,\n MultichainBalancesControllerActions | AllowedActions,\n MultichainBalancesControllerEvents | AllowedEvents\n>;\n\n/**\n * {@link MultichainBalancesController}'s metadata.\n *\n * This allows us to choose if fields of the state should be persisted or not\n * using the `persist` flag; and if they can be sent to Sentry or not, using\n * the `anonymous` flag.\n */\nconst balancesControllerMetadata: StateMetadata<MultichainBalancesControllerState> =\n {\n balances: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n };\n\n/**\n * The MultichainBalancesController is responsible for fetching and caching account\n * balances.\n */\nexport class MultichainBalancesController extends BaseController<\n typeof controllerName,\n MultichainBalancesControllerState,\n MultichainBalancesControllerMessenger\n> {\n readonly #isDeprecated: () => boolean;\n\n /**\n * Creates a MultichainBalancesController instance.\n *\n * @param options - Constructor options.\n * @param options.messenger - A reference to the messenger.\n * @param options.state - The initial state.\n * @param options.isDeprecated - Optional function that returns true to completely\n * disable this controller (no requests, no state updates). When it returns\n * `true`, `balances` is reset to `{}` at construction and at every entry point,\n * so no stale balances remain in state. The function is evaluated dynamically\n * on each entry point so it can be toggled at runtime. Intended for use when\n * a higher-level controller (e.g. AssetsController) supersedes this one.\n */\n constructor({\n messenger,\n state = {},\n isDeprecated = (): boolean => false,\n }: {\n messenger: MultichainBalancesControllerMessenger;\n state?: Partial<MultichainBalancesControllerState>;\n isDeprecated?: () => boolean;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: balancesControllerMetadata,\n state: {\n ...getDefaultMultichainBalancesControllerState(),\n ...state,\n },\n });\n\n this.#isDeprecated = isDeprecated;\n\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n } else {\n // Fetch initial balances for all non-EVM accounts\n for (const account of this.#listAccounts()) {\n // Fetching the balance is asynchronous and we cannot use `await` here.\n // eslint-disable-next-line no-void\n void this.updateBalance(account.id);\n }\n }\n\n this.messenger.subscribe(\n 'AccountsController:accountRemoved',\n (account: string) => this.#handleOnAccountRemoved(account),\n );\n this.messenger.subscribe(\n 'AccountsController:accountBalancesUpdated',\n (balanceUpdate: AccountBalancesUpdatedEventPayload) =>\n this.#handleOnAccountBalancesUpdated(balanceUpdate),\n );\n\n this.messenger.subscribe(\n 'MultichainAssetsController:accountAssetListUpdated',\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async ({ assets }) => {\n const updatedAccountAssets = Object.entries(assets).map(\n ([accountId, { added, removed, refreshed }]) => ({\n accountId,\n added: [...added],\n removed: [...removed],\n refreshed: refreshed ? [...refreshed] : [],\n }),\n );\n\n await this.#handleOnAccountAssetListUpdated(updatedAccountAssets);\n },\n );\n }\n\n /**\n * Clears all persisted `balances` so that no stale balances remain in state.\n *\n * Called from every entry point when `isDeprecated()` is true so that a\n * runtime toggle propagates to state immediately, even if the controller was\n * originally constructed while it was enabled. The update is skipped when\n * `balances` is already empty to avoid emitting redundant state changes.\n */\n #enforceDisabledState(): void {\n if (Object.keys(this.state.balances).length === 0) {\n return;\n }\n this.update((state) => {\n state.balances = {};\n });\n }\n\n /**\n * Reconciles cached balances after a multichain asset-list update event.\n *\n * The event payload is treated as a delta:\n * - balances for `removed` assets are deleted so stale entries cannot remain\n * - balances for `added` and `refreshed` assets are fetched from the snap and merged in\n * - if an added asset is not returned by the snap, a zero placeholder is stored\n * so the asset can still be represented in state\n *\n * @param accounts - The per-account asset deltas from the asset-list update event.\n */\n async #handleOnAccountAssetListUpdated(\n accounts: {\n accountId: string;\n added: CaipAssetType[];\n removed: CaipAssetType[];\n refreshed: CaipAssetType[];\n }[],\n ): Promise<void> {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n const { isUnlocked } = this.messenger.call('KeyringController:getState');\n\n if (!isUnlocked) {\n return;\n }\n const balancesToAdd: MultichainBalancesControllerState['balances'] = {};\n\n for (const { accountId, added, refreshed } of accounts) {\n const assetsToFetch = [...added, ...refreshed];\n if (assetsToFetch.length === 0) {\n continue;\n }\n\n const account = this.#getAccount(accountId);\n const snapId = account.metadata.snap?.id;\n\n if (!snapId) {\n continue;\n }\n\n const accountBalance = await this.#getBalances(\n account.id,\n snapId,\n assetsToFetch,\n );\n const chainId = account.scopes[0];\n\n const enrichment = chainId\n ? await this.#fetchAccountAssetInfo(\n accountId,\n chainId,\n snapId as SnapId,\n assetsToFetch,\n ).catch(() => undefined)\n : undefined;\n\n balancesToAdd[accountId] = buildBalanceRowsWithAccountAssetInfo(\n assetsToFetch,\n accountBalance,\n enrichment,\n );\n }\n\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n for (const { accountId, added, removed } of accounts) {\n const addedBalances = balancesToAdd[accountId] ?? {};\n\n state.balances[accountId] = state.balances[accountId] ?? {};\n\n // Remove balances for assets that disappeared from the account asset list\n // so stale entries cannot remain in state.\n for (const assetId of removed) {\n delete state.balances[accountId][assetId];\n }\n\n // Merge the balances returned by the snap for the newly added assets.\n for (const [assetId, balance] of Object.entries(addedBalances)) {\n state.balances[accountId][assetId] = {\n ...(state.balances[accountId][assetId] ?? {\n amount: '0',\n unit: '',\n }),\n ...balance,\n };\n }\n\n // If the asset list was updated but the snap did not return a balance for\n // one of the added assets, keep the asset visible with an explicit zero.\n for (const assetId of added) {\n if (!state.balances[accountId][assetId]) {\n state.balances[accountId][assetId] = { amount: '0', unit: '' };\n }\n }\n }\n });\n }\n\n /**\n * Updates the balances of one account. This method doesn't return\n * anything, but it updates the state of the controller.\n *\n * @param accountId - The account ID.\n * @param assets - The list of asset types for this account to upadte.\n */\n async #updateBalance(\n accountId: string,\n assets: CaipAssetType[],\n ): Promise<void> {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n const { isUnlocked } = this.messenger.call('KeyringController:getState');\n\n if (!isUnlocked) {\n return;\n }\n\n try {\n const account = this.#getAccount(accountId);\n\n if (account.metadata.snap) {\n const accountBalance = await this.#getBalances(\n account.id,\n account.metadata.snap.id,\n assets,\n );\n\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n state.balances[accountId] = mergeAccountBalances(\n state.balances[accountId] ?? {},\n accountBalance,\n );\n });\n }\n } catch (error) {\n // FIXME: Maybe we shouldn't catch all errors here since this method is also being\n // used in the public methods. This means if something else uses `updateBalance` it\n // won't be able to catch and gets the error itself...\n console.error(\n `Failed to fetch balances for account ${accountId}:`,\n error,\n );\n }\n }\n\n /**\n * Updates the balances of one account. This method doesn't return\n * anything, but it updates the state of the controller.\n *\n * @param accountId - The account ID.\n */\n async updateBalance(accountId: string): Promise<void> {\n await this.#updateBalance(accountId, this.#listAccountAssets(accountId));\n }\n\n /**\n * Lists the multichain accounts coming from the `AccountsController`.\n *\n * @returns A list of multichain accounts.\n */\n #listMultichainAccounts(): InternalAccount[] {\n return this.messenger.call('AccountsController:listMultichainAccounts');\n }\n\n /**\n * Lists the accounts that we should get balances for.\n *\n * @returns A list of accounts that we should get balances for.\n */\n #listAccounts(): InternalAccount[] {\n const accounts = this.#listMultichainAccounts();\n return accounts.filter((account) => this.#isNonEvmAccount(account));\n }\n\n /**\n * Lists the accounts assets.\n *\n * @param accountId - The account ID.\n * @returns The list of assets for this account, returns an empty list if none.\n */\n #listAccountAssets(accountId: string): CaipAssetType[] {\n // TODO: Add an action `MultichainAssetsController:getAccountAssets` maybe?\n const assetsState = this.messenger.call(\n 'MultichainAssetsController:getState',\n );\n\n return assetsState.accountsAssets[accountId] ?? [];\n }\n\n /**\n * Get a non-EVM account from its ID.\n *\n * @param accountId - The account ID.\n * @returns The non-EVM account.\n */\n #getAccount(accountId: string): InternalAccount {\n const account: InternalAccount | undefined = this.#listAccounts().find(\n (multichainAccount) => multichainAccount.id === accountId,\n );\n\n if (!account) {\n throw new Error(`Unknown account: ${accountId}`);\n }\n\n return account;\n }\n\n /**\n * Checks for non-EVM accounts.\n *\n * @param account - The new account to be checked.\n * @returns True if the account is a non-EVM account, false otherwise.\n */\n #isNonEvmAccount(account: InternalAccount): boolean {\n return (\n !isEvmAccountType(account.type) &&\n // Non-EVM accounts are backed by a Snap for now\n account.metadata.snap !== undefined\n );\n }\n\n /**\n * Handles balance updates received from the AccountsController.\n *\n * @param balanceUpdate - The balance update event containing new balances.\n */\n #handleOnAccountBalancesUpdated(\n balanceUpdate: AccountBalancesUpdatedEventPayload,\n ): void {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n for (const [accountId, assetBalances] of Object.entries(\n balanceUpdate.balances,\n )) {\n if (!(accountId in state.balances)) {\n continue;\n }\n state.balances[accountId] = mergeAccountBalances(\n state.balances[accountId],\n assetBalances,\n );\n }\n });\n }\n\n /**\n * Fetches snap account-asset enrichment for the given assets.\n *\n * @param accountId - Account id.\n * @param chainId - CAIP-2 chain id for enrichment.\n * @param snapId - Wallet snap id.\n * @param assetIds - Assets to enrich.\n * @returns Per-asset enrichment fields, or undefined when unavailable.\n */\n async #fetchAccountAssetInfo(\n accountId: string,\n chainId: CaipChainId,\n snapId: SnapId,\n assetIds: CaipAssetType[],\n ): Promise<GetAccountAssetInfoResponse | undefined> {\n const enrichmentAssets = filterAssetsForAccountAssetEnrichment(\n assetIds,\n chainId,\n );\n if (enrichmentAssets.length === 0) {\n return undefined;\n }\n\n return await fetchAccountAssetInfoFromSnap(\n (params) => this.messenger.call('SnapController:handleRequest', params),\n {\n accountId,\n snapId,\n chainId,\n assets: enrichmentAssets,\n },\n );\n }\n\n /**\n * Handles changes when a new account has been removed.\n *\n * @param accountId - The account ID being removed.\n */\n async #handleOnAccountRemoved(accountId: string): Promise<void> {\n if (this.#isDeprecated()) {\n this.#enforceDisabledState();\n return;\n }\n\n if (accountId in this.state.balances) {\n this.update((state: Draft<MultichainBalancesControllerState>) => {\n delete state.balances[accountId];\n });\n }\n }\n\n /**\n * Get the balances for an account.\n *\n * @param accountId - ID of the account to get balances for.\n * @param snapId - ID of the Snap which manages the account.\n * @param assetTypes - Array of asset types to get balances for.\n * @returns A map of asset types to balances.\n */\n async #getBalances(\n accountId: string,\n snapId: string,\n assetTypes: CaipAssetType[],\n ): Promise<Record<CaipAssetType, Balance>> {\n return await this.#getClient(snapId).getAccountBalances(\n accountId,\n assetTypes,\n );\n }\n\n /**\n * Gets a `KeyringClient` for a Snap.\n *\n * @param snapId - ID of the Snap to get the client for.\n * @returns A `KeyringClient` for the Snap.\n */\n #getClient(snapId: string): KeyringClient {\n return new KeyringClient({\n send: async (request: JsonRpcRequest) =>\n (await this.messenger.call('SnapController:handleRequest', {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n })) as Promise<Json>,\n });\n }\n}\n\nfunction mergeAccountBalances(\n previous: Record<string, MultichainAccountBalance>,\n incoming: Record<string, Partial<MultichainAccountBalance>>,\n): Record<string, MultichainAccountBalance> {\n const merged = { ...previous };\n\n for (const [assetId, balance] of Object.entries(incoming)) {\n merged[assetId] = {\n ...(merged[assetId] ?? { amount: '0', unit: '' }),\n ...balance,\n };\n }\n\n return merged;\n}\n"]}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildBalanceRowsWithAccountAssetInfo = exports.fetchAccountAssetInfoFromSnap = exports.createGetAccountAssetInfoClientRequest = exports.filterAssetsForAccountAssetEnrichment = exports.isAccountAssetInfoEnrichmentAvailable = exports.ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN = exports.GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD = void 0;
4
+ const snaps_utils_1 = require("@metamask/snaps-utils");
5
+ const utils_1 = require("@metamask/utils");
6
+ /** Snap clientRequest method for per-(account, asset) enrichment data. */
7
+ exports.GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD = 'getAccountAssetInfo';
8
+ /**
9
+ * Chains whose wallet snap implements {@link GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD}.
10
+ */
11
+ exports.ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN = {
12
+ 'stellar:pubnet': true,
13
+ 'stellar:testnet': true,
14
+ };
15
+ /**
16
+ * Returns whether the given chain supports snap account-asset enrichment.
17
+ *
18
+ * @param chainId - CAIP-2 chain identifier.
19
+ * @returns True when enrichment is configured for the chain.
20
+ */
21
+ function isAccountAssetInfoEnrichmentAvailable(chainId) {
22
+ return exports.ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN[chainId] === true;
23
+ }
24
+ exports.isAccountAssetInfoEnrichmentAvailable = isAccountAssetInfoEnrichmentAvailable;
25
+ /**
26
+ * Filters asset ids to those on a chain that supports account-asset enrichment.
27
+ *
28
+ * @param assetIds - CAIP-19 asset types to filter.
29
+ * @param chainId - Expected chain for enrichment (caller-provided scope).
30
+ * @returns Asset ids on the given chain when enrichment is available.
31
+ */
32
+ function filterAssetsForAccountAssetEnrichment(assetIds, chainId) {
33
+ if (!isAccountAssetInfoEnrichmentAvailable(chainId)) {
34
+ return [];
35
+ }
36
+ return assetIds.filter((assetId) => {
37
+ try {
38
+ return (0, utils_1.parseCaipAssetType)(assetId).chainId === chainId;
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ });
44
+ }
45
+ exports.filterAssetsForAccountAssetEnrichment = filterAssetsForAccountAssetEnrichment;
46
+ /**
47
+ * Builds a snap client request for `getAccountAssetInfo`.
48
+ *
49
+ * @param snapId - Wallet snap id.
50
+ * @param params - Account, chain scope, and assets to resolve.
51
+ * @param params.accountId - Account id passed to the snap client request.
52
+ * @param params.scope - CAIP-2 chain id for enrichment.
53
+ * @param params.assets - CAIP-19 assets to resolve.
54
+ * @returns Payload for `SnapController:handleRequest`.
55
+ */
56
+ function createGetAccountAssetInfoClientRequest(snapId, params) {
57
+ return {
58
+ snapId,
59
+ origin: 'metamask',
60
+ handler: snaps_utils_1.HandlerType.OnClientRequest,
61
+ request: {
62
+ jsonrpc: '2.0',
63
+ method: exports.GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD,
64
+ params,
65
+ },
66
+ };
67
+ }
68
+ exports.createGetAccountAssetInfoClientRequest = createGetAccountAssetInfoClientRequest;
69
+ /**
70
+ * Calls the snap `getAccountAssetInfo` client request handler.
71
+ *
72
+ * @param handleSnapRequest - SnapController handleRequest messenger action.
73
+ * @param options - Request parameters.
74
+ * @param options.accountId - Account id.
75
+ * @param options.snapId - Wallet snap id.
76
+ * @param options.chainId - CAIP-2 chain id.
77
+ * @param options.assets - CAIP-19 assets to resolve.
78
+ * @returns Per-asset enrichment fields, or undefined on failure.
79
+ */
80
+ async function fetchAccountAssetInfoFromSnap(handleSnapRequest, { accountId, snapId, chainId, assets, }) {
81
+ if (assets.length === 0) {
82
+ return undefined;
83
+ }
84
+ try {
85
+ return (await handleSnapRequest(createGetAccountAssetInfoClientRequest(snapId, {
86
+ accountId,
87
+ scope: chainId,
88
+ assets,
89
+ })));
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
95
+ exports.fetchAccountAssetInfoFromSnap = fetchAccountAssetInfoFromSnap;
96
+ /**
97
+ * Combines snap balance rows with optional per-asset enrichment fields.
98
+ *
99
+ * @param assetIds - Assets to include in the result.
100
+ * @param balances - Balance rows keyed by asset id.
101
+ * @param enrichment - Optional snap enrichment keyed by asset id.
102
+ * @returns Per-asset balance rows with merged `accountAssetInfo` when present.
103
+ */
104
+ function buildBalanceRowsWithAccountAssetInfo(assetIds, balances, enrichment) {
105
+ return Object.fromEntries(assetIds.map((assetId) => {
106
+ const balance = balances[assetId] ?? { amount: '0', unit: '' };
107
+ const accountAssetInfo = enrichment?.[assetId];
108
+ return [
109
+ assetId,
110
+ {
111
+ ...balance,
112
+ ...(accountAssetInfo === undefined ? {} : { accountAssetInfo }),
113
+ },
114
+ ];
115
+ }));
116
+ }
117
+ exports.buildBalanceRowsWithAccountAssetInfo = buildBalanceRowsWithAccountAssetInfo;
118
+ //# sourceMappingURL=account-asset-info.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-asset-info.cjs","sourceRoot":"","sources":["../../src/MultichainBalancesController/account-asset-info.ts"],"names":[],"mappings":";;;AAGA,uDAAoD;AAEpD,2CAAqD;AAErD,0EAA0E;AAC7D,QAAA,oCAAoC,GAC/C,qBAA8B,CAAC;AAqBjC;;GAEG;AACU,QAAA,sCAAsC,GAE/C;IACF,gBAAgB,EAAE,IAAI;IACtB,iBAAiB,EAAE,IAAI;CACxB,CAAC;AAOF;;;;;GAKG;AACH,SAAgB,qCAAqC,CACnD,OAAoB;IAEpB,OAAO,8CAAsC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;AAClE,CAAC;AAJD,sFAIC;AAED;;;;;;GAMG;AACH,SAAgB,qCAAqC,CACnD,QAAyB,EACzB,OAAoB;IAEpB,IAAI,CAAC,qCAAqC,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QACjC,IAAI,CAAC;YACH,OAAO,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAdD,sFAcC;AAED;;;;;;;;;GASG;AACH,SAAgB,sCAAsC,CACpD,MAAc,EACd,MAIC;IAED,OAAO;QACL,MAAM;QACN,MAAM,EAAE,UAAU;QAClB,OAAO,EAAE,yBAAW,CAAC,eAAe;QACpC,OAAO,EAAE;YACP,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,4CAAoC;YAC5C,MAAM;SACP;KACF,CAAC;AACJ,CAAC;AAlBD,wFAkBC;AAED;;;;;;;;;;GAUG;AACI,KAAK,UAAU,6BAA6B,CACjD,iBAA0C,EAC1C,EACE,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,GAMP;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,iBAAiB,CAC7B,sCAAsC,CAAC,MAAM,EAAE;YAC7C,SAAS;YACT,KAAK,EAAE,OAAO;YACd,MAAM;SACP,CAAC,CACH,CAAgC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AA7BD,sEA6BC;AAED;;;;;;;GAOG;AACH,SAAgB,oCAAoC,CAClD,QAAyB,EACzB,QAAwC,EACxC,UAAwC;IAExC,OAAO,MAAM,CAAC,WAAW,CACvB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAC/D,MAAM,gBAAgB,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC;QAE/C,OAAO;YACL,OAAO;YACP;gBACE,GAAG,OAAO;gBACV,GAAG,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC;aAChE;SACF,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAnBD,oFAmBC","sourcesContent":["import type { Balance } from '@metamask/keyring-api';\nimport type { SnapController } from '@metamask/snaps-controllers';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { CaipAssetType, CaipChainId } from '@metamask/utils';\nimport { parseCaipAssetType } from '@metamask/utils';\n\n/** Snap clientRequest method for per-(account, asset) enrichment data. */\nexport const GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD =\n 'getAccountAssetInfo' as const;\n\n/** Optional per-asset fields returned by snap enrichment (chain-specific semantics). */\nexport type AccountAssetInfo = {\n limit?: string;\n authorized?: boolean;\n sponsored?: boolean;\n};\n\nexport type GetAccountAssetInfoResponse = Record<\n CaipAssetType,\n AccountAssetInfo\n>;\n\n/** Per-asset balance row; `accountAssetInfo` carries chain-specific snap enrichment fields. */\nexport type MultichainAccountBalance = {\n amount: string;\n unit: string;\n accountAssetInfo?: AccountAssetInfo;\n};\n\n/**\n * Chains whose wallet snap implements {@link GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD}.\n */\nexport const ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN: Partial<\n Record<CaipChainId, boolean>\n> = {\n 'stellar:pubnet': true,\n 'stellar:testnet': true,\n};\n\n/** Caller shape for `SnapController:handleRequest`. */\nexport type SnapHandleRequestCaller = (\n params: Parameters<SnapController['handleRequest']>[0],\n) => Promise<unknown>;\n\n/**\n * Returns whether the given chain supports snap account-asset enrichment.\n *\n * @param chainId - CAIP-2 chain identifier.\n * @returns True when enrichment is configured for the chain.\n */\nexport function isAccountAssetInfoEnrichmentAvailable(\n chainId: CaipChainId,\n): boolean {\n return ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN[chainId] === true;\n}\n\n/**\n * Filters asset ids to those on a chain that supports account-asset enrichment.\n *\n * @param assetIds - CAIP-19 asset types to filter.\n * @param chainId - Expected chain for enrichment (caller-provided scope).\n * @returns Asset ids on the given chain when enrichment is available.\n */\nexport function filterAssetsForAccountAssetEnrichment(\n assetIds: CaipAssetType[],\n chainId: CaipChainId,\n): CaipAssetType[] {\n if (!isAccountAssetInfoEnrichmentAvailable(chainId)) {\n return [];\n }\n return assetIds.filter((assetId) => {\n try {\n return parseCaipAssetType(assetId).chainId === chainId;\n } catch {\n return false;\n }\n });\n}\n\n/**\n * Builds a snap client request for `getAccountAssetInfo`.\n *\n * @param snapId - Wallet snap id.\n * @param params - Account, chain scope, and assets to resolve.\n * @param params.accountId - Account id passed to the snap client request.\n * @param params.scope - CAIP-2 chain id for enrichment.\n * @param params.assets - CAIP-19 assets to resolve.\n * @returns Payload for `SnapController:handleRequest`.\n */\nexport function createGetAccountAssetInfoClientRequest(\n snapId: SnapId,\n params: {\n accountId: string;\n scope: CaipChainId;\n assets: CaipAssetType[];\n },\n): Parameters<SnapController['handleRequest']>[0] {\n return {\n snapId,\n origin: 'metamask',\n handler: HandlerType.OnClientRequest,\n request: {\n jsonrpc: '2.0',\n method: GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD,\n params,\n },\n };\n}\n\n/**\n * Calls the snap `getAccountAssetInfo` client request handler.\n *\n * @param handleSnapRequest - SnapController handleRequest messenger action.\n * @param options - Request parameters.\n * @param options.accountId - Account id.\n * @param options.snapId - Wallet snap id.\n * @param options.chainId - CAIP-2 chain id.\n * @param options.assets - CAIP-19 assets to resolve.\n * @returns Per-asset enrichment fields, or undefined on failure.\n */\nexport async function fetchAccountAssetInfoFromSnap(\n handleSnapRequest: SnapHandleRequestCaller,\n {\n accountId,\n snapId,\n chainId,\n assets,\n }: {\n accountId: string;\n snapId: SnapId;\n chainId: CaipChainId;\n assets: CaipAssetType[];\n },\n): Promise<GetAccountAssetInfoResponse | undefined> {\n if (assets.length === 0) {\n return undefined;\n }\n\n try {\n return (await handleSnapRequest(\n createGetAccountAssetInfoClientRequest(snapId, {\n accountId,\n scope: chainId,\n assets,\n }),\n )) as GetAccountAssetInfoResponse;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Combines snap balance rows with optional per-asset enrichment fields.\n *\n * @param assetIds - Assets to include in the result.\n * @param balances - Balance rows keyed by asset id.\n * @param enrichment - Optional snap enrichment keyed by asset id.\n * @returns Per-asset balance rows with merged `accountAssetInfo` when present.\n */\nexport function buildBalanceRowsWithAccountAssetInfo(\n assetIds: CaipAssetType[],\n balances: Record<CaipAssetType, Balance>,\n enrichment?: GetAccountAssetInfoResponse,\n): Record<string, MultichainAccountBalance> {\n return Object.fromEntries(\n assetIds.map((assetId) => {\n const balance = balances[assetId] ?? { amount: '0', unit: '' };\n const accountAssetInfo = enrichment?.[assetId];\n\n return [\n assetId,\n {\n ...balance,\n ...(accountAssetInfo === undefined ? {} : { accountAssetInfo }),\n },\n ];\n }),\n );\n}"]}
@@ -0,0 +1,82 @@
1
+ import type { Balance } from "@metamask/keyring-api";
2
+ import type { SnapController } from "@metamask/snaps-controllers";
3
+ import type { SnapId } from "@metamask/snaps-sdk";
4
+ import type { CaipAssetType, CaipChainId } from "@metamask/utils";
5
+ /** Snap clientRequest method for per-(account, asset) enrichment data. */
6
+ export declare const GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD: "getAccountAssetInfo";
7
+ /** Optional per-asset fields returned by snap enrichment (chain-specific semantics). */
8
+ export type AccountAssetInfo = {
9
+ limit?: string;
10
+ authorized?: boolean;
11
+ sponsored?: boolean;
12
+ };
13
+ export type GetAccountAssetInfoResponse = Record<CaipAssetType, AccountAssetInfo>;
14
+ /** Per-asset balance row; `accountAssetInfo` carries chain-specific snap enrichment fields. */
15
+ export type MultichainAccountBalance = {
16
+ amount: string;
17
+ unit: string;
18
+ accountAssetInfo?: AccountAssetInfo;
19
+ };
20
+ /**
21
+ * Chains whose wallet snap implements {@link GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD}.
22
+ */
23
+ export declare const ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN: Partial<Record<CaipChainId, boolean>>;
24
+ /** Caller shape for `SnapController:handleRequest`. */
25
+ export type SnapHandleRequestCaller = (params: Parameters<SnapController['handleRequest']>[0]) => Promise<unknown>;
26
+ /**
27
+ * Returns whether the given chain supports snap account-asset enrichment.
28
+ *
29
+ * @param chainId - CAIP-2 chain identifier.
30
+ * @returns True when enrichment is configured for the chain.
31
+ */
32
+ export declare function isAccountAssetInfoEnrichmentAvailable(chainId: CaipChainId): boolean;
33
+ /**
34
+ * Filters asset ids to those on a chain that supports account-asset enrichment.
35
+ *
36
+ * @param assetIds - CAIP-19 asset types to filter.
37
+ * @param chainId - Expected chain for enrichment (caller-provided scope).
38
+ * @returns Asset ids on the given chain when enrichment is available.
39
+ */
40
+ export declare function filterAssetsForAccountAssetEnrichment(assetIds: CaipAssetType[], chainId: CaipChainId): CaipAssetType[];
41
+ /**
42
+ * Builds a snap client request for `getAccountAssetInfo`.
43
+ *
44
+ * @param snapId - Wallet snap id.
45
+ * @param params - Account, chain scope, and assets to resolve.
46
+ * @param params.accountId - Account id passed to the snap client request.
47
+ * @param params.scope - CAIP-2 chain id for enrichment.
48
+ * @param params.assets - CAIP-19 assets to resolve.
49
+ * @returns Payload for `SnapController:handleRequest`.
50
+ */
51
+ export declare function createGetAccountAssetInfoClientRequest(snapId: SnapId, params: {
52
+ accountId: string;
53
+ scope: CaipChainId;
54
+ assets: CaipAssetType[];
55
+ }): Parameters<SnapController['handleRequest']>[0];
56
+ /**
57
+ * Calls the snap `getAccountAssetInfo` client request handler.
58
+ *
59
+ * @param handleSnapRequest - SnapController handleRequest messenger action.
60
+ * @param options - Request parameters.
61
+ * @param options.accountId - Account id.
62
+ * @param options.snapId - Wallet snap id.
63
+ * @param options.chainId - CAIP-2 chain id.
64
+ * @param options.assets - CAIP-19 assets to resolve.
65
+ * @returns Per-asset enrichment fields, or undefined on failure.
66
+ */
67
+ export declare function fetchAccountAssetInfoFromSnap(handleSnapRequest: SnapHandleRequestCaller, { accountId, snapId, chainId, assets, }: {
68
+ accountId: string;
69
+ snapId: SnapId;
70
+ chainId: CaipChainId;
71
+ assets: CaipAssetType[];
72
+ }): Promise<GetAccountAssetInfoResponse | undefined>;
73
+ /**
74
+ * Combines snap balance rows with optional per-asset enrichment fields.
75
+ *
76
+ * @param assetIds - Assets to include in the result.
77
+ * @param balances - Balance rows keyed by asset id.
78
+ * @param enrichment - Optional snap enrichment keyed by asset id.
79
+ * @returns Per-asset balance rows with merged `accountAssetInfo` when present.
80
+ */
81
+ export declare function buildBalanceRowsWithAccountAssetInfo(assetIds: CaipAssetType[], balances: Record<CaipAssetType, Balance>, enrichment?: GetAccountAssetInfoResponse): Record<string, MultichainAccountBalance>;
82
+ //# sourceMappingURL=account-asset-info.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-asset-info.d.cts","sourceRoot":"","sources":["../../src/MultichainBalancesController/account-asset-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,8BAA8B;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,oCAAoC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,4BAA4B;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,wBAAwB;AAGlE,0EAA0E;AAC1E,eAAO,MAAM,oCAAoC,uBACjB,CAAC;AAEjC,wFAAwF;AACxF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,aAAa,EACb,gBAAgB,CACjB,CAAC;AAEF,+FAA+F;AAC/F,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sCAAsC,EAAE,OAAO,CAC1D,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAI7B,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,uBAAuB,GAAG,CACpC,MAAM,EAAE,UAAU,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,KACnD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;GAKG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,WAAW,GACnB,OAAO,CAET;AAED;;;;;;GAMG;AACH,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,aAAa,EAAE,EACzB,OAAO,EAAE,WAAW,GACnB,aAAa,EAAE,CAWjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sCAAsC,CACpD,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IACN,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,GACA,UAAU,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAWhD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,6BAA6B,CACjD,iBAAiB,EAAE,uBAAuB,EAC1C,EACE,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,GACA,OAAO,CAAC,2BAA2B,GAAG,SAAS,CAAC,CAgBlD;AAED;;;;;;;GAOG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,aAAa,EAAE,EACzB,QAAQ,EAAE,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,EACxC,UAAU,CAAC,EAAE,2BAA2B,GACvC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAe1C"}
@@ -0,0 +1,82 @@
1
+ import type { Balance } from "@metamask/keyring-api";
2
+ import type { SnapController } from "@metamask/snaps-controllers";
3
+ import type { SnapId } from "@metamask/snaps-sdk";
4
+ import type { CaipAssetType, CaipChainId } from "@metamask/utils";
5
+ /** Snap clientRequest method for per-(account, asset) enrichment data. */
6
+ export declare const GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD: "getAccountAssetInfo";
7
+ /** Optional per-asset fields returned by snap enrichment (chain-specific semantics). */
8
+ export type AccountAssetInfo = {
9
+ limit?: string;
10
+ authorized?: boolean;
11
+ sponsored?: boolean;
12
+ };
13
+ export type GetAccountAssetInfoResponse = Record<CaipAssetType, AccountAssetInfo>;
14
+ /** Per-asset balance row; `accountAssetInfo` carries chain-specific snap enrichment fields. */
15
+ export type MultichainAccountBalance = {
16
+ amount: string;
17
+ unit: string;
18
+ accountAssetInfo?: AccountAssetInfo;
19
+ };
20
+ /**
21
+ * Chains whose wallet snap implements {@link GET_ACCOUNT_ASSET_INFO_CLIENT_METHOD}.
22
+ */
23
+ export declare const ACCOUNT_ASSET_INFO_ENRICHMENT_BY_CHAIN: Partial<Record<CaipChainId, boolean>>;
24
+ /** Caller shape for `SnapController:handleRequest`. */
25
+ export type SnapHandleRequestCaller = (params: Parameters<SnapController['handleRequest']>[0]) => Promise<unknown>;
26
+ /**
27
+ * Returns whether the given chain supports snap account-asset enrichment.
28
+ *
29
+ * @param chainId - CAIP-2 chain identifier.
30
+ * @returns True when enrichment is configured for the chain.
31
+ */
32
+ export declare function isAccountAssetInfoEnrichmentAvailable(chainId: CaipChainId): boolean;
33
+ /**
34
+ * Filters asset ids to those on a chain that supports account-asset enrichment.
35
+ *
36
+ * @param assetIds - CAIP-19 asset types to filter.
37
+ * @param chainId - Expected chain for enrichment (caller-provided scope).
38
+ * @returns Asset ids on the given chain when enrichment is available.
39
+ */
40
+ export declare function filterAssetsForAccountAssetEnrichment(assetIds: CaipAssetType[], chainId: CaipChainId): CaipAssetType[];
41
+ /**
42
+ * Builds a snap client request for `getAccountAssetInfo`.
43
+ *
44
+ * @param snapId - Wallet snap id.
45
+ * @param params - Account, chain scope, and assets to resolve.
46
+ * @param params.accountId - Account id passed to the snap client request.
47
+ * @param params.scope - CAIP-2 chain id for enrichment.
48
+ * @param params.assets - CAIP-19 assets to resolve.
49
+ * @returns Payload for `SnapController:handleRequest`.
50
+ */
51
+ export declare function createGetAccountAssetInfoClientRequest(snapId: SnapId, params: {
52
+ accountId: string;
53
+ scope: CaipChainId;
54
+ assets: CaipAssetType[];
55
+ }): Parameters<SnapController['handleRequest']>[0];
56
+ /**
57
+ * Calls the snap `getAccountAssetInfo` client request handler.
58
+ *
59
+ * @param handleSnapRequest - SnapController handleRequest messenger action.
60
+ * @param options - Request parameters.
61
+ * @param options.accountId - Account id.
62
+ * @param options.snapId - Wallet snap id.
63
+ * @param options.chainId - CAIP-2 chain id.
64
+ * @param options.assets - CAIP-19 assets to resolve.
65
+ * @returns Per-asset enrichment fields, or undefined on failure.
66
+ */
67
+ export declare function fetchAccountAssetInfoFromSnap(handleSnapRequest: SnapHandleRequestCaller, { accountId, snapId, chainId, assets, }: {
68
+ accountId: string;
69
+ snapId: SnapId;
70
+ chainId: CaipChainId;
71
+ assets: CaipAssetType[];
72
+ }): Promise<GetAccountAssetInfoResponse | undefined>;
73
+ /**
74
+ * Combines snap balance rows with optional per-asset enrichment fields.
75
+ *
76
+ * @param assetIds - Assets to include in the result.
77
+ * @param balances - Balance rows keyed by asset id.
78
+ * @param enrichment - Optional snap enrichment keyed by asset id.
79
+ * @returns Per-asset balance rows with merged `accountAssetInfo` when present.
80
+ */
81
+ export declare function buildBalanceRowsWithAccountAssetInfo(assetIds: CaipAssetType[], balances: Record<CaipAssetType, Balance>, enrichment?: GetAccountAssetInfoResponse): Record<string, MultichainAccountBalance>;
82
+ //# sourceMappingURL=account-asset-info.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-asset-info.d.mts","sourceRoot":"","sources":["../../src/MultichainBalancesController/account-asset-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,8BAA8B;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,oCAAoC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,4BAA4B;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,wBAAwB;AAGlE,0EAA0E;AAC1E,eAAO,MAAM,oCAAoC,uBACjB,CAAC;AAEjC,wFAAwF;AACxF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,aAAa,EACb,gBAAgB,CACjB,CAAC;AAEF,+FAA+F;AAC/F,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sCAAsC,EAAE,OAAO,CAC1D,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAI7B,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,uBAAuB,GAAG,CACpC,MAAM,EAAE,UAAU,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,KACnD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;GAKG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,WAAW,GACnB,OAAO,CAET;AAED;;;;;;GAMG;AACH,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,aAAa,EAAE,EACzB,OAAO,EAAE,WAAW,GACnB,aAAa,EAAE,CAWjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sCAAsC,CACpD,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IACN,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,GACA,UAAU,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAWhD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,6BAA6B,CACjD,iBAAiB,EAAE,uBAAuB,EAC1C,EACE,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,GACA,OAAO,CAAC,2BAA2B,GAAG,SAAS,CAAC,CAgBlD;AAED;;;;;;;GAOG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,aAAa,EAAE,EACzB,QAAQ,EAAE,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,EACxC,UAAU,CAAC,EAAE,2BAA2B,GACvC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAe1C"}