@metamask/assets-controllers 32.0.0 → 33.0.0

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 (172) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/dist/AccountTrackerController.js +2 -2
  3. package/dist/AccountTrackerController.mjs +1 -1
  4. package/dist/AssetsContractController.js +6 -6
  5. package/dist/AssetsContractController.mjs +5 -5
  6. package/dist/CurrencyRateController.js +3 -3
  7. package/dist/CurrencyRateController.mjs +2 -2
  8. package/dist/NftController.js +3 -3
  9. package/dist/NftController.mjs +2 -2
  10. package/dist/NftDetectionController.js +2 -2
  11. package/dist/NftDetectionController.mjs +1 -1
  12. package/dist/RatesController/RatesController.js +3 -3
  13. package/dist/RatesController/RatesController.mjs +2 -2
  14. package/dist/RatesController/index.js +3 -3
  15. package/dist/RatesController/index.mjs +2 -2
  16. package/dist/Standards/ERC20Standard.js +3 -3
  17. package/dist/Standards/ERC20Standard.mjs +2 -2
  18. package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.js +3 -3
  19. package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.mjs +2 -2
  20. package/dist/Standards/NftStandards/ERC721/ERC721Standard.js +3 -3
  21. package/dist/Standards/NftStandards/ERC721/ERC721Standard.mjs +2 -2
  22. package/dist/TokenBalancesController.js +2 -2
  23. package/dist/TokenBalancesController.mjs +1 -1
  24. package/dist/TokenDetectionController.js +3 -3
  25. package/dist/TokenDetectionController.mjs +2 -2
  26. package/dist/TokenListController.js +4 -4
  27. package/dist/TokenListController.mjs +3 -3
  28. package/dist/TokenRatesController.js +9 -5
  29. package/dist/TokenRatesController.mjs +10 -6
  30. package/dist/TokensController.js +6 -6
  31. package/dist/TokensController.mjs +5 -5
  32. package/dist/assetsUtil.js +2 -2
  33. package/dist/assetsUtil.mjs +1 -1
  34. package/dist/chunk-2IIQTSLV.mjs +236 -0
  35. package/dist/chunk-2IIQTSLV.mjs.map +1 -0
  36. package/dist/chunk-354SINOH.js +236 -0
  37. package/dist/chunk-354SINOH.js.map +1 -0
  38. package/dist/{chunk-MR6EF4B7.mjs → chunk-3K3LE2CZ.mjs} +14 -2
  39. package/dist/chunk-3K3LE2CZ.mjs.map +1 -0
  40. package/dist/{chunk-5W5OO2Q5.mjs → chunk-47CRHAUE.mjs} +2 -2
  41. package/dist/chunk-47CRHAUE.mjs.map +1 -0
  42. package/dist/{chunk-65PB33TE.mjs → chunk-526TATMH.mjs} +25 -17
  43. package/dist/chunk-526TATMH.mjs.map +1 -0
  44. package/dist/{chunk-D3K5MPMW.mjs → chunk-56O7BVZV.mjs} +2 -2
  45. package/dist/{chunk-B5YY22QQ.js → chunk-6PPM4ETZ.js} +3 -3
  46. package/dist/{chunk-CGLUTXI7.js → chunk-7JWDWDXT.js} +190 -107
  47. package/dist/chunk-7JWDWDXT.js.map +1 -0
  48. package/dist/{chunk-JUI3XNEF.js → chunk-7OZL6IDY.js} +16 -9
  49. package/dist/chunk-7OZL6IDY.js.map +1 -0
  50. package/dist/{chunk-GU53EI7A.js → chunk-AZ6SRJVI.js} +24 -16
  51. package/dist/chunk-AZ6SRJVI.js.map +1 -0
  52. package/dist/{chunk-YIFA2HXH.js → chunk-F6L3DFOZ.js} +5 -3
  53. package/dist/chunk-F6L3DFOZ.js.map +1 -0
  54. package/dist/{chunk-UEDNQBJN.js → chunk-FGAZXVKS.js} +200 -104
  55. package/dist/chunk-FGAZXVKS.js.map +1 -0
  56. package/dist/{chunk-B6W4CQOR.mjs → chunk-HJ5GXVDT.mjs} +2 -2
  57. package/dist/chunk-HJ5GXVDT.mjs.map +1 -0
  58. package/dist/{chunk-BOTVAG4A.js → chunk-JBF4XEGR.js} +5 -5
  59. package/dist/{chunk-ASA5RLBY.mjs → chunk-JEIAMJGS.mjs} +2 -2
  60. package/dist/{chunk-CNKVITJO.mjs → chunk-JTXPJ6TK.mjs} +4 -2
  61. package/dist/chunk-JTXPJ6TK.mjs.map +1 -0
  62. package/dist/{chunk-U3DJJN4X.js → chunk-LBJTMJFA.js} +4 -4
  63. package/dist/{chunk-IBK6AXPP.js → chunk-NWVMFCSC.js} +1 -1
  64. package/dist/{chunk-IBK6AXPP.js.map → chunk-NWVMFCSC.js.map} +1 -1
  65. package/dist/{chunk-NEXY7SE2.js → chunk-NYVA7ZTQ.js} +2 -2
  66. package/dist/chunk-NYVA7ZTQ.js.map +1 -0
  67. package/dist/{chunk-WCCLSUOI.mjs → chunk-OEEFNXR7.mjs} +1 -1
  68. package/dist/{chunk-WCCLSUOI.mjs.map → chunk-OEEFNXR7.mjs.map} +1 -1
  69. package/dist/{chunk-E4ECCGJV.mjs → chunk-P3O5CVAH.mjs} +195 -99
  70. package/dist/chunk-P3O5CVAH.mjs.map +1 -0
  71. package/dist/{chunk-4AC3X2U5.js → chunk-RJBYLTY5.js} +29 -14
  72. package/dist/chunk-RJBYLTY5.js.map +1 -0
  73. package/dist/{chunk-B5YVX5IO.mjs → chunk-SBWPU4VT.mjs} +4 -2
  74. package/dist/chunk-SBWPU4VT.mjs.map +1 -0
  75. package/dist/{chunk-ELSMS5S7.js → chunk-T5ZX5BV7.js} +3 -3
  76. package/dist/{chunk-ELSMS5S7.js.map → chunk-T5ZX5BV7.js.map} +1 -1
  77. package/dist/{chunk-HLCGZGPA.mjs → chunk-TWR6P5WG.mjs} +11 -7
  78. package/dist/chunk-TWR6P5WG.mjs.map +1 -0
  79. package/dist/{chunk-73F3SN5O.mjs → chunk-UVE4XY5Y.mjs} +23 -8
  80. package/dist/chunk-UVE4XY5Y.mjs.map +1 -0
  81. package/dist/{chunk-LZ5ZGQEX.mjs → chunk-UYH6NWKB.mjs} +2 -2
  82. package/dist/{chunk-Z6TBQQE5.js → chunk-V6DNVROD.js} +4 -4
  83. package/dist/chunk-V6DNVROD.js.map +1 -0
  84. package/dist/{chunk-Q5JRBGWO.mjs → chunk-VELPHITE.mjs} +2 -2
  85. package/dist/chunk-VELPHITE.mjs.map +1 -0
  86. package/dist/{chunk-HDI4L2DD.js → chunk-VZF43NDM.js} +17 -5
  87. package/dist/chunk-VZF43NDM.js.map +1 -0
  88. package/dist/{chunk-62T7RKU3.mjs → chunk-W57QXOHJ.mjs} +188 -105
  89. package/dist/chunk-W57QXOHJ.mjs.map +1 -0
  90. package/dist/{chunk-PYMUBJQX.js → chunk-X4RMS365.js} +3 -3
  91. package/dist/chunk-X4RMS365.js.map +1 -0
  92. package/dist/{chunk-Y35SM7TO.mjs → chunk-XQO3EG4J.mjs} +4 -4
  93. package/dist/chunk-XQO3EG4J.mjs.map +1 -0
  94. package/dist/{chunk-QHWKLZUC.js → chunk-XSNGD5US.js} +14 -10
  95. package/dist/chunk-XSNGD5US.js.map +1 -0
  96. package/dist/{chunk-A2DDWXMS.mjs → chunk-Y45HQET7.mjs} +12 -5
  97. package/dist/chunk-Y45HQET7.mjs.map +1 -0
  98. package/dist/{chunk-WB6KJX4N.js → chunk-ZG5MS2TO.js} +4 -2
  99. package/dist/chunk-ZG5MS2TO.js.map +1 -0
  100. package/dist/crypto-compare-service/crypto-compare.js +2 -2
  101. package/dist/crypto-compare-service/crypto-compare.mjs +1 -1
  102. package/dist/crypto-compare-service/index.js +2 -2
  103. package/dist/crypto-compare-service/index.mjs +1 -1
  104. package/dist/index.js +21 -19
  105. package/dist/index.mjs +21 -19
  106. package/dist/token-prices-service/codefi-v2.js +2 -2
  107. package/dist/token-prices-service/codefi-v2.mjs +1 -1
  108. package/dist/token-prices-service/index.js +2 -2
  109. package/dist/token-prices-service/index.mjs +1 -1
  110. package/dist/token-service.js +3 -3
  111. package/dist/token-service.mjs +2 -2
  112. package/dist/tsconfig.build.tsbuildinfo +1 -1
  113. package/dist/types/AccountTrackerController.d.ts +13 -12
  114. package/dist/types/AccountTrackerController.d.ts.map +1 -1
  115. package/dist/types/AssetsContractController.d.ts.map +1 -1
  116. package/dist/types/CurrencyRateController.d.ts.map +1 -1
  117. package/dist/types/NftController.d.ts +4 -5
  118. package/dist/types/NftController.d.ts.map +1 -1
  119. package/dist/types/NftDetectionController.d.ts +7 -18
  120. package/dist/types/NftDetectionController.d.ts.map +1 -1
  121. package/dist/types/RatesController/RatesController.d.ts.map +1 -1
  122. package/dist/types/TokenBalancesController.d.ts.map +1 -1
  123. package/dist/types/TokenDetectionController.d.ts.map +1 -1
  124. package/dist/types/TokenListController.d.ts.map +1 -1
  125. package/dist/types/TokenRatesController.d.ts +72 -65
  126. package/dist/types/TokenRatesController.d.ts.map +1 -1
  127. package/dist/types/TokensController.d.ts.map +1 -1
  128. package/dist/types/assetsUtil.d.ts.map +1 -1
  129. package/dist/types/crypto-compare-service/crypto-compare.d.ts.map +1 -1
  130. package/dist/types/index.d.ts +2 -2
  131. package/dist/types/index.d.ts.map +1 -1
  132. package/dist/types/token-prices-service/abstract-token-prices-service.d.ts +0 -1
  133. package/dist/types/token-prices-service/abstract-token-prices-service.d.ts.map +1 -1
  134. package/dist/types/token-prices-service/codefi-v2.d.ts +1 -1
  135. package/dist/types/token-prices-service/codefi-v2.d.ts.map +1 -1
  136. package/dist/types/token-service.d.ts.map +1 -1
  137. package/package.json +5 -5
  138. package/dist/chunk-4AC3X2U5.js.map +0 -1
  139. package/dist/chunk-543CC74T.mjs +0 -253
  140. package/dist/chunk-543CC74T.mjs.map +0 -1
  141. package/dist/chunk-5W5OO2Q5.mjs.map +0 -1
  142. package/dist/chunk-62T7RKU3.mjs.map +0 -1
  143. package/dist/chunk-65PB33TE.mjs.map +0 -1
  144. package/dist/chunk-73F3SN5O.mjs.map +0 -1
  145. package/dist/chunk-A2DDWXMS.mjs.map +0 -1
  146. package/dist/chunk-B5YVX5IO.mjs.map +0 -1
  147. package/dist/chunk-B6W4CQOR.mjs.map +0 -1
  148. package/dist/chunk-CGLUTXI7.js.map +0 -1
  149. package/dist/chunk-CNKVITJO.mjs.map +0 -1
  150. package/dist/chunk-E4ECCGJV.mjs.map +0 -1
  151. package/dist/chunk-GU53EI7A.js.map +0 -1
  152. package/dist/chunk-HDI4L2DD.js.map +0 -1
  153. package/dist/chunk-HLCGZGPA.mjs.map +0 -1
  154. package/dist/chunk-JUI3XNEF.js.map +0 -1
  155. package/dist/chunk-MR6EF4B7.mjs.map +0 -1
  156. package/dist/chunk-NEXY7SE2.js.map +0 -1
  157. package/dist/chunk-PYMUBJQX.js.map +0 -1
  158. package/dist/chunk-Q5JRBGWO.mjs.map +0 -1
  159. package/dist/chunk-QFDTOEYR.js +0 -253
  160. package/dist/chunk-QFDTOEYR.js.map +0 -1
  161. package/dist/chunk-QHWKLZUC.js.map +0 -1
  162. package/dist/chunk-UEDNQBJN.js.map +0 -1
  163. package/dist/chunk-WB6KJX4N.js.map +0 -1
  164. package/dist/chunk-Y35SM7TO.mjs.map +0 -1
  165. package/dist/chunk-YIFA2HXH.js.map +0 -1
  166. package/dist/chunk-Z6TBQQE5.js.map +0 -1
  167. /package/dist/{chunk-D3K5MPMW.mjs.map → chunk-56O7BVZV.mjs.map} +0 -0
  168. /package/dist/{chunk-B5YY22QQ.js.map → chunk-6PPM4ETZ.js.map} +0 -0
  169. /package/dist/{chunk-BOTVAG4A.js.map → chunk-JBF4XEGR.js.map} +0 -0
  170. /package/dist/{chunk-ASA5RLBY.mjs.map → chunk-JEIAMJGS.mjs.map} +0 -0
  171. /package/dist/{chunk-U3DJJN4X.js.map → chunk-LBJTMJFA.js.map} +0 -0
  172. /package/dist/{chunk-LZ5ZGQEX.mjs.map → chunk-UYH6NWKB.mjs.map} +0 -0
@@ -0,0 +1,236 @@
1
+ import {
2
+ __privateAdd,
3
+ __privateGet,
4
+ __privateMethod,
5
+ __privateSet
6
+ } from "./chunk-XUI43LEZ.mjs";
7
+
8
+ // src/NftDetectionController.ts
9
+ import { BaseController } from "@metamask/base-controller";
10
+ import {
11
+ toChecksumHexAddress,
12
+ ChainId,
13
+ NFT_API_BASE_URL,
14
+ NFT_API_VERSION,
15
+ convertHexToDecimal,
16
+ handleFetch
17
+ } from "@metamask/controller-utils";
18
+ import { createDeferredPromise } from "@metamask/utils";
19
+ var controllerName = "NftDetectionController";
20
+ var supportedNftDetectionNetworks = [ChainId.mainnet];
21
+ var BlockaidResultType = /* @__PURE__ */ ((BlockaidResultType2) => {
22
+ BlockaidResultType2["Benign"] = "Benign";
23
+ BlockaidResultType2["Spam"] = "Spam";
24
+ BlockaidResultType2["Warning"] = "Warning";
25
+ BlockaidResultType2["Malicious"] = "Malicious";
26
+ return BlockaidResultType2;
27
+ })(BlockaidResultType || {});
28
+ var _disabled, _addNft, _getNftState, _inProcessNftFetchingUpdates, _onPreferencesControllerStateChange, onPreferencesControllerStateChange_fn, _getOwnerNftApi, getOwnerNftApi_fn, _getOwnerNfts, getOwnerNfts_fn;
29
+ var NftDetectionController = class extends BaseController {
30
+ /**
31
+ * The controller options
32
+ *
33
+ * @param options - The controller options.
34
+ * @param options.messenger - A reference to the messaging system.
35
+ * @param options.disabled - Represents previous value of useNftDetection. Used to detect changes of useNftDetection. Default value is true.
36
+ * @param options.addNft - Add an NFT.
37
+ * @param options.getNftState - Gets the current state of the Assets controller.
38
+ */
39
+ constructor({
40
+ messenger,
41
+ disabled = false,
42
+ addNft,
43
+ getNftState
44
+ }) {
45
+ super({
46
+ name: controllerName,
47
+ messenger,
48
+ metadata: {},
49
+ state: {}
50
+ });
51
+ /**
52
+ * Handles the state change of the preference controller.
53
+ * @param preferencesState - The new state of the preference controller.
54
+ * @param preferencesState.useNftDetection - Boolean indicating user preference on NFT detection.
55
+ */
56
+ __privateAdd(this, _onPreferencesControllerStateChange);
57
+ __privateAdd(this, _getOwnerNftApi);
58
+ __privateAdd(this, _getOwnerNfts);
59
+ __privateAdd(this, _disabled, void 0);
60
+ __privateAdd(this, _addNft, void 0);
61
+ __privateAdd(this, _getNftState, void 0);
62
+ __privateAdd(this, _inProcessNftFetchingUpdates, void 0);
63
+ __privateSet(this, _disabled, disabled);
64
+ __privateSet(this, _inProcessNftFetchingUpdates, {});
65
+ __privateSet(this, _getNftState, getNftState);
66
+ __privateSet(this, _addNft, addNft);
67
+ this.messagingSystem.subscribe(
68
+ "PreferencesController:stateChange",
69
+ __privateMethod(this, _onPreferencesControllerStateChange, onPreferencesControllerStateChange_fn).bind(this)
70
+ );
71
+ }
72
+ /**
73
+ * Checks whether network is mainnet or not.
74
+ *
75
+ * @returns Whether current network is mainnet.
76
+ */
77
+ isMainnet() {
78
+ const { selectedNetworkClientId } = this.messagingSystem.call(
79
+ "NetworkController:getState"
80
+ );
81
+ const {
82
+ configuration: { chainId }
83
+ } = this.messagingSystem.call(
84
+ "NetworkController:getNetworkClientById",
85
+ selectedNetworkClientId
86
+ );
87
+ return chainId === ChainId.mainnet;
88
+ }
89
+ isMainnetByNetworkClientId(networkClient) {
90
+ return networkClient.configuration.chainId === ChainId.mainnet;
91
+ }
92
+ /**
93
+ * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are
94
+ * added.
95
+ *
96
+ * @param options - Options bag.
97
+ * @param options.networkClientId - The network client ID to detect NFTs on.
98
+ * @param options.userAddress - The address to detect NFTs for.
99
+ */
100
+ async detectNfts(options) {
101
+ const userAddress = options?.userAddress ?? this.messagingSystem.call("AccountsController:getSelectedAccount").address;
102
+ const { selectedNetworkClientId } = this.messagingSystem.call(
103
+ "NetworkController:getState"
104
+ );
105
+ const {
106
+ configuration: { chainId }
107
+ } = this.messagingSystem.call(
108
+ "NetworkController:getNetworkClientById",
109
+ selectedNetworkClientId
110
+ );
111
+ if (!supportedNftDetectionNetworks.includes(chainId) || __privateGet(this, _disabled)) {
112
+ return;
113
+ }
114
+ if (!userAddress) {
115
+ return;
116
+ }
117
+ const updateKey = `${chainId}:${userAddress}`;
118
+ if (updateKey in __privateGet(this, _inProcessNftFetchingUpdates)) {
119
+ await __privateGet(this, _inProcessNftFetchingUpdates)[updateKey];
120
+ return;
121
+ }
122
+ const {
123
+ promise: inProgressUpdate,
124
+ resolve: updateSucceeded,
125
+ reject: updateFailed
126
+ } = createDeferredPromise({ suppressUnhandledRejection: true });
127
+ __privateGet(this, _inProcessNftFetchingUpdates)[updateKey] = inProgressUpdate;
128
+ let next;
129
+ let apiNfts = [];
130
+ let resultNftApi;
131
+ try {
132
+ do {
133
+ resultNftApi = await __privateMethod(this, _getOwnerNfts, getOwnerNfts_fn).call(this, userAddress, chainId, next);
134
+ apiNfts = resultNftApi.tokens.filter(
135
+ (elm) => elm.token.isSpam === false && (elm.blockaidResult?.result_type ? elm.blockaidResult?.result_type === "Benign" /* Benign */ : true)
136
+ );
137
+ const addNftPromises = apiNfts.map(async (nft) => {
138
+ const {
139
+ tokenId,
140
+ contract,
141
+ kind,
142
+ image: imageUrl,
143
+ imageSmall: imageThumbnailUrl,
144
+ metadata: { imageOriginal: imageOriginalUrl } = {},
145
+ name,
146
+ description,
147
+ attributes,
148
+ topBid,
149
+ lastSale,
150
+ rarityRank,
151
+ rarityScore,
152
+ collection
153
+ } = nft.token;
154
+ let ignored;
155
+ const { ignoredNfts } = __privateGet(this, _getNftState).call(this);
156
+ if (ignoredNfts.length) {
157
+ ignored = ignoredNfts.find((c) => {
158
+ return c.address === toChecksumHexAddress(contract) && c.tokenId === tokenId;
159
+ });
160
+ }
161
+ if (!ignored) {
162
+ const nftMetadata = Object.assign(
163
+ {},
164
+ { name },
165
+ description && { description },
166
+ imageUrl && { image: imageUrl },
167
+ imageThumbnailUrl && { imageThumbnail: imageThumbnailUrl },
168
+ imageOriginalUrl && { imageOriginal: imageOriginalUrl },
169
+ kind && { standard: kind.toUpperCase() },
170
+ lastSale && { lastSale },
171
+ attributes && { attributes },
172
+ topBid && { topBid },
173
+ rarityRank && { rarityRank },
174
+ rarityScore && { rarityScore },
175
+ collection && { collection }
176
+ );
177
+ await __privateGet(this, _addNft).call(this, contract, tokenId, {
178
+ nftMetadata,
179
+ userAddress,
180
+ source: "detected" /* Detected */,
181
+ networkClientId: options?.networkClientId
182
+ });
183
+ }
184
+ });
185
+ await Promise.all(addNftPromises);
186
+ } while (next = resultNftApi.continuation);
187
+ updateSucceeded();
188
+ } catch (error) {
189
+ updateFailed(error);
190
+ throw error;
191
+ } finally {
192
+ delete __privateGet(this, _inProcessNftFetchingUpdates)[updateKey];
193
+ }
194
+ }
195
+ };
196
+ _disabled = new WeakMap();
197
+ _addNft = new WeakMap();
198
+ _getNftState = new WeakMap();
199
+ _inProcessNftFetchingUpdates = new WeakMap();
200
+ _onPreferencesControllerStateChange = new WeakSet();
201
+ onPreferencesControllerStateChange_fn = function({ useNftDetection }) {
202
+ if (!useNftDetection !== __privateGet(this, _disabled)) {
203
+ __privateSet(this, _disabled, !useNftDetection);
204
+ }
205
+ };
206
+ _getOwnerNftApi = new WeakSet();
207
+ getOwnerNftApi_fn = function({
208
+ chainId,
209
+ address,
210
+ next
211
+ }) {
212
+ return `${NFT_API_BASE_URL}/users/${address}/tokens?chainIds=${chainId}&limit=50&includeTopBid=true&continuation=${next ?? ""}`;
213
+ };
214
+ _getOwnerNfts = new WeakSet();
215
+ getOwnerNfts_fn = async function(address, chainId, cursor) {
216
+ const convertedChainId = convertHexToDecimal(chainId).toString();
217
+ const url = __privateMethod(this, _getOwnerNftApi, getOwnerNftApi_fn).call(this, {
218
+ chainId: convertedChainId,
219
+ address,
220
+ next: cursor
221
+ });
222
+ const nftApiResponse = await handleFetch(url, {
223
+ headers: {
224
+ Version: NFT_API_VERSION
225
+ }
226
+ });
227
+ return nftApiResponse;
228
+ };
229
+ var NftDetectionController_default = NftDetectionController;
230
+
231
+ export {
232
+ BlockaidResultType,
233
+ NftDetectionController,
234
+ NftDetectionController_default
235
+ };
236
+ //# sourceMappingURL=chunk-2IIQTSLV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/NftDetectionController.ts"],"sourcesContent":["import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type { AddApprovalRequest } from '@metamask/approval-controller';\nimport type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n toChecksumHexAddress,\n ChainId,\n NFT_API_BASE_URL,\n NFT_API_VERSION,\n convertHexToDecimal,\n handleFetch,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkClient,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerStateChangeEvent,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { createDeferredPromise, type Hex } from '@metamask/utils';\n\nimport { Source } from './constants';\nimport {\n type NftController,\n type NftControllerState,\n type NftMetadata,\n} from './NftController';\n\nconst controllerName = 'NftDetectionController';\n\nexport type NFTDetectionControllerState = Record<never, never>;\n\nexport type AllowedActions =\n | AddApprovalRequest\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction;\n\nexport type AllowedEvents =\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent;\n\nexport type NftDetectionControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AllowedActions,\n AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\nconst supportedNftDetectionNetworks: Hex[] = [ChainId.mainnet];\n\n/**\n * @type ApiNft\n *\n * NFT object coming from OpenSea api\n * @property token_id - The NFT identifier\n * @property num_sales - Number of sales\n * @property background_color - The background color to be displayed with the item\n * @property image_url - URI of an image associated with this NFT\n * @property image_preview_url - URI of a smaller image associated with this NFT\n * @property image_thumbnail_url - URI of a thumbnail image associated with this NFT\n * @property image_original_url - URI of the original image associated with this NFT\n * @property animation_url - URI of a animation associated with this NFT\n * @property animation_original_url - URI of the original animation associated with this NFT\n * @property name - The NFT name\n * @property description - The NFT description\n * @property external_link - External link containing additional information\n * @property assetContract - The NFT contract information object\n * @property creator - The NFT owner information object\n * @property lastSale - When this item was last sold\n */\nexport type ApiNft = {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n token_id: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n num_sales: number | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n background_color: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_preview_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_thumbnail_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_original_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n animation_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n animation_original_url: string | null;\n name: string | null;\n description: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n external_link: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n asset_contract: ApiNftContract;\n creator: ApiNftCreator;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n last_sale: ApiNftLastSale | null;\n};\n\n/**\n * @type ApiNftContract\n *\n * NFT contract object coming from OpenSea api\n * @property address - Address of the NFT contract\n * @property asset_contract_type - The NFT type, it could be `semi-fungible` or `non-fungible`\n * @property created_date - Creation date\n * @property collection - Object containing the contract name and URI of an image associated\n * @property schema_name - The schema followed by the contract, it could be `ERC721` or `ERC1155`\n * @property symbol - The NFT contract symbol\n * @property total_supply - Total supply of NFTs\n * @property description - The NFT contract description\n * @property external_link - External link containing additional information\n */\nexport type ApiNftContract = {\n address: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n asset_contract_type: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n created_date: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n schema_name: string | null;\n symbol: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n total_supply: string | null;\n description: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n external_link: string | null;\n collection: {\n name: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_url?: string | null;\n tokenCount?: string | null;\n };\n};\n\n/**\n * @type ApiNftLastSale\n *\n * NFT sale object coming from OpenSea api\n * @property event_timestamp - Object containing a `username`\n * @property total_price - URI of NFT image associated with this owner\n * @property transaction - Object containing transaction_hash and block_hash\n */\nexport type ApiNftLastSale = {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n event_timestamp: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n total_price: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n transaction: { transaction_hash: string; block_hash: string };\n};\n\n/**\n * @type ApiNftCreator\n *\n * NFT creator object coming from OpenSea api\n * @property user - Object containing a `username`\n * @property profile_img_url - URI of NFT image associated with this owner\n * @property address - The owner address\n */\nexport type ApiNftCreator = {\n user: { username: string };\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n profile_img_url: string;\n address: string;\n};\n\nexport type ReservoirResponse = {\n tokens: TokensResponse[];\n continuation?: string;\n};\n\nexport type TokensResponse = {\n token: TokenResponse;\n ownership: Ownership;\n market?: Market;\n blockaidResult?: Blockaid;\n};\n\nexport enum BlockaidResultType {\n Benign = 'Benign',\n Spam = 'Spam',\n Warning = 'Warning',\n Malicious = 'Malicious',\n}\n\nexport type Blockaid = {\n contract: string;\n chainId: number;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n result_type: BlockaidResultType;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n malicious_score: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n attack_types: object;\n};\n\nexport type Market = {\n floorAsk?: FloorAsk;\n topBid?: TopBid;\n};\n\nexport type TokenResponse = {\n chainId: number;\n contract: string;\n tokenId: string;\n kind?: string;\n name?: string;\n image?: string;\n imageSmall?: string;\n imageLarge?: string;\n metadata?: Metadata;\n description?: string;\n supply?: number;\n remainingSupply?: number;\n rarityScore?: number;\n rarity?: number;\n rarityRank?: number;\n media?: string;\n isFlagged?: boolean;\n isSpam?: boolean;\n isNsfw?: boolean;\n metadataDisabled?: boolean;\n lastFlagUpdate?: string;\n lastFlagChange?: string;\n collection?: Collection;\n lastSale?: LastSale;\n topBid?: TopBid;\n lastAppraisalValue?: number;\n attributes?: Attributes[];\n};\n\nexport type TopBid = {\n id?: string;\n price?: Price;\n source?: {\n id?: string;\n domain?: string;\n name?: string;\n icon?: string;\n url?: string;\n };\n};\n\nexport type LastSale = {\n saleId?: string;\n token?: {\n contract?: string;\n tokenId?: string;\n name?: string;\n image?: string;\n collection?: {\n id?: string;\n name?: string;\n };\n };\n orderSource?: string;\n orderSide?: 'ask' | 'bid';\n orderKind?: string;\n orderId?: string;\n from?: string;\n to?: string;\n amount?: string;\n fillSource?: string;\n block?: number;\n txHash?: string;\n logIndex?: number;\n batchIndex?: number;\n timestamp?: number;\n price?: Price;\n washTradingScore?: number;\n royaltyFeeBps?: number;\n marketplaceFeeBps?: number;\n paidFullRoyalty?: boolean;\n feeBreakdown?: FeeBreakdown[];\n isDeleted?: boolean;\n createdAt?: string;\n updatedAt?: string;\n};\n\nexport type FeeBreakdown = {\n kind?: string;\n bps?: number;\n recipient?: string;\n source?: string;\n rawAmount?: string;\n};\n\nexport type Attributes = {\n key?: string;\n kind?: string;\n value: string;\n tokenCount?: number;\n onSaleCount?: number;\n floorAskPrice?: Price | null;\n topBidValue?: number | null;\n createdAt?: string;\n};\n\nexport type Collection = {\n id?: string;\n name?: string;\n slug?: string;\n symbol?: string;\n imageUrl?: string;\n image?: string;\n isSpam?: boolean;\n isNsfw?: boolean;\n creator?: string;\n tokenCount?: string;\n metadataDisabled?: boolean;\n openseaVerificationStatus?: string;\n floorAskPrice?: Price;\n royaltiesBps?: number;\n royalties?: Royalties[];\n};\n\nexport type Royalties = {\n bps?: number;\n recipient?: string;\n};\n\nexport type Ownership = {\n tokenCount?: string;\n onSaleCount?: string;\n floorAsk?: FloorAsk;\n acquiredAt?: string;\n};\n\nexport type FloorAsk = {\n id?: string;\n price?: Price;\n maker?: string;\n kind?: string;\n validFrom?: number;\n validUntil?: number;\n source?: Source;\n rawData?: Metadata;\n isNativeOffChainCancellable?: boolean;\n};\n\nexport type Price = {\n currency?: {\n contract?: string;\n name?: string;\n symbol?: string;\n decimals?: number;\n chainId?: number;\n };\n amount?: {\n raw?: string;\n decimal?: number;\n usd?: number;\n native?: number;\n };\n netAmount?: {\n raw?: string;\n decimal?: number;\n usd?: number;\n native?: number;\n };\n};\n\nexport type Metadata = {\n imageOriginal?: string;\n tokenURI?: string;\n};\n\n/**\n * Controller that passively detects nfts for a user address\n */\nexport class NftDetectionController extends BaseController<\n typeof controllerName,\n NFTDetectionControllerState,\n NftDetectionControllerMessenger\n> {\n #disabled: boolean;\n\n readonly #addNft: NftController['addNft'];\n\n readonly #getNftState: () => NftControllerState;\n\n #inProcessNftFetchingUpdates: Record<`${Hex}:${string}`, Promise<void>>;\n\n /**\n * The controller options\n *\n * @param options - The controller options.\n * @param options.messenger - A reference to the messaging system.\n * @param options.disabled - Represents previous value of useNftDetection. Used to detect changes of useNftDetection. Default value is true.\n * @param options.addNft - Add an NFT.\n * @param options.getNftState - Gets the current state of the Assets controller.\n */\n constructor({\n messenger,\n disabled = false,\n addNft,\n getNftState,\n }: {\n messenger: NftDetectionControllerMessenger;\n disabled: boolean;\n addNft: NftController['addNft'];\n getNftState: () => NftControllerState;\n }) {\n super({\n name: controllerName,\n messenger,\n metadata: {},\n state: {},\n });\n this.#disabled = disabled;\n this.#inProcessNftFetchingUpdates = {};\n\n this.#getNftState = getNftState;\n this.#addNft = addNft;\n\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesControllerStateChange.bind(this),\n );\n }\n\n /**\n * Checks whether network is mainnet or not.\n *\n * @returns Whether current network is mainnet.\n */\n isMainnet(): boolean {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n return chainId === ChainId.mainnet;\n }\n\n isMainnetByNetworkClientId(networkClient: NetworkClient): boolean {\n return networkClient.configuration.chainId === ChainId.mainnet;\n }\n\n /**\n * Handles the state change of the preference controller.\n * @param preferencesState - The new state of the preference controller.\n * @param preferencesState.useNftDetection - Boolean indicating user preference on NFT detection.\n */\n #onPreferencesControllerStateChange({ useNftDetection }: PreferencesState) {\n if (!useNftDetection !== this.#disabled) {\n this.#disabled = !useNftDetection;\n }\n }\n\n #getOwnerNftApi({\n chainId,\n address,\n next,\n }: {\n chainId: string;\n address: string;\n next?: string;\n }) {\n return `${\n NFT_API_BASE_URL as string\n }/users/${address}/tokens?chainIds=${chainId}&limit=50&includeTopBid=true&continuation=${\n next ?? ''\n }`;\n }\n\n async #getOwnerNfts(\n address: string,\n chainId: Hex,\n cursor: string | undefined,\n ) {\n // Convert hex chainId to number\n const convertedChainId = convertHexToDecimal(chainId).toString();\n const url = this.#getOwnerNftApi({\n chainId: convertedChainId,\n address,\n next: cursor,\n });\n const nftApiResponse: ReservoirResponse = await handleFetch(url, {\n headers: {\n Version: NFT_API_VERSION,\n },\n });\n return nftApiResponse;\n }\n\n /**\n * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are\n * added.\n *\n * @param options - Options bag.\n * @param options.networkClientId - The network client ID to detect NFTs on.\n * @param options.userAddress - The address to detect NFTs for.\n */\n async detectNfts(options?: {\n networkClientId?: NetworkClientId;\n userAddress?: string;\n }) {\n const userAddress =\n options?.userAddress ??\n this.messagingSystem.call('AccountsController:getSelectedAccount')\n .address;\n\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n /* istanbul ignore if */\n if (!supportedNftDetectionNetworks.includes(chainId) || this.#disabled) {\n return;\n }\n /* istanbul ignore else */\n if (!userAddress) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const updateKey: `${Hex}:${string}` = `${chainId}:${userAddress}`;\n if (updateKey in this.#inProcessNftFetchingUpdates) {\n // This prevents redundant updates\n // This promise is resolved after the in-progress update has finished,\n // and state has been updated.\n await this.#inProcessNftFetchingUpdates[updateKey];\n return;\n }\n\n const {\n promise: inProgressUpdate,\n resolve: updateSucceeded,\n reject: updateFailed,\n } = createDeferredPromise({ suppressUnhandledRejection: true });\n this.#inProcessNftFetchingUpdates[updateKey] = inProgressUpdate;\n\n let next;\n let apiNfts: TokensResponse[] = [];\n let resultNftApi: ReservoirResponse;\n try {\n do {\n resultNftApi = await this.#getOwnerNfts(userAddress, chainId, next);\n apiNfts = resultNftApi.tokens.filter(\n (elm) =>\n elm.token.isSpam === false &&\n (elm.blockaidResult?.result_type\n ? elm.blockaidResult?.result_type === BlockaidResultType.Benign\n : true),\n );\n const addNftPromises = apiNfts.map(async (nft) => {\n const {\n tokenId,\n contract,\n kind,\n image: imageUrl,\n imageSmall: imageThumbnailUrl,\n metadata: { imageOriginal: imageOriginalUrl } = {},\n name,\n description,\n attributes,\n topBid,\n lastSale,\n rarityRank,\n rarityScore,\n collection,\n } = nft.token;\n\n let ignored;\n /* istanbul ignore else */\n const { ignoredNfts } = this.#getNftState();\n if (ignoredNfts.length) {\n ignored = ignoredNfts.find((c) => {\n /* istanbul ignore next */\n return (\n c.address === toChecksumHexAddress(contract) &&\n c.tokenId === tokenId\n );\n });\n }\n\n /* istanbul ignore else */\n if (!ignored) {\n /* istanbul ignore next */\n const nftMetadata: NftMetadata = Object.assign(\n {},\n { name },\n description && { description },\n imageUrl && { image: imageUrl },\n imageThumbnailUrl && { imageThumbnail: imageThumbnailUrl },\n imageOriginalUrl && { imageOriginal: imageOriginalUrl },\n kind && { standard: kind.toUpperCase() },\n lastSale && { lastSale },\n attributes && { attributes },\n topBid && { topBid },\n rarityRank && { rarityRank },\n rarityScore && { rarityScore },\n collection && { collection },\n );\n\n await this.#addNft(contract, tokenId, {\n nftMetadata,\n userAddress,\n source: Source.Detected,\n networkClientId: options?.networkClientId,\n });\n }\n });\n await Promise.all(addNftPromises);\n } while ((next = resultNftApi.continuation));\n updateSucceeded();\n } catch (error) {\n updateFailed(error);\n throw error;\n } finally {\n delete this.#inProcessNftFetchingUpdates[updateKey];\n }\n }\n}\n\nexport default NftDetectionController;\n"],"mappings":"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","names":["BlockaidResultType"]}
@@ -0,0 +1,236 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+ var _chunkZ4BLTVTBjs = require('./chunk-Z4BLTVTB.js');
7
+
8
+ // src/NftDetectionController.ts
9
+ var _basecontroller = require('@metamask/base-controller');
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ var _controllerutils = require('@metamask/controller-utils');
18
+ var _utils = require('@metamask/utils');
19
+ var controllerName = "NftDetectionController";
20
+ var supportedNftDetectionNetworks = [_controllerutils.ChainId.mainnet];
21
+ var BlockaidResultType = /* @__PURE__ */ ((BlockaidResultType2) => {
22
+ BlockaidResultType2["Benign"] = "Benign";
23
+ BlockaidResultType2["Spam"] = "Spam";
24
+ BlockaidResultType2["Warning"] = "Warning";
25
+ BlockaidResultType2["Malicious"] = "Malicious";
26
+ return BlockaidResultType2;
27
+ })(BlockaidResultType || {});
28
+ var _disabled, _addNft, _getNftState, _inProcessNftFetchingUpdates, _onPreferencesControllerStateChange, onPreferencesControllerStateChange_fn, _getOwnerNftApi, getOwnerNftApi_fn, _getOwnerNfts, getOwnerNfts_fn;
29
+ var NftDetectionController = class extends _basecontroller.BaseController {
30
+ /**
31
+ * The controller options
32
+ *
33
+ * @param options - The controller options.
34
+ * @param options.messenger - A reference to the messaging system.
35
+ * @param options.disabled - Represents previous value of useNftDetection. Used to detect changes of useNftDetection. Default value is true.
36
+ * @param options.addNft - Add an NFT.
37
+ * @param options.getNftState - Gets the current state of the Assets controller.
38
+ */
39
+ constructor({
40
+ messenger,
41
+ disabled = false,
42
+ addNft,
43
+ getNftState
44
+ }) {
45
+ super({
46
+ name: controllerName,
47
+ messenger,
48
+ metadata: {},
49
+ state: {}
50
+ });
51
+ /**
52
+ * Handles the state change of the preference controller.
53
+ * @param preferencesState - The new state of the preference controller.
54
+ * @param preferencesState.useNftDetection - Boolean indicating user preference on NFT detection.
55
+ */
56
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _onPreferencesControllerStateChange);
57
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getOwnerNftApi);
58
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getOwnerNfts);
59
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _disabled, void 0);
60
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _addNft, void 0);
61
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getNftState, void 0);
62
+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _inProcessNftFetchingUpdates, void 0);
63
+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _disabled, disabled);
64
+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _inProcessNftFetchingUpdates, {});
65
+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _getNftState, getNftState);
66
+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _addNft, addNft);
67
+ this.messagingSystem.subscribe(
68
+ "PreferencesController:stateChange",
69
+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _onPreferencesControllerStateChange, onPreferencesControllerStateChange_fn).bind(this)
70
+ );
71
+ }
72
+ /**
73
+ * Checks whether network is mainnet or not.
74
+ *
75
+ * @returns Whether current network is mainnet.
76
+ */
77
+ isMainnet() {
78
+ const { selectedNetworkClientId } = this.messagingSystem.call(
79
+ "NetworkController:getState"
80
+ );
81
+ const {
82
+ configuration: { chainId }
83
+ } = this.messagingSystem.call(
84
+ "NetworkController:getNetworkClientById",
85
+ selectedNetworkClientId
86
+ );
87
+ return chainId === _controllerutils.ChainId.mainnet;
88
+ }
89
+ isMainnetByNetworkClientId(networkClient) {
90
+ return networkClient.configuration.chainId === _controllerutils.ChainId.mainnet;
91
+ }
92
+ /**
93
+ * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are
94
+ * added.
95
+ *
96
+ * @param options - Options bag.
97
+ * @param options.networkClientId - The network client ID to detect NFTs on.
98
+ * @param options.userAddress - The address to detect NFTs for.
99
+ */
100
+ async detectNfts(options) {
101
+ const userAddress = options?.userAddress ?? this.messagingSystem.call("AccountsController:getSelectedAccount").address;
102
+ const { selectedNetworkClientId } = this.messagingSystem.call(
103
+ "NetworkController:getState"
104
+ );
105
+ const {
106
+ configuration: { chainId }
107
+ } = this.messagingSystem.call(
108
+ "NetworkController:getNetworkClientById",
109
+ selectedNetworkClientId
110
+ );
111
+ if (!supportedNftDetectionNetworks.includes(chainId) || _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _disabled)) {
112
+ return;
113
+ }
114
+ if (!userAddress) {
115
+ return;
116
+ }
117
+ const updateKey = `${chainId}:${userAddress}`;
118
+ if (updateKey in _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _inProcessNftFetchingUpdates)) {
119
+ await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _inProcessNftFetchingUpdates)[updateKey];
120
+ return;
121
+ }
122
+ const {
123
+ promise: inProgressUpdate,
124
+ resolve: updateSucceeded,
125
+ reject: updateFailed
126
+ } = _utils.createDeferredPromise.call(void 0, { suppressUnhandledRejection: true });
127
+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _inProcessNftFetchingUpdates)[updateKey] = inProgressUpdate;
128
+ let next;
129
+ let apiNfts = [];
130
+ let resultNftApi;
131
+ try {
132
+ do {
133
+ resultNftApi = await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getOwnerNfts, getOwnerNfts_fn).call(this, userAddress, chainId, next);
134
+ apiNfts = resultNftApi.tokens.filter(
135
+ (elm) => elm.token.isSpam === false && (elm.blockaidResult?.result_type ? elm.blockaidResult?.result_type === "Benign" /* Benign */ : true)
136
+ );
137
+ const addNftPromises = apiNfts.map(async (nft) => {
138
+ const {
139
+ tokenId,
140
+ contract,
141
+ kind,
142
+ image: imageUrl,
143
+ imageSmall: imageThumbnailUrl,
144
+ metadata: { imageOriginal: imageOriginalUrl } = {},
145
+ name,
146
+ description,
147
+ attributes,
148
+ topBid,
149
+ lastSale,
150
+ rarityRank,
151
+ rarityScore,
152
+ collection
153
+ } = nft.token;
154
+ let ignored;
155
+ const { ignoredNfts } = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getNftState).call(this);
156
+ if (ignoredNfts.length) {
157
+ ignored = ignoredNfts.find((c) => {
158
+ return c.address === _controllerutils.toChecksumHexAddress.call(void 0, contract) && c.tokenId === tokenId;
159
+ });
160
+ }
161
+ if (!ignored) {
162
+ const nftMetadata = Object.assign(
163
+ {},
164
+ { name },
165
+ description && { description },
166
+ imageUrl && { image: imageUrl },
167
+ imageThumbnailUrl && { imageThumbnail: imageThumbnailUrl },
168
+ imageOriginalUrl && { imageOriginal: imageOriginalUrl },
169
+ kind && { standard: kind.toUpperCase() },
170
+ lastSale && { lastSale },
171
+ attributes && { attributes },
172
+ topBid && { topBid },
173
+ rarityRank && { rarityRank },
174
+ rarityScore && { rarityScore },
175
+ collection && { collection }
176
+ );
177
+ await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _addNft).call(this, contract, tokenId, {
178
+ nftMetadata,
179
+ userAddress,
180
+ source: "detected" /* Detected */,
181
+ networkClientId: options?.networkClientId
182
+ });
183
+ }
184
+ });
185
+ await Promise.all(addNftPromises);
186
+ } while (next = resultNftApi.continuation);
187
+ updateSucceeded();
188
+ } catch (error) {
189
+ updateFailed(error);
190
+ throw error;
191
+ } finally {
192
+ delete _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _inProcessNftFetchingUpdates)[updateKey];
193
+ }
194
+ }
195
+ };
196
+ _disabled = new WeakMap();
197
+ _addNft = new WeakMap();
198
+ _getNftState = new WeakMap();
199
+ _inProcessNftFetchingUpdates = new WeakMap();
200
+ _onPreferencesControllerStateChange = new WeakSet();
201
+ onPreferencesControllerStateChange_fn = function({ useNftDetection }) {
202
+ if (!useNftDetection !== _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _disabled)) {
203
+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _disabled, !useNftDetection);
204
+ }
205
+ };
206
+ _getOwnerNftApi = new WeakSet();
207
+ getOwnerNftApi_fn = function({
208
+ chainId,
209
+ address,
210
+ next
211
+ }) {
212
+ return `${_controllerutils.NFT_API_BASE_URL}/users/${address}/tokens?chainIds=${chainId}&limit=50&includeTopBid=true&continuation=${next ?? ""}`;
213
+ };
214
+ _getOwnerNfts = new WeakSet();
215
+ getOwnerNfts_fn = async function(address, chainId, cursor) {
216
+ const convertedChainId = _controllerutils.convertHexToDecimal.call(void 0, chainId).toString();
217
+ const url = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getOwnerNftApi, getOwnerNftApi_fn).call(this, {
218
+ chainId: convertedChainId,
219
+ address,
220
+ next: cursor
221
+ });
222
+ const nftApiResponse = await _controllerutils.handleFetch.call(void 0, url, {
223
+ headers: {
224
+ Version: _controllerutils.NFT_API_VERSION
225
+ }
226
+ });
227
+ return nftApiResponse;
228
+ };
229
+ var NftDetectionController_default = NftDetectionController;
230
+
231
+
232
+
233
+
234
+
235
+ exports.BlockaidResultType = BlockaidResultType; exports.NftDetectionController = NftDetectionController; exports.NftDetectionController_default = NftDetectionController_default;
236
+ //# sourceMappingURL=chunk-354SINOH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/NftDetectionController.ts"],"names":["BlockaidResultType"],"mappings":"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","sourcesContent":["import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type { AddApprovalRequest } from '@metamask/approval-controller';\nimport type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n toChecksumHexAddress,\n ChainId,\n NFT_API_BASE_URL,\n NFT_API_VERSION,\n convertHexToDecimal,\n handleFetch,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkClient,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerStateChangeEvent,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { createDeferredPromise, type Hex } from '@metamask/utils';\n\nimport { Source } from './constants';\nimport {\n type NftController,\n type NftControllerState,\n type NftMetadata,\n} from './NftController';\n\nconst controllerName = 'NftDetectionController';\n\nexport type NFTDetectionControllerState = Record<never, never>;\n\nexport type AllowedActions =\n | AddApprovalRequest\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction;\n\nexport type AllowedEvents =\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent;\n\nexport type NftDetectionControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AllowedActions,\n AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\nconst supportedNftDetectionNetworks: Hex[] = [ChainId.mainnet];\n\n/**\n * @type ApiNft\n *\n * NFT object coming from OpenSea api\n * @property token_id - The NFT identifier\n * @property num_sales - Number of sales\n * @property background_color - The background color to be displayed with the item\n * @property image_url - URI of an image associated with this NFT\n * @property image_preview_url - URI of a smaller image associated with this NFT\n * @property image_thumbnail_url - URI of a thumbnail image associated with this NFT\n * @property image_original_url - URI of the original image associated with this NFT\n * @property animation_url - URI of a animation associated with this NFT\n * @property animation_original_url - URI of the original animation associated with this NFT\n * @property name - The NFT name\n * @property description - The NFT description\n * @property external_link - External link containing additional information\n * @property assetContract - The NFT contract information object\n * @property creator - The NFT owner information object\n * @property lastSale - When this item was last sold\n */\nexport type ApiNft = {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n token_id: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n num_sales: number | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n background_color: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_preview_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_thumbnail_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_original_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n animation_url: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n animation_original_url: string | null;\n name: string | null;\n description: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n external_link: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n asset_contract: ApiNftContract;\n creator: ApiNftCreator;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n last_sale: ApiNftLastSale | null;\n};\n\n/**\n * @type ApiNftContract\n *\n * NFT contract object coming from OpenSea api\n * @property address - Address of the NFT contract\n * @property asset_contract_type - The NFT type, it could be `semi-fungible` or `non-fungible`\n * @property created_date - Creation date\n * @property collection - Object containing the contract name and URI of an image associated\n * @property schema_name - The schema followed by the contract, it could be `ERC721` or `ERC1155`\n * @property symbol - The NFT contract symbol\n * @property total_supply - Total supply of NFTs\n * @property description - The NFT contract description\n * @property external_link - External link containing additional information\n */\nexport type ApiNftContract = {\n address: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n asset_contract_type: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n created_date: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n schema_name: string | null;\n symbol: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n total_supply: string | null;\n description: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n external_link: string | null;\n collection: {\n name: string | null;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n image_url?: string | null;\n tokenCount?: string | null;\n };\n};\n\n/**\n * @type ApiNftLastSale\n *\n * NFT sale object coming from OpenSea api\n * @property event_timestamp - Object containing a `username`\n * @property total_price - URI of NFT image associated with this owner\n * @property transaction - Object containing transaction_hash and block_hash\n */\nexport type ApiNftLastSale = {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n event_timestamp: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n total_price: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n transaction: { transaction_hash: string; block_hash: string };\n};\n\n/**\n * @type ApiNftCreator\n *\n * NFT creator object coming from OpenSea api\n * @property user - Object containing a `username`\n * @property profile_img_url - URI of NFT image associated with this owner\n * @property address - The owner address\n */\nexport type ApiNftCreator = {\n user: { username: string };\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n profile_img_url: string;\n address: string;\n};\n\nexport type ReservoirResponse = {\n tokens: TokensResponse[];\n continuation?: string;\n};\n\nexport type TokensResponse = {\n token: TokenResponse;\n ownership: Ownership;\n market?: Market;\n blockaidResult?: Blockaid;\n};\n\nexport enum BlockaidResultType {\n Benign = 'Benign',\n Spam = 'Spam',\n Warning = 'Warning',\n Malicious = 'Malicious',\n}\n\nexport type Blockaid = {\n contract: string;\n chainId: number;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n result_type: BlockaidResultType;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n malicious_score: string;\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n attack_types: object;\n};\n\nexport type Market = {\n floorAsk?: FloorAsk;\n topBid?: TopBid;\n};\n\nexport type TokenResponse = {\n chainId: number;\n contract: string;\n tokenId: string;\n kind?: string;\n name?: string;\n image?: string;\n imageSmall?: string;\n imageLarge?: string;\n metadata?: Metadata;\n description?: string;\n supply?: number;\n remainingSupply?: number;\n rarityScore?: number;\n rarity?: number;\n rarityRank?: number;\n media?: string;\n isFlagged?: boolean;\n isSpam?: boolean;\n isNsfw?: boolean;\n metadataDisabled?: boolean;\n lastFlagUpdate?: string;\n lastFlagChange?: string;\n collection?: Collection;\n lastSale?: LastSale;\n topBid?: TopBid;\n lastAppraisalValue?: number;\n attributes?: Attributes[];\n};\n\nexport type TopBid = {\n id?: string;\n price?: Price;\n source?: {\n id?: string;\n domain?: string;\n name?: string;\n icon?: string;\n url?: string;\n };\n};\n\nexport type LastSale = {\n saleId?: string;\n token?: {\n contract?: string;\n tokenId?: string;\n name?: string;\n image?: string;\n collection?: {\n id?: string;\n name?: string;\n };\n };\n orderSource?: string;\n orderSide?: 'ask' | 'bid';\n orderKind?: string;\n orderId?: string;\n from?: string;\n to?: string;\n amount?: string;\n fillSource?: string;\n block?: number;\n txHash?: string;\n logIndex?: number;\n batchIndex?: number;\n timestamp?: number;\n price?: Price;\n washTradingScore?: number;\n royaltyFeeBps?: number;\n marketplaceFeeBps?: number;\n paidFullRoyalty?: boolean;\n feeBreakdown?: FeeBreakdown[];\n isDeleted?: boolean;\n createdAt?: string;\n updatedAt?: string;\n};\n\nexport type FeeBreakdown = {\n kind?: string;\n bps?: number;\n recipient?: string;\n source?: string;\n rawAmount?: string;\n};\n\nexport type Attributes = {\n key?: string;\n kind?: string;\n value: string;\n tokenCount?: number;\n onSaleCount?: number;\n floorAskPrice?: Price | null;\n topBidValue?: number | null;\n createdAt?: string;\n};\n\nexport type Collection = {\n id?: string;\n name?: string;\n slug?: string;\n symbol?: string;\n imageUrl?: string;\n image?: string;\n isSpam?: boolean;\n isNsfw?: boolean;\n creator?: string;\n tokenCount?: string;\n metadataDisabled?: boolean;\n openseaVerificationStatus?: string;\n floorAskPrice?: Price;\n royaltiesBps?: number;\n royalties?: Royalties[];\n};\n\nexport type Royalties = {\n bps?: number;\n recipient?: string;\n};\n\nexport type Ownership = {\n tokenCount?: string;\n onSaleCount?: string;\n floorAsk?: FloorAsk;\n acquiredAt?: string;\n};\n\nexport type FloorAsk = {\n id?: string;\n price?: Price;\n maker?: string;\n kind?: string;\n validFrom?: number;\n validUntil?: number;\n source?: Source;\n rawData?: Metadata;\n isNativeOffChainCancellable?: boolean;\n};\n\nexport type Price = {\n currency?: {\n contract?: string;\n name?: string;\n symbol?: string;\n decimals?: number;\n chainId?: number;\n };\n amount?: {\n raw?: string;\n decimal?: number;\n usd?: number;\n native?: number;\n };\n netAmount?: {\n raw?: string;\n decimal?: number;\n usd?: number;\n native?: number;\n };\n};\n\nexport type Metadata = {\n imageOriginal?: string;\n tokenURI?: string;\n};\n\n/**\n * Controller that passively detects nfts for a user address\n */\nexport class NftDetectionController extends BaseController<\n typeof controllerName,\n NFTDetectionControllerState,\n NftDetectionControllerMessenger\n> {\n #disabled: boolean;\n\n readonly #addNft: NftController['addNft'];\n\n readonly #getNftState: () => NftControllerState;\n\n #inProcessNftFetchingUpdates: Record<`${Hex}:${string}`, Promise<void>>;\n\n /**\n * The controller options\n *\n * @param options - The controller options.\n * @param options.messenger - A reference to the messaging system.\n * @param options.disabled - Represents previous value of useNftDetection. Used to detect changes of useNftDetection. Default value is true.\n * @param options.addNft - Add an NFT.\n * @param options.getNftState - Gets the current state of the Assets controller.\n */\n constructor({\n messenger,\n disabled = false,\n addNft,\n getNftState,\n }: {\n messenger: NftDetectionControllerMessenger;\n disabled: boolean;\n addNft: NftController['addNft'];\n getNftState: () => NftControllerState;\n }) {\n super({\n name: controllerName,\n messenger,\n metadata: {},\n state: {},\n });\n this.#disabled = disabled;\n this.#inProcessNftFetchingUpdates = {};\n\n this.#getNftState = getNftState;\n this.#addNft = addNft;\n\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesControllerStateChange.bind(this),\n );\n }\n\n /**\n * Checks whether network is mainnet or not.\n *\n * @returns Whether current network is mainnet.\n */\n isMainnet(): boolean {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n return chainId === ChainId.mainnet;\n }\n\n isMainnetByNetworkClientId(networkClient: NetworkClient): boolean {\n return networkClient.configuration.chainId === ChainId.mainnet;\n }\n\n /**\n * Handles the state change of the preference controller.\n * @param preferencesState - The new state of the preference controller.\n * @param preferencesState.useNftDetection - Boolean indicating user preference on NFT detection.\n */\n #onPreferencesControllerStateChange({ useNftDetection }: PreferencesState) {\n if (!useNftDetection !== this.#disabled) {\n this.#disabled = !useNftDetection;\n }\n }\n\n #getOwnerNftApi({\n chainId,\n address,\n next,\n }: {\n chainId: string;\n address: string;\n next?: string;\n }) {\n return `${\n NFT_API_BASE_URL as string\n }/users/${address}/tokens?chainIds=${chainId}&limit=50&includeTopBid=true&continuation=${\n next ?? ''\n }`;\n }\n\n async #getOwnerNfts(\n address: string,\n chainId: Hex,\n cursor: string | undefined,\n ) {\n // Convert hex chainId to number\n const convertedChainId = convertHexToDecimal(chainId).toString();\n const url = this.#getOwnerNftApi({\n chainId: convertedChainId,\n address,\n next: cursor,\n });\n const nftApiResponse: ReservoirResponse = await handleFetch(url, {\n headers: {\n Version: NFT_API_VERSION,\n },\n });\n return nftApiResponse;\n }\n\n /**\n * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are\n * added.\n *\n * @param options - Options bag.\n * @param options.networkClientId - The network client ID to detect NFTs on.\n * @param options.userAddress - The address to detect NFTs for.\n */\n async detectNfts(options?: {\n networkClientId?: NetworkClientId;\n userAddress?: string;\n }) {\n const userAddress =\n options?.userAddress ??\n this.messagingSystem.call('AccountsController:getSelectedAccount')\n .address;\n\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n /* istanbul ignore if */\n if (!supportedNftDetectionNetworks.includes(chainId) || this.#disabled) {\n return;\n }\n /* istanbul ignore else */\n if (!userAddress) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const updateKey: `${Hex}:${string}` = `${chainId}:${userAddress}`;\n if (updateKey in this.#inProcessNftFetchingUpdates) {\n // This prevents redundant updates\n // This promise is resolved after the in-progress update has finished,\n // and state has been updated.\n await this.#inProcessNftFetchingUpdates[updateKey];\n return;\n }\n\n const {\n promise: inProgressUpdate,\n resolve: updateSucceeded,\n reject: updateFailed,\n } = createDeferredPromise({ suppressUnhandledRejection: true });\n this.#inProcessNftFetchingUpdates[updateKey] = inProgressUpdate;\n\n let next;\n let apiNfts: TokensResponse[] = [];\n let resultNftApi: ReservoirResponse;\n try {\n do {\n resultNftApi = await this.#getOwnerNfts(userAddress, chainId, next);\n apiNfts = resultNftApi.tokens.filter(\n (elm) =>\n elm.token.isSpam === false &&\n (elm.blockaidResult?.result_type\n ? elm.blockaidResult?.result_type === BlockaidResultType.Benign\n : true),\n );\n const addNftPromises = apiNfts.map(async (nft) => {\n const {\n tokenId,\n contract,\n kind,\n image: imageUrl,\n imageSmall: imageThumbnailUrl,\n metadata: { imageOriginal: imageOriginalUrl } = {},\n name,\n description,\n attributes,\n topBid,\n lastSale,\n rarityRank,\n rarityScore,\n collection,\n } = nft.token;\n\n let ignored;\n /* istanbul ignore else */\n const { ignoredNfts } = this.#getNftState();\n if (ignoredNfts.length) {\n ignored = ignoredNfts.find((c) => {\n /* istanbul ignore next */\n return (\n c.address === toChecksumHexAddress(contract) &&\n c.tokenId === tokenId\n );\n });\n }\n\n /* istanbul ignore else */\n if (!ignored) {\n /* istanbul ignore next */\n const nftMetadata: NftMetadata = Object.assign(\n {},\n { name },\n description && { description },\n imageUrl && { image: imageUrl },\n imageThumbnailUrl && { imageThumbnail: imageThumbnailUrl },\n imageOriginalUrl && { imageOriginal: imageOriginalUrl },\n kind && { standard: kind.toUpperCase() },\n lastSale && { lastSale },\n attributes && { attributes },\n topBid && { topBid },\n rarityRank && { rarityRank },\n rarityScore && { rarityScore },\n collection && { collection },\n );\n\n await this.#addNft(contract, tokenId, {\n nftMetadata,\n userAddress,\n source: Source.Detected,\n networkClientId: options?.networkClientId,\n });\n }\n });\n await Promise.all(addNftPromises);\n } while ((next = resultNftApi.continuation));\n updateSucceeded();\n } catch (error) {\n updateFailed(error);\n throw error;\n } finally {\n delete this.#inProcessNftFetchingUpdates[updateKey];\n }\n }\n}\n\nexport default NftDetectionController;\n"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isTokenDetectionSupportedForNetwork
3
- } from "./chunk-Q5JRBGWO.mjs";
3
+ } from "./chunk-VELPHITE.mjs";
4
4
  import {
5
5
  __privateAdd,
6
6
  __privateGet,
@@ -215,6 +215,8 @@ registerEventListeners_fn = function() {
215
215
  });
216
216
  this.messagingSystem.subscribe(
217
217
  "TokenListController:stateChange",
218
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
219
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
218
220
  async ({ tokenList }) => {
219
221
  const hasTokens = Object.keys(tokenList).length;
220
222
  if (hasTokens) {
@@ -224,6 +226,8 @@ registerEventListeners_fn = function() {
224
226
  );
225
227
  this.messagingSystem.subscribe(
226
228
  "PreferencesController:stateChange",
229
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
230
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
227
231
  async ({ selectedAddress: newSelectedAddress, useTokenDetection }) => {
228
232
  const isSelectedAddressChanged = __privateGet(this, _selectedAddress) !== newSelectedAddress;
229
233
  const isDetectionChangedFromPreferences = __privateGet(this, _isDetectionEnabledFromPreferences) !== useTokenDetection;
@@ -238,6 +242,8 @@ registerEventListeners_fn = function() {
238
242
  );
239
243
  this.messagingSystem.subscribe(
240
244
  "AccountsController:selectedAccountChange",
245
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
246
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
241
247
  async ({ address: newSelectedAddress }) => {
242
248
  const isSelectedAddressChanged = __privateGet(this, _selectedAddress) !== newSelectedAddress;
243
249
  if (isSelectedAddressChanged) {
@@ -250,6 +256,8 @@ registerEventListeners_fn = function() {
250
256
  );
251
257
  this.messagingSystem.subscribe(
252
258
  "NetworkController:networkDidChange",
259
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
260
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
253
261
  async ({ selectedNetworkClientId }) => {
254
262
  const isNetworkClientIdChanged = __privateGet(this, _networkClientId) !== selectedNetworkClientId;
255
263
  const { chainId: newChainId } = __privateMethod(this, _getCorrectChainIdAndNetworkClientId, getCorrectChainIdAndNetworkClientId_fn).call(this, selectedNetworkClientId);
@@ -384,7 +392,11 @@ addDetectedTokens_fn = async function({
384
392
  category: "Wallet",
385
393
  properties: {
386
394
  tokens: eventTokensDetails,
395
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
396
+ // eslint-disable-next-line @typescript-eslint/naming-convention
387
397
  token_standard: "ERC20",
398
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
399
+ // eslint-disable-next-line @typescript-eslint/naming-convention
388
400
  asset_type: "TOKEN"
389
401
  }
390
402
  });
@@ -408,4 +420,4 @@ export {
408
420
  TokenDetectionController,
409
421
  TokenDetectionController_default
410
422
  };
411
- //# sourceMappingURL=chunk-MR6EF4B7.mjs.map
423
+ //# sourceMappingURL=chunk-3K3LE2CZ.mjs.map