@metamask-previews/assets-controllers 110.0.0-preview-855d28a → 110.0.0-preview-8c40ad69c

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.
package/CHANGELOG.md CHANGED
@@ -11,10 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  - Bump `@metamask/keyring-api` from `^23.5.0` to `^23.7.0` ([#9676](https://github.com/MetaMask/core/pull/9676))
13
13
 
14
- ### Fixed
15
-
16
- - Treat `loan` (not `lending`) as the DeFi liability position type in `DEFI_POSITION_LIABILITY_TYPES`, matching corrected Accounts API v6 position types ([#9683](https://github.com/MetaMask/core/pull/9683))
17
-
18
14
  ## [110.0.0]
19
15
 
20
16
  ### Added
@@ -14,7 +14,7 @@ exports.DEFI_POSITION_TYPES = core_backend_1.V6_DEFI_POSITION_TYPES;
14
14
  * Position types whose fiat value is a liability and is subtracted from the
15
15
  * protocol group's aggregated `marketValue`.
16
16
  */
17
- exports.DEFI_POSITION_LIABILITY_TYPES = new Set(['loan']);
17
+ exports.DEFI_POSITION_LIABILITY_TYPES = new Set(['lending']);
18
18
  // Prefer ETH/WETH first in the list-row icon stack when a protocol has multiple
19
19
  // underlyings. Display-only.
20
20
  const SYMBOL_PRIORITY = ['ETH', 'WETH'];
@@ -1 +1 @@
1
- {"version":3,"file":"group-defi-positions-v6.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"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","sourcesContent":["import { V6_DEFI_POSITION_TYPES } from '@metamask/core-backend';\nimport type {\n V6BalanceItem,\n V6BalanceMetadata,\n V6BalancesResponse,\n V6DeFiPositionType,\n} from '@metamask/core-backend';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n} from '@metamask/utils';\nimport {\n KnownCaipNamespace,\n parseCaipAccountId,\n parseCaipAssetType,\n parseCaipChainId,\n} from '@metamask/utils';\n\n/** Static.cx host used to build CAIP-19 token icon URLs for DeFi positions. */\nconst STATIC_METAMASK_BASE_URL = 'https://static.cx.metamask.io';\n\n/**\n * Possible `positionType` values from Accounts API v6 DeFi metadata.\n * Re-export of {@link V6_DEFI_POSITION_TYPES} from `@metamask/core-backend`.\n */\nexport const DEFI_POSITION_TYPES = V6_DEFI_POSITION_TYPES;\n\n/**\n * The specific module or functionality within a DeFi protocol where a position\n * is held. Alias of {@link V6DeFiPositionType} from `@metamask/core-backend`.\n */\nexport type DeFiPositionType = V6DeFiPositionType;\n\n/**\n * Position types whose fiat value is a liability and is subtracted from the\n * protocol group's aggregated `marketValue`.\n */\nexport const DEFI_POSITION_LIABILITY_TYPES: ReadonlySet<DeFiPositionType> =\n new Set<DeFiPositionType>(['loan']);\n\n/**\n * An icon-group entry shown next to a protocol in the DeFi tab list.\n */\nexport type DeFiPositionIconGroupItem = {\n /** Token icon URL, when one can be built for the asset. */\n avatarValue?: string;\n symbol: string;\n};\n\n/**\n * A single underlying position row shown on the DeFi details page.\n */\nexport type DeFiUnderlyingPosition = {\n assetId: CaipAssetType;\n chainId: CaipChainId;\n symbol: string;\n name: string;\n decimals: number;\n /** Raw balance string as returned by the API. */\n balance: string;\n /** Fiat market value in the requested currency, when a price is available. */\n marketValue?: number;\n /** Position type from protocol metadata. */\n positionType: DeFiPositionType;\n /** Address of the pool this position belongs to. */\n poolAddress: string;\n /**\n * Upstream grouping id from the API. Rows that share a `productName` can\n * still carry distinct `groupId`s (e.g. multiple Pendle YT markets).\n */\n groupId: string;\n /** Token icon URL, when one can be built for the asset. */\n tokenImage?: string;\n};\n\n/**\n * A section of the details page, grouping positions that share the same\n * API `productName`. A single `protocolId` can have multiple products\n * (different pools/markets under one protocol), so a group may contain\n * several sections.\n */\nexport type DeFiPositionDetailsSection = {\n /** Section label from the API (`metadata.productName`). */\n productName: string;\n positions: DeFiUnderlyingPosition[];\n};\n\n/**\n * One row in the DeFi tab list (a protocol on a given chain), with the details\n * needed to render the details page embedded directly inside it.\n */\nexport type DeFiProtocolPositionGroup = {\n protocolId: string;\n /**\n * Product name from the first position seen for this protocol. Prefer\n * `protocolId` for the list-row title; use section `productName`s for\n * per-product detail headings.\n */\n productName: string;\n protocolIconUrl: string;\n chainId: CaipChainId;\n /**\n * Aggregated fiat market value across all positions in the group.\n * `lending` positions are subtracted; all other types are added.\n */\n marketValue: number;\n /** Icon-group entries for the list row. */\n iconGroup: DeFiPositionIconGroupItem[];\n /**\n * Detail sections consumed by the details page, one per distinct API\n * `productName` under this `protocolId`.\n */\n sections: DeFiPositionDetailsSection[];\n};\n\n/**\n * DeFi positions for every queried account, keyed by the internal MetaMask\n * account ID (`InternalAccount.id` UUID), the same key AssetsController uses.\n * Each account maps to a flat list of protocol groups; filter by each group's\n * `chainId` rather than digging through a nested chain map.\n */\nexport type DeFiPositionsByAccount = {\n [accountId: string]: DeFiProtocolPositionGroup[];\n};\n\n// Prefer ETH/WETH first in the list-row icon stack when a protocol has multiple\n// underlyings. Display-only.\nconst SYMBOL_PRIORITY = ['ETH', 'WETH'];\n\ntype DefiBalanceWithMetadata = V6BalanceItem & { metadata: V6BalanceMetadata };\n\n/**\n * Builds a static token icon URL for a CAIP asset ID.\n *\n * @param assetId - The CAIP-19 asset ID.\n * @returns The token icon URL, or `undefined` when it cannot be built.\n */\nfunction getDefiTokenImageUrl(assetId: CaipAssetType): string | undefined {\n try {\n const { chainId } = parseCaipAssetType(assetId);\n const { namespace } = parseCaipChainId(chainId);\n const isEvm = namespace === KnownCaipNamespace.Eip155;\n const normalizedAssetId = (isEvm ? assetId.toLowerCase() : assetId).replace(\n /:/gu,\n '/',\n );\n\n return `${STATIC_METAMASK_BASE_URL}/api/v2/tokenIcons/assets/${normalizedAssetId}.png`;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns whether a balance row is a DeFi position carrying protocol metadata.\n *\n * @param balance - A balance row from the v6 API.\n * @returns True when the row is a `category: defi` row with protocol metadata.\n */\nfunction isDefiBalanceWithMetadata(\n balance: V6BalanceItem,\n): balance is DefiBalanceWithMetadata {\n return (\n balance.category === 'defi' &&\n balance.metadata !== undefined &&\n (balance.metadata as Partial<V6BalanceMetadata>).protocolId !== undefined\n );\n}\n\n/**\n * Returns the fiat market value for a v6 DeFi balance row.\n *\n * @param balance - A balance row from the v6 API.\n * @returns The fiat value, or `undefined` when price is missing or the\n * balance/price is invalid.\n */\nfunction getMarketValue(balance: V6BalanceItem): number | undefined {\n if (balance.price === undefined) {\n return undefined;\n }\n\n const normalizedBalance = Number.parseFloat(balance.balance);\n const price = Number.parseFloat(balance.price);\n\n if (!Number.isFinite(normalizedBalance) || !Number.isFinite(price)) {\n return undefined;\n }\n\n return normalizedBalance * price;\n}\n\n/**\n * Returns the sign used when rolling a position's market value into a protocol\n * group total. Liability types (currently `lending`) subtract; all others add.\n *\n * @param positionType - The position's protocol module type.\n * @returns `-1` for liabilities, `1` otherwise.\n */\nfunction getMarketValueSign(positionType: DeFiPositionType): 1 | -1 {\n return DEFI_POSITION_LIABILITY_TYPES.has(positionType) ? -1 : 1;\n}\n\n/**\n * Moves a priority symbol (ETH/WETH) to the front of the icon group, in place.\n *\n * @param iconGroup - The icon-group entries to reorder.\n */\nfunction orderIconGroup(iconGroup: DeFiPositionIconGroupItem[]): void {\n const priorityIndex = iconGroup.findIndex((item) =>\n SYMBOL_PRIORITY.includes(item.symbol),\n );\n\n if (priorityIndex > 0) {\n const [priorityIcon] = iconGroup.splice(priorityIndex, 1);\n iconGroup.unshift(priorityIcon);\n }\n}\n\n/**\n * Maps a DeFi balance row to a details-page underlying position.\n *\n * @param balance - A DeFi balance row with protocol metadata.\n * @returns The underlying position for the details page.\n */\nfunction toUnderlyingPosition(\n balance: DefiBalanceWithMetadata,\n): DeFiUnderlyingPosition {\n const assetId = balance.assetId as CaipAssetType;\n const { chainId } = parseCaipAssetType(assetId);\n const { positionType, poolAddress, groupId } = balance.metadata;\n\n return {\n assetId,\n chainId,\n symbol: balance.symbol,\n name: balance.name,\n balance: balance.balance,\n decimals: balance.decimals,\n marketValue: getMarketValue(balance),\n positionType,\n poolAddress,\n groupId,\n tokenImage: getDefiTokenImageUrl(assetId),\n };\n}\n\n/**\n * Builds a chain-reference-agnostic key (`namespace:address`) for matching the\n * CAIP-10 account IDs we request against the ones the v6 API echoes back.\n *\n * We request EVM balances with the all-chains reference (`eip155:0:<address>`),\n * but the response echoes a separate per-chain ID for every chain\n * (`eip155:1:<address>`, `eip155:137:<address>`, ...). Matching on the full\n * CAIP-10 string therefore fails, so we drop the reference and match on\n * namespace + address instead. EVM addresses are lowercased; other namespaces\n * keep their case.\n *\n * @param caipAccountId - A CAIP-10 account ID.\n * @returns The match key, or a case-normalized fallback if parsing fails.\n */\nfunction toAccountMatchKey(caipAccountId: string): string {\n try {\n const {\n chain: { namespace },\n address,\n } = parseCaipAccountId(caipAccountId as CaipAccountId);\n const normalizedAddress =\n namespace === KnownCaipNamespace.Eip155 ? address.toLowerCase() : address;\n return `${namespace}:${normalizedAddress}`;\n } catch {\n return caipAccountId.startsWith(`${KnownCaipNamespace.Eip155}:`)\n ? caipAccountId.toLowerCase()\n : caipAccountId;\n }\n}\n\n/**\n * Transforms a v6 multiaccount balances response into the stored DeFi state:\n * positions keyed by internal account ID, each mapping to a flat list of\n * protocol groups. Every group carries its own `chainId` (so the client can\n * filter without a nested chain map) plus both the DeFi-tab summary and the\n * details-page sections. Accounts present in the response but with no DeFi\n * positions are included with an empty list so stale data is cleared.\n *\n * When `internalAccountIdByCaip` is provided, response account IDs are matched\n * to internal MetaMask account IDs via namespace + address (ignoring chain\n * reference and EVM case). Unmatched accounts are skipped. When omitted, the\n * response account ID is used as-is (handy for unit tests).\n *\n * @param response - The v6 multiaccount balances response.\n * @param internalAccountIdByCaip - Optional map of request CAIP-10 account IDs\n * to internal MetaMask account IDs.\n * @returns DeFi positions keyed by internal account ID.\n */\nexport function groupDeFiPositionsV6(\n response: V6BalancesResponse,\n internalAccountIdByCaip?: Map<string, string>,\n): DeFiPositionsByAccount {\n const internalAccountIdByMatchKey = internalAccountIdByCaip\n ? new Map(\n [...internalAccountIdByCaip].map(([caipAccountId, internalId]) => [\n toAccountMatchKey(caipAccountId),\n internalId,\n ]),\n )\n : undefined;\n\n // Accumulate groups per resolved internal account ID. The v6 response returns\n // a separate entry per chain (e.g. `eip155:1:<addr>`, `eip155:137:<addr>`),\n // and several of them can resolve to the same internal account ID, so we must\n // merge across all of them rather than overwrite per response account.\n const groupsByAccountKey = new Map<\n string,\n Map<string, DeFiProtocolPositionGroup>\n >();\n\n for (const account of response.accounts) {\n const accountId = internalAccountIdByMatchKey\n ? internalAccountIdByMatchKey.get(toAccountMatchKey(account.accountId))\n : account.accountId;\n if (accountId === undefined) {\n continue;\n }\n\n // Seed every queried account so accounts that no longer hold positions\n // overwrite (clear) any previously stored data.\n let groupsByKey = groupsByAccountKey.get(accountId);\n if (!groupsByKey) {\n groupsByKey = new Map<string, DeFiProtocolPositionGroup>();\n groupsByAccountKey.set(accountId, groupsByKey);\n }\n\n for (const balance of account.balances) {\n if (!isDefiBalanceWithMetadata(balance)) {\n continue;\n }\n\n const position = toUnderlyingPosition(balance);\n const { protocolId, productName, protocolIconUrl } = balance.metadata;\n const groupKey = `${position.chainId}#${protocolId}`;\n\n let group = groupsByKey.get(groupKey);\n if (!group) {\n group = {\n protocolId,\n productName,\n protocolIconUrl,\n chainId: position.chainId,\n marketValue: 0,\n iconGroup: [],\n sections: [],\n };\n groupsByKey.set(groupKey, group);\n }\n\n if (position.marketValue !== undefined) {\n group.marketValue +=\n position.marketValue * getMarketValueSign(position.positionType);\n }\n\n if (!group.iconGroup.some((item) => item.symbol === position.symbol)) {\n group.iconGroup.push({\n symbol: position.symbol,\n avatarValue: position.tokenImage,\n });\n }\n\n // Sections are keyed by productName; distinct groupIds under the same\n // product remain available on each underlying position.\n let section = group.sections.find(\n (item) => item.productName === productName,\n );\n if (!section) {\n section = { productName, positions: [] };\n group.sections.push(section);\n }\n section.positions.push(position);\n }\n }\n\n const result: DeFiPositionsByAccount = {};\n for (const [accountId, groupsByKey] of groupsByAccountKey) {\n const groups = [...groupsByKey.values()];\n for (const group of groups) {\n orderIconGroup(group.iconGroup);\n }\n result[accountId] = groups;\n }\n\n return result;\n}\n"]}
1
+ {"version":3,"file":"group-defi-positions-v6.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"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","sourcesContent":["import { V6_DEFI_POSITION_TYPES } from '@metamask/core-backend';\nimport type {\n V6BalanceItem,\n V6BalanceMetadata,\n V6BalancesResponse,\n V6DeFiPositionType,\n} from '@metamask/core-backend';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n} from '@metamask/utils';\nimport {\n KnownCaipNamespace,\n parseCaipAccountId,\n parseCaipAssetType,\n parseCaipChainId,\n} from '@metamask/utils';\n\n/** Static.cx host used to build CAIP-19 token icon URLs for DeFi positions. */\nconst STATIC_METAMASK_BASE_URL = 'https://static.cx.metamask.io';\n\n/**\n * Possible `positionType` values from Accounts API v6 DeFi metadata.\n * Re-export of {@link V6_DEFI_POSITION_TYPES} from `@metamask/core-backend`.\n */\nexport const DEFI_POSITION_TYPES = V6_DEFI_POSITION_TYPES;\n\n/**\n * The specific module or functionality within a DeFi protocol where a position\n * is held. Alias of {@link V6DeFiPositionType} from `@metamask/core-backend`.\n */\nexport type DeFiPositionType = V6DeFiPositionType;\n\n/**\n * Position types whose fiat value is a liability and is subtracted from the\n * protocol group's aggregated `marketValue`.\n */\nexport const DEFI_POSITION_LIABILITY_TYPES: ReadonlySet<DeFiPositionType> =\n new Set<DeFiPositionType>(['lending']);\n\n/**\n * An icon-group entry shown next to a protocol in the DeFi tab list.\n */\nexport type DeFiPositionIconGroupItem = {\n /** Token icon URL, when one can be built for the asset. */\n avatarValue?: string;\n symbol: string;\n};\n\n/**\n * A single underlying position row shown on the DeFi details page.\n */\nexport type DeFiUnderlyingPosition = {\n assetId: CaipAssetType;\n chainId: CaipChainId;\n symbol: string;\n name: string;\n decimals: number;\n /** Raw balance string as returned by the API. */\n balance: string;\n /** Fiat market value in the requested currency, when a price is available. */\n marketValue?: number;\n /** Position type from protocol metadata. */\n positionType: DeFiPositionType;\n /** Address of the pool this position belongs to. */\n poolAddress: string;\n /**\n * Upstream grouping id from the API. Rows that share a `productName` can\n * still carry distinct `groupId`s (e.g. multiple Pendle YT markets).\n */\n groupId: string;\n /** Token icon URL, when one can be built for the asset. */\n tokenImage?: string;\n};\n\n/**\n * A section of the details page, grouping positions that share the same\n * API `productName`. A single `protocolId` can have multiple products\n * (different pools/markets under one protocol), so a group may contain\n * several sections.\n */\nexport type DeFiPositionDetailsSection = {\n /** Section label from the API (`metadata.productName`). */\n productName: string;\n positions: DeFiUnderlyingPosition[];\n};\n\n/**\n * One row in the DeFi tab list (a protocol on a given chain), with the details\n * needed to render the details page embedded directly inside it.\n */\nexport type DeFiProtocolPositionGroup = {\n protocolId: string;\n /**\n * Product name from the first position seen for this protocol. Prefer\n * `protocolId` for the list-row title; use section `productName`s for\n * per-product detail headings.\n */\n productName: string;\n protocolIconUrl: string;\n chainId: CaipChainId;\n /**\n * Aggregated fiat market value across all positions in the group.\n * `lending` positions are subtracted; all other types are added.\n */\n marketValue: number;\n /** Icon-group entries for the list row. */\n iconGroup: DeFiPositionIconGroupItem[];\n /**\n * Detail sections consumed by the details page, one per distinct API\n * `productName` under this `protocolId`.\n */\n sections: DeFiPositionDetailsSection[];\n};\n\n/**\n * DeFi positions for every queried account, keyed by the internal MetaMask\n * account ID (`InternalAccount.id` UUID), the same key AssetsController uses.\n * Each account maps to a flat list of protocol groups; filter by each group's\n * `chainId` rather than digging through a nested chain map.\n */\nexport type DeFiPositionsByAccount = {\n [accountId: string]: DeFiProtocolPositionGroup[];\n};\n\n// Prefer ETH/WETH first in the list-row icon stack when a protocol has multiple\n// underlyings. Display-only.\nconst SYMBOL_PRIORITY = ['ETH', 'WETH'];\n\ntype DefiBalanceWithMetadata = V6BalanceItem & { metadata: V6BalanceMetadata };\n\n/**\n * Builds a static token icon URL for a CAIP asset ID.\n *\n * @param assetId - The CAIP-19 asset ID.\n * @returns The token icon URL, or `undefined` when it cannot be built.\n */\nfunction getDefiTokenImageUrl(assetId: CaipAssetType): string | undefined {\n try {\n const { chainId } = parseCaipAssetType(assetId);\n const { namespace } = parseCaipChainId(chainId);\n const isEvm = namespace === KnownCaipNamespace.Eip155;\n const normalizedAssetId = (isEvm ? assetId.toLowerCase() : assetId).replace(\n /:/gu,\n '/',\n );\n\n return `${STATIC_METAMASK_BASE_URL}/api/v2/tokenIcons/assets/${normalizedAssetId}.png`;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns whether a balance row is a DeFi position carrying protocol metadata.\n *\n * @param balance - A balance row from the v6 API.\n * @returns True when the row is a `category: defi` row with protocol metadata.\n */\nfunction isDefiBalanceWithMetadata(\n balance: V6BalanceItem,\n): balance is DefiBalanceWithMetadata {\n return (\n balance.category === 'defi' &&\n balance.metadata !== undefined &&\n (balance.metadata as Partial<V6BalanceMetadata>).protocolId !== undefined\n );\n}\n\n/**\n * Returns the fiat market value for a v6 DeFi balance row.\n *\n * @param balance - A balance row from the v6 API.\n * @returns The fiat value, or `undefined` when price is missing or the\n * balance/price is invalid.\n */\nfunction getMarketValue(balance: V6BalanceItem): number | undefined {\n if (balance.price === undefined) {\n return undefined;\n }\n\n const normalizedBalance = Number.parseFloat(balance.balance);\n const price = Number.parseFloat(balance.price);\n\n if (!Number.isFinite(normalizedBalance) || !Number.isFinite(price)) {\n return undefined;\n }\n\n return normalizedBalance * price;\n}\n\n/**\n * Returns the sign used when rolling a position's market value into a protocol\n * group total. Liability types (currently `lending`) subtract; all others add.\n *\n * @param positionType - The position's protocol module type.\n * @returns `-1` for liabilities, `1` otherwise.\n */\nfunction getMarketValueSign(positionType: DeFiPositionType): 1 | -1 {\n return DEFI_POSITION_LIABILITY_TYPES.has(positionType) ? -1 : 1;\n}\n\n/**\n * Moves a priority symbol (ETH/WETH) to the front of the icon group, in place.\n *\n * @param iconGroup - The icon-group entries to reorder.\n */\nfunction orderIconGroup(iconGroup: DeFiPositionIconGroupItem[]): void {\n const priorityIndex = iconGroup.findIndex((item) =>\n SYMBOL_PRIORITY.includes(item.symbol),\n );\n\n if (priorityIndex > 0) {\n const [priorityIcon] = iconGroup.splice(priorityIndex, 1);\n iconGroup.unshift(priorityIcon);\n }\n}\n\n/**\n * Maps a DeFi balance row to a details-page underlying position.\n *\n * @param balance - A DeFi balance row with protocol metadata.\n * @returns The underlying position for the details page.\n */\nfunction toUnderlyingPosition(\n balance: DefiBalanceWithMetadata,\n): DeFiUnderlyingPosition {\n const assetId = balance.assetId as CaipAssetType;\n const { chainId } = parseCaipAssetType(assetId);\n const { positionType, poolAddress, groupId } = balance.metadata;\n\n return {\n assetId,\n chainId,\n symbol: balance.symbol,\n name: balance.name,\n balance: balance.balance,\n decimals: balance.decimals,\n marketValue: getMarketValue(balance),\n positionType,\n poolAddress,\n groupId,\n tokenImage: getDefiTokenImageUrl(assetId),\n };\n}\n\n/**\n * Builds a chain-reference-agnostic key (`namespace:address`) for matching the\n * CAIP-10 account IDs we request against the ones the v6 API echoes back.\n *\n * We request EVM balances with the all-chains reference (`eip155:0:<address>`),\n * but the response echoes a separate per-chain ID for every chain\n * (`eip155:1:<address>`, `eip155:137:<address>`, ...). Matching on the full\n * CAIP-10 string therefore fails, so we drop the reference and match on\n * namespace + address instead. EVM addresses are lowercased; other namespaces\n * keep their case.\n *\n * @param caipAccountId - A CAIP-10 account ID.\n * @returns The match key, or a case-normalized fallback if parsing fails.\n */\nfunction toAccountMatchKey(caipAccountId: string): string {\n try {\n const {\n chain: { namespace },\n address,\n } = parseCaipAccountId(caipAccountId as CaipAccountId);\n const normalizedAddress =\n namespace === KnownCaipNamespace.Eip155 ? address.toLowerCase() : address;\n return `${namespace}:${normalizedAddress}`;\n } catch {\n return caipAccountId.startsWith(`${KnownCaipNamespace.Eip155}:`)\n ? caipAccountId.toLowerCase()\n : caipAccountId;\n }\n}\n\n/**\n * Transforms a v6 multiaccount balances response into the stored DeFi state:\n * positions keyed by internal account ID, each mapping to a flat list of\n * protocol groups. Every group carries its own `chainId` (so the client can\n * filter without a nested chain map) plus both the DeFi-tab summary and the\n * details-page sections. Accounts present in the response but with no DeFi\n * positions are included with an empty list so stale data is cleared.\n *\n * When `internalAccountIdByCaip` is provided, response account IDs are matched\n * to internal MetaMask account IDs via namespace + address (ignoring chain\n * reference and EVM case). Unmatched accounts are skipped. When omitted, the\n * response account ID is used as-is (handy for unit tests).\n *\n * @param response - The v6 multiaccount balances response.\n * @param internalAccountIdByCaip - Optional map of request CAIP-10 account IDs\n * to internal MetaMask account IDs.\n * @returns DeFi positions keyed by internal account ID.\n */\nexport function groupDeFiPositionsV6(\n response: V6BalancesResponse,\n internalAccountIdByCaip?: Map<string, string>,\n): DeFiPositionsByAccount {\n const internalAccountIdByMatchKey = internalAccountIdByCaip\n ? new Map(\n [...internalAccountIdByCaip].map(([caipAccountId, internalId]) => [\n toAccountMatchKey(caipAccountId),\n internalId,\n ]),\n )\n : undefined;\n\n // Accumulate groups per resolved internal account ID. The v6 response returns\n // a separate entry per chain (e.g. `eip155:1:<addr>`, `eip155:137:<addr>`),\n // and several of them can resolve to the same internal account ID, so we must\n // merge across all of them rather than overwrite per response account.\n const groupsByAccountKey = new Map<\n string,\n Map<string, DeFiProtocolPositionGroup>\n >();\n\n for (const account of response.accounts) {\n const accountId = internalAccountIdByMatchKey\n ? internalAccountIdByMatchKey.get(toAccountMatchKey(account.accountId))\n : account.accountId;\n if (accountId === undefined) {\n continue;\n }\n\n // Seed every queried account so accounts that no longer hold positions\n // overwrite (clear) any previously stored data.\n let groupsByKey = groupsByAccountKey.get(accountId);\n if (!groupsByKey) {\n groupsByKey = new Map<string, DeFiProtocolPositionGroup>();\n groupsByAccountKey.set(accountId, groupsByKey);\n }\n\n for (const balance of account.balances) {\n if (!isDefiBalanceWithMetadata(balance)) {\n continue;\n }\n\n const position = toUnderlyingPosition(balance);\n const { protocolId, productName, protocolIconUrl } = balance.metadata;\n const groupKey = `${position.chainId}#${protocolId}`;\n\n let group = groupsByKey.get(groupKey);\n if (!group) {\n group = {\n protocolId,\n productName,\n protocolIconUrl,\n chainId: position.chainId,\n marketValue: 0,\n iconGroup: [],\n sections: [],\n };\n groupsByKey.set(groupKey, group);\n }\n\n if (position.marketValue !== undefined) {\n group.marketValue +=\n position.marketValue * getMarketValueSign(position.positionType);\n }\n\n if (!group.iconGroup.some((item) => item.symbol === position.symbol)) {\n group.iconGroup.push({\n symbol: position.symbol,\n avatarValue: position.tokenImage,\n });\n }\n\n // Sections are keyed by productName; distinct groupIds under the same\n // product remain available on each underlying position.\n let section = group.sections.find(\n (item) => item.productName === productName,\n );\n if (!section) {\n section = { productName, positions: [] };\n group.sections.push(section);\n }\n section.positions.push(position);\n }\n }\n\n const result: DeFiPositionsByAccount = {};\n for (const [accountId, groupsByKey] of groupsByAccountKey) {\n const groups = [...groupsByKey.values()];\n for (const group of groups) {\n orderIconGroup(group.iconGroup);\n }\n result[accountId] = groups;\n }\n\n return result;\n}\n"]}
@@ -4,7 +4,7 @@ import type { CaipAssetType, CaipChainId } from "@metamask/utils";
4
4
  * Possible `positionType` values from Accounts API v6 DeFi metadata.
5
5
  * Re-export of {@link V6_DEFI_POSITION_TYPES} from `@metamask/core-backend`.
6
6
  */
7
- export declare const DEFI_POSITION_TYPES: readonly ["deposit", "loan", "locked", "staked", "reward", "wallet", "investment"];
7
+ export declare const DEFI_POSITION_TYPES: readonly ["deposit", "lending", "yield", "liquidity_pool", "staked", "leveraged_farming", "nft_staked", "farming", "locked", "vesting", "rewards", "investment"];
8
8
  /**
9
9
  * The specific module or functionality within a DeFi protocol where a position
10
10
  * is held. Alias of {@link V6DeFiPositionType} from `@metamask/core-backend`.
@@ -1 +1 @@
1
- {"version":3,"file":"group-defi-positions-v6.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,kBAAkB,EAClB,kBAAkB,EACnB,+BAA+B;AAChC,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACZ,wBAAwB;AAWzB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,oFAAyB,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,WAAW,CAAC,gBAAgB,CACnC,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,sBAAsB,EAAE,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,yBAAyB,EAAE,CAAC;IACvC;;;OAGG;IACH,QAAQ,EAAE,0BAA0B,EAAE,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,EAAE,CAAC;CAClD,CAAC;AAyJF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,kBAAkB,EAC5B,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5C,sBAAsB,CA6FxB"}
1
+ {"version":3,"file":"group-defi-positions-v6.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,kBAAkB,EAClB,kBAAkB,EACnB,+BAA+B;AAChC,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACZ,wBAAwB;AAWzB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kKAAyB,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,WAAW,CAAC,gBAAgB,CAChC,CAAC;AAEzC;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,sBAAsB,EAAE,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,yBAAyB,EAAE,CAAC;IACvC;;;OAGG;IACH,QAAQ,EAAE,0BAA0B,EAAE,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,EAAE,CAAC;CAClD,CAAC;AAyJF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,kBAAkB,EAC5B,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5C,sBAAsB,CA6FxB"}
@@ -4,7 +4,7 @@ import type { CaipAssetType, CaipChainId } from "@metamask/utils";
4
4
  * Possible `positionType` values from Accounts API v6 DeFi metadata.
5
5
  * Re-export of {@link V6_DEFI_POSITION_TYPES} from `@metamask/core-backend`.
6
6
  */
7
- export declare const DEFI_POSITION_TYPES: readonly ["deposit", "loan", "locked", "staked", "reward", "wallet", "investment"];
7
+ export declare const DEFI_POSITION_TYPES: readonly ["deposit", "lending", "yield", "liquidity_pool", "staked", "leveraged_farming", "nft_staked", "farming", "locked", "vesting", "rewards", "investment"];
8
8
  /**
9
9
  * The specific module or functionality within a DeFi protocol where a position
10
10
  * is held. Alias of {@link V6DeFiPositionType} from `@metamask/core-backend`.
@@ -1 +1 @@
1
- {"version":3,"file":"group-defi-positions-v6.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,kBAAkB,EAClB,kBAAkB,EACnB,+BAA+B;AAChC,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACZ,wBAAwB;AAWzB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,oFAAyB,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,WAAW,CAAC,gBAAgB,CACnC,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,sBAAsB,EAAE,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,yBAAyB,EAAE,CAAC;IACvC;;;OAGG;IACH,QAAQ,EAAE,0BAA0B,EAAE,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,EAAE,CAAC;CAClD,CAAC;AAyJF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,kBAAkB,EAC5B,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5C,sBAAsB,CA6FxB"}
1
+ {"version":3,"file":"group-defi-positions-v6.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,kBAAkB,EAClB,kBAAkB,EACnB,+BAA+B;AAChC,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACZ,wBAAwB;AAWzB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kKAAyB,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,WAAW,CAAC,gBAAgB,CAChC,CAAC;AAEzC;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,sBAAsB,EAAE,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,yBAAyB,EAAE,CAAC;IACvC;;;OAGG;IACH,QAAQ,EAAE,0BAA0B,EAAE,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,EAAE,CAAC;CAClD,CAAC;AAyJF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,kBAAkB,EAC5B,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5C,sBAAsB,CA6FxB"}
@@ -11,7 +11,7 @@ export const DEFI_POSITION_TYPES = V6_DEFI_POSITION_TYPES;
11
11
  * Position types whose fiat value is a liability and is subtracted from the
12
12
  * protocol group's aggregated `marketValue`.
13
13
  */
14
- export const DEFI_POSITION_LIABILITY_TYPES = new Set(['loan']);
14
+ export const DEFI_POSITION_LIABILITY_TYPES = new Set(['lending']);
15
15
  // Prefer ETH/WETH first in the list-row icon stack when a protocol has multiple
16
16
  // underlyings. Display-only.
17
17
  const SYMBOL_PRIORITY = ['ETH', 'WETH'];
@@ -1 +1 @@
1
- {"version":3,"file":"group-defi-positions-v6.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,+BAA+B;AAYhE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EACjB,wBAAwB;AAEzB,+EAA+E;AAC/E,MAAM,wBAAwB,GAAG,+BAA+B,CAAC;AAEjE;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,sBAAsB,CAAC;AAQ1D;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,IAAI,GAAG,CAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;AAuFtC,gFAAgF;AAChF,6BAA6B;AAC7B,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAIxC;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,OAAsB;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,SAAS,KAAK,kBAAkB,CAAC,MAAM,CAAC;QACtD,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CACzE,KAAK,EACL,GAAG,CACJ,CAAC;QAEF,OAAO,GAAG,wBAAwB,6BAA6B,iBAAiB,MAAM,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,yBAAyB,CAChC,OAAsB;IAEtB,OAAO,CACL,OAAO,CAAC,QAAQ,KAAK,MAAM;QAC3B,OAAO,CAAC,QAAQ,KAAK,SAAS;QAC7B,OAAO,CAAC,QAAuC,CAAC,UAAU,KAAK,SAAS,CAC1E,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,OAAsB;IAC5C,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACnE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,iBAAiB,GAAG,KAAK,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,YAA8B;IACxD,OAAO,6BAA6B,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,SAAsC;IAC5D,MAAM,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CACjD,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CACtC,CAAC;IAEF,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QAC1D,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAC3B,OAAgC;IAEhC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAwB,CAAC;IACjD,MAAM,EAAE,OAAO,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAEhE,OAAO;QACL,OAAO;QACP,OAAO;QACP,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC;QACpC,YAAY;QACZ,WAAW;QACX,OAAO;QACP,UAAU,EAAE,oBAAoB,CAAC,OAAO,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,iBAAiB,CAAC,aAAqB;IAC9C,IAAI,CAAC;QACH,MAAM,EACJ,KAAK,EAAE,EAAE,SAAS,EAAE,EACpB,OAAO,GACR,GAAG,kBAAkB,CAAC,aAA8B,CAAC,CAAC;QACvD,MAAM,iBAAiB,GACrB,SAAS,KAAK,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5E,OAAO,GAAG,SAAS,IAAI,iBAAiB,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,CAAC,UAAU,CAAC,GAAG,kBAAkB,CAAC,MAAM,GAAG,CAAC;YAC9D,CAAC,CAAC,aAAa,CAAC,WAAW,EAAE;YAC7B,CAAC,CAAC,aAAa,CAAC;IACpB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAA4B,EAC5B,uBAA6C;IAE7C,MAAM,2BAA2B,GAAG,uBAAuB;QACzD,CAAC,CAAC,IAAI,GAAG,CACL,CAAC,GAAG,uBAAuB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;YAChE,iBAAiB,CAAC,aAAa,CAAC;YAChC,UAAU;SACX,CAAC,CACH;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,8EAA8E;IAC9E,4EAA4E;IAC5E,8EAA8E;IAC9E,uEAAuE;IACvE,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAG/B,CAAC;IAEJ,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACxC,MAAM,SAAS,GAAG,2BAA2B;YAC3C,CAAC,CAAC,2BAA2B,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QACtB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,SAAS;QACX,CAAC;QAED,uEAAuE;QACvE,gDAAgD;QAChD,IAAI,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,WAAW,GAAG,IAAI,GAAG,EAAqC,CAAC;YAC3D,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACjD,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC;gBACxC,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;YACtE,MAAM,QAAQ,GAAG,GAAG,QAAQ,CAAC,OAAO,IAAI,UAAU,EAAE,CAAC;YAErD,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG;oBACN,UAAU;oBACV,WAAW;oBACX,eAAe;oBACf,OAAO,EAAE,QAAQ,CAAC,OAAO;oBACzB,WAAW,EAAE,CAAC;oBACd,SAAS,EAAE,EAAE;oBACb,QAAQ,EAAE,EAAE;iBACb,CAAC;gBACF,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;YAED,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBACvC,KAAK,CAAC,WAAW;oBACf,QAAQ,CAAC,WAAW,GAAG,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YACrE,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC;oBACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,WAAW,EAAE,QAAQ,CAAC,UAAU;iBACjC,CAAC,CAAC;YACL,CAAC;YAED,sEAAsE;YACtE,wDAAwD;YACxD,IAAI,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAC/B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,KAAK,WAAW,CAC3C,CAAC;YACF,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;gBACzC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,kBAAkB,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;IAC7B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { V6_DEFI_POSITION_TYPES } from '@metamask/core-backend';\nimport type {\n V6BalanceItem,\n V6BalanceMetadata,\n V6BalancesResponse,\n V6DeFiPositionType,\n} from '@metamask/core-backend';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n} from '@metamask/utils';\nimport {\n KnownCaipNamespace,\n parseCaipAccountId,\n parseCaipAssetType,\n parseCaipChainId,\n} from '@metamask/utils';\n\n/** Static.cx host used to build CAIP-19 token icon URLs for DeFi positions. */\nconst STATIC_METAMASK_BASE_URL = 'https://static.cx.metamask.io';\n\n/**\n * Possible `positionType` values from Accounts API v6 DeFi metadata.\n * Re-export of {@link V6_DEFI_POSITION_TYPES} from `@metamask/core-backend`.\n */\nexport const DEFI_POSITION_TYPES = V6_DEFI_POSITION_TYPES;\n\n/**\n * The specific module or functionality within a DeFi protocol where a position\n * is held. Alias of {@link V6DeFiPositionType} from `@metamask/core-backend`.\n */\nexport type DeFiPositionType = V6DeFiPositionType;\n\n/**\n * Position types whose fiat value is a liability and is subtracted from the\n * protocol group's aggregated `marketValue`.\n */\nexport const DEFI_POSITION_LIABILITY_TYPES: ReadonlySet<DeFiPositionType> =\n new Set<DeFiPositionType>(['loan']);\n\n/**\n * An icon-group entry shown next to a protocol in the DeFi tab list.\n */\nexport type DeFiPositionIconGroupItem = {\n /** Token icon URL, when one can be built for the asset. */\n avatarValue?: string;\n symbol: string;\n};\n\n/**\n * A single underlying position row shown on the DeFi details page.\n */\nexport type DeFiUnderlyingPosition = {\n assetId: CaipAssetType;\n chainId: CaipChainId;\n symbol: string;\n name: string;\n decimals: number;\n /** Raw balance string as returned by the API. */\n balance: string;\n /** Fiat market value in the requested currency, when a price is available. */\n marketValue?: number;\n /** Position type from protocol metadata. */\n positionType: DeFiPositionType;\n /** Address of the pool this position belongs to. */\n poolAddress: string;\n /**\n * Upstream grouping id from the API. Rows that share a `productName` can\n * still carry distinct `groupId`s (e.g. multiple Pendle YT markets).\n */\n groupId: string;\n /** Token icon URL, when one can be built for the asset. */\n tokenImage?: string;\n};\n\n/**\n * A section of the details page, grouping positions that share the same\n * API `productName`. A single `protocolId` can have multiple products\n * (different pools/markets under one protocol), so a group may contain\n * several sections.\n */\nexport type DeFiPositionDetailsSection = {\n /** Section label from the API (`metadata.productName`). */\n productName: string;\n positions: DeFiUnderlyingPosition[];\n};\n\n/**\n * One row in the DeFi tab list (a protocol on a given chain), with the details\n * needed to render the details page embedded directly inside it.\n */\nexport type DeFiProtocolPositionGroup = {\n protocolId: string;\n /**\n * Product name from the first position seen for this protocol. Prefer\n * `protocolId` for the list-row title; use section `productName`s for\n * per-product detail headings.\n */\n productName: string;\n protocolIconUrl: string;\n chainId: CaipChainId;\n /**\n * Aggregated fiat market value across all positions in the group.\n * `lending` positions are subtracted; all other types are added.\n */\n marketValue: number;\n /** Icon-group entries for the list row. */\n iconGroup: DeFiPositionIconGroupItem[];\n /**\n * Detail sections consumed by the details page, one per distinct API\n * `productName` under this `protocolId`.\n */\n sections: DeFiPositionDetailsSection[];\n};\n\n/**\n * DeFi positions for every queried account, keyed by the internal MetaMask\n * account ID (`InternalAccount.id` UUID), the same key AssetsController uses.\n * Each account maps to a flat list of protocol groups; filter by each group's\n * `chainId` rather than digging through a nested chain map.\n */\nexport type DeFiPositionsByAccount = {\n [accountId: string]: DeFiProtocolPositionGroup[];\n};\n\n// Prefer ETH/WETH first in the list-row icon stack when a protocol has multiple\n// underlyings. Display-only.\nconst SYMBOL_PRIORITY = ['ETH', 'WETH'];\n\ntype DefiBalanceWithMetadata = V6BalanceItem & { metadata: V6BalanceMetadata };\n\n/**\n * Builds a static token icon URL for a CAIP asset ID.\n *\n * @param assetId - The CAIP-19 asset ID.\n * @returns The token icon URL, or `undefined` when it cannot be built.\n */\nfunction getDefiTokenImageUrl(assetId: CaipAssetType): string | undefined {\n try {\n const { chainId } = parseCaipAssetType(assetId);\n const { namespace } = parseCaipChainId(chainId);\n const isEvm = namespace === KnownCaipNamespace.Eip155;\n const normalizedAssetId = (isEvm ? assetId.toLowerCase() : assetId).replace(\n /:/gu,\n '/',\n );\n\n return `${STATIC_METAMASK_BASE_URL}/api/v2/tokenIcons/assets/${normalizedAssetId}.png`;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns whether a balance row is a DeFi position carrying protocol metadata.\n *\n * @param balance - A balance row from the v6 API.\n * @returns True when the row is a `category: defi` row with protocol metadata.\n */\nfunction isDefiBalanceWithMetadata(\n balance: V6BalanceItem,\n): balance is DefiBalanceWithMetadata {\n return (\n balance.category === 'defi' &&\n balance.metadata !== undefined &&\n (balance.metadata as Partial<V6BalanceMetadata>).protocolId !== undefined\n );\n}\n\n/**\n * Returns the fiat market value for a v6 DeFi balance row.\n *\n * @param balance - A balance row from the v6 API.\n * @returns The fiat value, or `undefined` when price is missing or the\n * balance/price is invalid.\n */\nfunction getMarketValue(balance: V6BalanceItem): number | undefined {\n if (balance.price === undefined) {\n return undefined;\n }\n\n const normalizedBalance = Number.parseFloat(balance.balance);\n const price = Number.parseFloat(balance.price);\n\n if (!Number.isFinite(normalizedBalance) || !Number.isFinite(price)) {\n return undefined;\n }\n\n return normalizedBalance * price;\n}\n\n/**\n * Returns the sign used when rolling a position's market value into a protocol\n * group total. Liability types (currently `lending`) subtract; all others add.\n *\n * @param positionType - The position's protocol module type.\n * @returns `-1` for liabilities, `1` otherwise.\n */\nfunction getMarketValueSign(positionType: DeFiPositionType): 1 | -1 {\n return DEFI_POSITION_LIABILITY_TYPES.has(positionType) ? -1 : 1;\n}\n\n/**\n * Moves a priority symbol (ETH/WETH) to the front of the icon group, in place.\n *\n * @param iconGroup - The icon-group entries to reorder.\n */\nfunction orderIconGroup(iconGroup: DeFiPositionIconGroupItem[]): void {\n const priorityIndex = iconGroup.findIndex((item) =>\n SYMBOL_PRIORITY.includes(item.symbol),\n );\n\n if (priorityIndex > 0) {\n const [priorityIcon] = iconGroup.splice(priorityIndex, 1);\n iconGroup.unshift(priorityIcon);\n }\n}\n\n/**\n * Maps a DeFi balance row to a details-page underlying position.\n *\n * @param balance - A DeFi balance row with protocol metadata.\n * @returns The underlying position for the details page.\n */\nfunction toUnderlyingPosition(\n balance: DefiBalanceWithMetadata,\n): DeFiUnderlyingPosition {\n const assetId = balance.assetId as CaipAssetType;\n const { chainId } = parseCaipAssetType(assetId);\n const { positionType, poolAddress, groupId } = balance.metadata;\n\n return {\n assetId,\n chainId,\n symbol: balance.symbol,\n name: balance.name,\n balance: balance.balance,\n decimals: balance.decimals,\n marketValue: getMarketValue(balance),\n positionType,\n poolAddress,\n groupId,\n tokenImage: getDefiTokenImageUrl(assetId),\n };\n}\n\n/**\n * Builds a chain-reference-agnostic key (`namespace:address`) for matching the\n * CAIP-10 account IDs we request against the ones the v6 API echoes back.\n *\n * We request EVM balances with the all-chains reference (`eip155:0:<address>`),\n * but the response echoes a separate per-chain ID for every chain\n * (`eip155:1:<address>`, `eip155:137:<address>`, ...). Matching on the full\n * CAIP-10 string therefore fails, so we drop the reference and match on\n * namespace + address instead. EVM addresses are lowercased; other namespaces\n * keep their case.\n *\n * @param caipAccountId - A CAIP-10 account ID.\n * @returns The match key, or a case-normalized fallback if parsing fails.\n */\nfunction toAccountMatchKey(caipAccountId: string): string {\n try {\n const {\n chain: { namespace },\n address,\n } = parseCaipAccountId(caipAccountId as CaipAccountId);\n const normalizedAddress =\n namespace === KnownCaipNamespace.Eip155 ? address.toLowerCase() : address;\n return `${namespace}:${normalizedAddress}`;\n } catch {\n return caipAccountId.startsWith(`${KnownCaipNamespace.Eip155}:`)\n ? caipAccountId.toLowerCase()\n : caipAccountId;\n }\n}\n\n/**\n * Transforms a v6 multiaccount balances response into the stored DeFi state:\n * positions keyed by internal account ID, each mapping to a flat list of\n * protocol groups. Every group carries its own `chainId` (so the client can\n * filter without a nested chain map) plus both the DeFi-tab summary and the\n * details-page sections. Accounts present in the response but with no DeFi\n * positions are included with an empty list so stale data is cleared.\n *\n * When `internalAccountIdByCaip` is provided, response account IDs are matched\n * to internal MetaMask account IDs via namespace + address (ignoring chain\n * reference and EVM case). Unmatched accounts are skipped. When omitted, the\n * response account ID is used as-is (handy for unit tests).\n *\n * @param response - The v6 multiaccount balances response.\n * @param internalAccountIdByCaip - Optional map of request CAIP-10 account IDs\n * to internal MetaMask account IDs.\n * @returns DeFi positions keyed by internal account ID.\n */\nexport function groupDeFiPositionsV6(\n response: V6BalancesResponse,\n internalAccountIdByCaip?: Map<string, string>,\n): DeFiPositionsByAccount {\n const internalAccountIdByMatchKey = internalAccountIdByCaip\n ? new Map(\n [...internalAccountIdByCaip].map(([caipAccountId, internalId]) => [\n toAccountMatchKey(caipAccountId),\n internalId,\n ]),\n )\n : undefined;\n\n // Accumulate groups per resolved internal account ID. The v6 response returns\n // a separate entry per chain (e.g. `eip155:1:<addr>`, `eip155:137:<addr>`),\n // and several of them can resolve to the same internal account ID, so we must\n // merge across all of them rather than overwrite per response account.\n const groupsByAccountKey = new Map<\n string,\n Map<string, DeFiProtocolPositionGroup>\n >();\n\n for (const account of response.accounts) {\n const accountId = internalAccountIdByMatchKey\n ? internalAccountIdByMatchKey.get(toAccountMatchKey(account.accountId))\n : account.accountId;\n if (accountId === undefined) {\n continue;\n }\n\n // Seed every queried account so accounts that no longer hold positions\n // overwrite (clear) any previously stored data.\n let groupsByKey = groupsByAccountKey.get(accountId);\n if (!groupsByKey) {\n groupsByKey = new Map<string, DeFiProtocolPositionGroup>();\n groupsByAccountKey.set(accountId, groupsByKey);\n }\n\n for (const balance of account.balances) {\n if (!isDefiBalanceWithMetadata(balance)) {\n continue;\n }\n\n const position = toUnderlyingPosition(balance);\n const { protocolId, productName, protocolIconUrl } = balance.metadata;\n const groupKey = `${position.chainId}#${protocolId}`;\n\n let group = groupsByKey.get(groupKey);\n if (!group) {\n group = {\n protocolId,\n productName,\n protocolIconUrl,\n chainId: position.chainId,\n marketValue: 0,\n iconGroup: [],\n sections: [],\n };\n groupsByKey.set(groupKey, group);\n }\n\n if (position.marketValue !== undefined) {\n group.marketValue +=\n position.marketValue * getMarketValueSign(position.positionType);\n }\n\n if (!group.iconGroup.some((item) => item.symbol === position.symbol)) {\n group.iconGroup.push({\n symbol: position.symbol,\n avatarValue: position.tokenImage,\n });\n }\n\n // Sections are keyed by productName; distinct groupIds under the same\n // product remain available on each underlying position.\n let section = group.sections.find(\n (item) => item.productName === productName,\n );\n if (!section) {\n section = { productName, positions: [] };\n group.sections.push(section);\n }\n section.positions.push(position);\n }\n }\n\n const result: DeFiPositionsByAccount = {};\n for (const [accountId, groupsByKey] of groupsByAccountKey) {\n const groups = [...groupsByKey.values()];\n for (const group of groups) {\n orderIconGroup(group.iconGroup);\n }\n result[accountId] = groups;\n }\n\n return result;\n}\n"]}
1
+ {"version":3,"file":"group-defi-positions-v6.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/group-defi-positions-v6.ts"],"names":[],"mappings":"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","sourcesContent":["import { V6_DEFI_POSITION_TYPES } from '@metamask/core-backend';\nimport type {\n V6BalanceItem,\n V6BalanceMetadata,\n V6BalancesResponse,\n V6DeFiPositionType,\n} from '@metamask/core-backend';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n} from '@metamask/utils';\nimport {\n KnownCaipNamespace,\n parseCaipAccountId,\n parseCaipAssetType,\n parseCaipChainId,\n} from '@metamask/utils';\n\n/** Static.cx host used to build CAIP-19 token icon URLs for DeFi positions. */\nconst STATIC_METAMASK_BASE_URL = 'https://static.cx.metamask.io';\n\n/**\n * Possible `positionType` values from Accounts API v6 DeFi metadata.\n * Re-export of {@link V6_DEFI_POSITION_TYPES} from `@metamask/core-backend`.\n */\nexport const DEFI_POSITION_TYPES = V6_DEFI_POSITION_TYPES;\n\n/**\n * The specific module or functionality within a DeFi protocol where a position\n * is held. Alias of {@link V6DeFiPositionType} from `@metamask/core-backend`.\n */\nexport type DeFiPositionType = V6DeFiPositionType;\n\n/**\n * Position types whose fiat value is a liability and is subtracted from the\n * protocol group's aggregated `marketValue`.\n */\nexport const DEFI_POSITION_LIABILITY_TYPES: ReadonlySet<DeFiPositionType> =\n new Set<DeFiPositionType>(['lending']);\n\n/**\n * An icon-group entry shown next to a protocol in the DeFi tab list.\n */\nexport type DeFiPositionIconGroupItem = {\n /** Token icon URL, when one can be built for the asset. */\n avatarValue?: string;\n symbol: string;\n};\n\n/**\n * A single underlying position row shown on the DeFi details page.\n */\nexport type DeFiUnderlyingPosition = {\n assetId: CaipAssetType;\n chainId: CaipChainId;\n symbol: string;\n name: string;\n decimals: number;\n /** Raw balance string as returned by the API. */\n balance: string;\n /** Fiat market value in the requested currency, when a price is available. */\n marketValue?: number;\n /** Position type from protocol metadata. */\n positionType: DeFiPositionType;\n /** Address of the pool this position belongs to. */\n poolAddress: string;\n /**\n * Upstream grouping id from the API. Rows that share a `productName` can\n * still carry distinct `groupId`s (e.g. multiple Pendle YT markets).\n */\n groupId: string;\n /** Token icon URL, when one can be built for the asset. */\n tokenImage?: string;\n};\n\n/**\n * A section of the details page, grouping positions that share the same\n * API `productName`. A single `protocolId` can have multiple products\n * (different pools/markets under one protocol), so a group may contain\n * several sections.\n */\nexport type DeFiPositionDetailsSection = {\n /** Section label from the API (`metadata.productName`). */\n productName: string;\n positions: DeFiUnderlyingPosition[];\n};\n\n/**\n * One row in the DeFi tab list (a protocol on a given chain), with the details\n * needed to render the details page embedded directly inside it.\n */\nexport type DeFiProtocolPositionGroup = {\n protocolId: string;\n /**\n * Product name from the first position seen for this protocol. Prefer\n * `protocolId` for the list-row title; use section `productName`s for\n * per-product detail headings.\n */\n productName: string;\n protocolIconUrl: string;\n chainId: CaipChainId;\n /**\n * Aggregated fiat market value across all positions in the group.\n * `lending` positions are subtracted; all other types are added.\n */\n marketValue: number;\n /** Icon-group entries for the list row. */\n iconGroup: DeFiPositionIconGroupItem[];\n /**\n * Detail sections consumed by the details page, one per distinct API\n * `productName` under this `protocolId`.\n */\n sections: DeFiPositionDetailsSection[];\n};\n\n/**\n * DeFi positions for every queried account, keyed by the internal MetaMask\n * account ID (`InternalAccount.id` UUID), the same key AssetsController uses.\n * Each account maps to a flat list of protocol groups; filter by each group's\n * `chainId` rather than digging through a nested chain map.\n */\nexport type DeFiPositionsByAccount = {\n [accountId: string]: DeFiProtocolPositionGroup[];\n};\n\n// Prefer ETH/WETH first in the list-row icon stack when a protocol has multiple\n// underlyings. Display-only.\nconst SYMBOL_PRIORITY = ['ETH', 'WETH'];\n\ntype DefiBalanceWithMetadata = V6BalanceItem & { metadata: V6BalanceMetadata };\n\n/**\n * Builds a static token icon URL for a CAIP asset ID.\n *\n * @param assetId - The CAIP-19 asset ID.\n * @returns The token icon URL, or `undefined` when it cannot be built.\n */\nfunction getDefiTokenImageUrl(assetId: CaipAssetType): string | undefined {\n try {\n const { chainId } = parseCaipAssetType(assetId);\n const { namespace } = parseCaipChainId(chainId);\n const isEvm = namespace === KnownCaipNamespace.Eip155;\n const normalizedAssetId = (isEvm ? assetId.toLowerCase() : assetId).replace(\n /:/gu,\n '/',\n );\n\n return `${STATIC_METAMASK_BASE_URL}/api/v2/tokenIcons/assets/${normalizedAssetId}.png`;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns whether a balance row is a DeFi position carrying protocol metadata.\n *\n * @param balance - A balance row from the v6 API.\n * @returns True when the row is a `category: defi` row with protocol metadata.\n */\nfunction isDefiBalanceWithMetadata(\n balance: V6BalanceItem,\n): balance is DefiBalanceWithMetadata {\n return (\n balance.category === 'defi' &&\n balance.metadata !== undefined &&\n (balance.metadata as Partial<V6BalanceMetadata>).protocolId !== undefined\n );\n}\n\n/**\n * Returns the fiat market value for a v6 DeFi balance row.\n *\n * @param balance - A balance row from the v6 API.\n * @returns The fiat value, or `undefined` when price is missing or the\n * balance/price is invalid.\n */\nfunction getMarketValue(balance: V6BalanceItem): number | undefined {\n if (balance.price === undefined) {\n return undefined;\n }\n\n const normalizedBalance = Number.parseFloat(balance.balance);\n const price = Number.parseFloat(balance.price);\n\n if (!Number.isFinite(normalizedBalance) || !Number.isFinite(price)) {\n return undefined;\n }\n\n return normalizedBalance * price;\n}\n\n/**\n * Returns the sign used when rolling a position's market value into a protocol\n * group total. Liability types (currently `lending`) subtract; all others add.\n *\n * @param positionType - The position's protocol module type.\n * @returns `-1` for liabilities, `1` otherwise.\n */\nfunction getMarketValueSign(positionType: DeFiPositionType): 1 | -1 {\n return DEFI_POSITION_LIABILITY_TYPES.has(positionType) ? -1 : 1;\n}\n\n/**\n * Moves a priority symbol (ETH/WETH) to the front of the icon group, in place.\n *\n * @param iconGroup - The icon-group entries to reorder.\n */\nfunction orderIconGroup(iconGroup: DeFiPositionIconGroupItem[]): void {\n const priorityIndex = iconGroup.findIndex((item) =>\n SYMBOL_PRIORITY.includes(item.symbol),\n );\n\n if (priorityIndex > 0) {\n const [priorityIcon] = iconGroup.splice(priorityIndex, 1);\n iconGroup.unshift(priorityIcon);\n }\n}\n\n/**\n * Maps a DeFi balance row to a details-page underlying position.\n *\n * @param balance - A DeFi balance row with protocol metadata.\n * @returns The underlying position for the details page.\n */\nfunction toUnderlyingPosition(\n balance: DefiBalanceWithMetadata,\n): DeFiUnderlyingPosition {\n const assetId = balance.assetId as CaipAssetType;\n const { chainId } = parseCaipAssetType(assetId);\n const { positionType, poolAddress, groupId } = balance.metadata;\n\n return {\n assetId,\n chainId,\n symbol: balance.symbol,\n name: balance.name,\n balance: balance.balance,\n decimals: balance.decimals,\n marketValue: getMarketValue(balance),\n positionType,\n poolAddress,\n groupId,\n tokenImage: getDefiTokenImageUrl(assetId),\n };\n}\n\n/**\n * Builds a chain-reference-agnostic key (`namespace:address`) for matching the\n * CAIP-10 account IDs we request against the ones the v6 API echoes back.\n *\n * We request EVM balances with the all-chains reference (`eip155:0:<address>`),\n * but the response echoes a separate per-chain ID for every chain\n * (`eip155:1:<address>`, `eip155:137:<address>`, ...). Matching on the full\n * CAIP-10 string therefore fails, so we drop the reference and match on\n * namespace + address instead. EVM addresses are lowercased; other namespaces\n * keep their case.\n *\n * @param caipAccountId - A CAIP-10 account ID.\n * @returns The match key, or a case-normalized fallback if parsing fails.\n */\nfunction toAccountMatchKey(caipAccountId: string): string {\n try {\n const {\n chain: { namespace },\n address,\n } = parseCaipAccountId(caipAccountId as CaipAccountId);\n const normalizedAddress =\n namespace === KnownCaipNamespace.Eip155 ? address.toLowerCase() : address;\n return `${namespace}:${normalizedAddress}`;\n } catch {\n return caipAccountId.startsWith(`${KnownCaipNamespace.Eip155}:`)\n ? caipAccountId.toLowerCase()\n : caipAccountId;\n }\n}\n\n/**\n * Transforms a v6 multiaccount balances response into the stored DeFi state:\n * positions keyed by internal account ID, each mapping to a flat list of\n * protocol groups. Every group carries its own `chainId` (so the client can\n * filter without a nested chain map) plus both the DeFi-tab summary and the\n * details-page sections. Accounts present in the response but with no DeFi\n * positions are included with an empty list so stale data is cleared.\n *\n * When `internalAccountIdByCaip` is provided, response account IDs are matched\n * to internal MetaMask account IDs via namespace + address (ignoring chain\n * reference and EVM case). Unmatched accounts are skipped. When omitted, the\n * response account ID is used as-is (handy for unit tests).\n *\n * @param response - The v6 multiaccount balances response.\n * @param internalAccountIdByCaip - Optional map of request CAIP-10 account IDs\n * to internal MetaMask account IDs.\n * @returns DeFi positions keyed by internal account ID.\n */\nexport function groupDeFiPositionsV6(\n response: V6BalancesResponse,\n internalAccountIdByCaip?: Map<string, string>,\n): DeFiPositionsByAccount {\n const internalAccountIdByMatchKey = internalAccountIdByCaip\n ? new Map(\n [...internalAccountIdByCaip].map(([caipAccountId, internalId]) => [\n toAccountMatchKey(caipAccountId),\n internalId,\n ]),\n )\n : undefined;\n\n // Accumulate groups per resolved internal account ID. The v6 response returns\n // a separate entry per chain (e.g. `eip155:1:<addr>`, `eip155:137:<addr>`),\n // and several of them can resolve to the same internal account ID, so we must\n // merge across all of them rather than overwrite per response account.\n const groupsByAccountKey = new Map<\n string,\n Map<string, DeFiProtocolPositionGroup>\n >();\n\n for (const account of response.accounts) {\n const accountId = internalAccountIdByMatchKey\n ? internalAccountIdByMatchKey.get(toAccountMatchKey(account.accountId))\n : account.accountId;\n if (accountId === undefined) {\n continue;\n }\n\n // Seed every queried account so accounts that no longer hold positions\n // overwrite (clear) any previously stored data.\n let groupsByKey = groupsByAccountKey.get(accountId);\n if (!groupsByKey) {\n groupsByKey = new Map<string, DeFiProtocolPositionGroup>();\n groupsByAccountKey.set(accountId, groupsByKey);\n }\n\n for (const balance of account.balances) {\n if (!isDefiBalanceWithMetadata(balance)) {\n continue;\n }\n\n const position = toUnderlyingPosition(balance);\n const { protocolId, productName, protocolIconUrl } = balance.metadata;\n const groupKey = `${position.chainId}#${protocolId}`;\n\n let group = groupsByKey.get(groupKey);\n if (!group) {\n group = {\n protocolId,\n productName,\n protocolIconUrl,\n chainId: position.chainId,\n marketValue: 0,\n iconGroup: [],\n sections: [],\n };\n groupsByKey.set(groupKey, group);\n }\n\n if (position.marketValue !== undefined) {\n group.marketValue +=\n position.marketValue * getMarketValueSign(position.positionType);\n }\n\n if (!group.iconGroup.some((item) => item.symbol === position.symbol)) {\n group.iconGroup.push({\n symbol: position.symbol,\n avatarValue: position.tokenImage,\n });\n }\n\n // Sections are keyed by productName; distinct groupIds under the same\n // product remain available on each underlying position.\n let section = group.sections.find(\n (item) => item.productName === productName,\n );\n if (!section) {\n section = { productName, positions: [] };\n group.sections.push(section);\n }\n section.positions.push(position);\n }\n }\n\n const result: DeFiPositionsByAccount = {};\n for (const [accountId, groupsByKey] of groupsByAccountKey) {\n const groups = [...groupsByKey.values()];\n for (const group of groups) {\n orderIconGroup(group.iconGroup);\n }\n result[accountId] = groups;\n }\n\n return result;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/assets-controllers",
3
- "version": "110.0.0-preview-855d28a",
3
+ "version": "110.0.0-preview-8c40ad69c",
4
4
  "description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
5
5
  "keywords": [
6
6
  "Ethereum",