@metamask-previews/assets-controllers 32.0.0-preview-c439acc5 → 32.0.0-preview-e2529f0
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/dist/AccountTrackerController.js +2 -2
- package/dist/AccountTrackerController.mjs +1 -1
- package/dist/AssetsContractController.js +6 -6
- package/dist/AssetsContractController.mjs +5 -5
- package/dist/NftController.js +3 -3
- package/dist/NftController.mjs +2 -2
- package/dist/Standards/ERC20Standard.js +3 -3
- package/dist/Standards/ERC20Standard.mjs +2 -2
- package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.js +3 -3
- package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.mjs +2 -2
- package/dist/Standards/NftStandards/ERC721/ERC721Standard.js +3 -3
- package/dist/Standards/NftStandards/ERC721/ERC721Standard.mjs +2 -2
- package/dist/TokenDetectionController.js +3 -3
- package/dist/TokenDetectionController.mjs +2 -2
- package/dist/TokenListController.js +4 -4
- package/dist/TokenListController.mjs +3 -3
- package/dist/TokenRatesController.js +4 -4
- package/dist/TokenRatesController.mjs +3 -3
- package/dist/TokensController.js +6 -6
- package/dist/TokensController.mjs +5 -5
- package/dist/assetsUtil.js +2 -2
- package/dist/assetsUtil.mjs +1 -1
- package/dist/{chunk-M4ZW6TYC.js → chunk-3NTGK7OO.js} +8 -8
- package/dist/chunk-3NTGK7OO.js.map +1 -0
- package/dist/{chunk-JUI3XNEF.js → chunk-5JDOJSUA.js} +14 -9
- package/dist/chunk-5JDOJSUA.js.map +1 -0
- package/dist/{chunk-ASA5RLBY.mjs → chunk-A3FHQNJH.mjs} +2 -2
- package/dist/{chunk-QHWKLZUC.js → chunk-AEM6O7HM.js} +14 -10
- package/dist/chunk-AEM6O7HM.js.map +1 -0
- package/dist/{chunk-LZ5ZGQEX.mjs → chunk-AMB5TO4Q.mjs} +2 -2
- package/dist/{chunk-5W5OO2Q5.mjs → chunk-BMMTR4HK.mjs} +2 -2
- package/dist/{chunk-XSXRLN3P.mjs → chunk-BWZXTNMF.mjs} +4 -4
- package/dist/chunk-BWZXTNMF.mjs.map +1 -0
- package/dist/{chunk-CGLUTXI7.js → chunk-F577WEB7.js} +5 -5
- package/dist/{chunk-D3K5MPMW.mjs → chunk-GRUCF6OP.mjs} +2 -2
- package/dist/{chunk-A2DDWXMS.mjs → chunk-GYWAJHCH.mjs} +10 -5
- package/dist/chunk-GYWAJHCH.mjs.map +1 -0
- package/dist/{chunk-HDI4L2DD.js → chunk-J2NHA3RU.js} +5 -5
- package/dist/{chunk-A7SULHJK.js → chunk-KJGV2U5Z.js} +1 -1
- package/dist/chunk-KJGV2U5Z.js.map +1 -0
- package/dist/{chunk-HLCGZGPA.mjs → chunk-LDG4TZCJ.mjs} +13 -9
- package/dist/chunk-LDG4TZCJ.mjs.map +1 -0
- package/dist/{chunk-62T7RKU3.mjs → chunk-MQUZBTRE.mjs} +2 -2
- package/dist/{chunk-MR6EF4B7.mjs → chunk-NY3GKW5L.mjs} +2 -2
- package/dist/{chunk-U3DJJN4X.js → chunk-OOQ23ZM4.js} +4 -4
- package/dist/{chunk-4AC3X2U5.js → chunk-OPPL2AVG.js} +19 -14
- package/dist/chunk-OPPL2AVG.js.map +1 -0
- package/dist/{chunk-PYMUBJQX.js → chunk-S3OXHT4M.js} +3 -3
- package/dist/{chunk-Q5JRBGWO.mjs → chunk-SXFTKOHZ.mjs} +2 -2
- package/dist/{chunk-Q5JRBGWO.mjs.map → chunk-SXFTKOHZ.mjs.map} +1 -1
- package/dist/{chunk-B5YY22QQ.js → chunk-T2UKYSM3.js} +3 -3
- package/dist/{chunk-7J63MEC3.mjs → chunk-TLABIOJD.mjs} +1 -1
- package/dist/chunk-TLABIOJD.mjs.map +1 -0
- package/dist/{chunk-Y7G5RKIF.js → chunk-TN227P74.js} +1 -3
- package/dist/chunk-TN227P74.js.map +1 -0
- package/dist/{chunk-NEXY7SE2.js → chunk-VJCRI43T.js} +2 -2
- package/dist/{chunk-NEXY7SE2.js.map → chunk-VJCRI43T.js.map} +1 -1
- package/dist/{chunk-BOTVAG4A.js → chunk-XHVPM73D.js} +5 -5
- package/dist/{chunk-G2ZRGQPQ.mjs → chunk-Y5ZJ2KRC.mjs} +1 -3
- package/dist/{chunk-G2ZRGQPQ.mjs.map → chunk-Y5ZJ2KRC.mjs.map} +1 -1
- package/dist/{chunk-73F3SN5O.mjs → chunk-ZJBJ2VBS.mjs} +15 -10
- package/dist/chunk-ZJBJ2VBS.mjs.map +1 -0
- package/dist/index.js +16 -16
- package/dist/index.mjs +14 -14
- package/dist/token-prices-service/codefi-v2.js +2 -2
- package/dist/token-prices-service/codefi-v2.mjs +1 -1
- package/dist/token-prices-service/index.js +2 -2
- package/dist/token-prices-service/index.mjs +1 -1
- package/dist/token-service.js +3 -3
- package/dist/token-service.mjs +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/AccountTrackerController.d.ts +3 -2
- package/dist/types/AccountTrackerController.d.ts.map +1 -1
- package/dist/types/AssetsContractController.d.ts.map +1 -1
- package/dist/types/TokenListController.d.ts.map +1 -1
- package/dist/types/TokenRatesController.d.ts +0 -1
- package/dist/types/TokenRatesController.d.ts.map +1 -1
- package/dist/types/TokensController.d.ts.map +1 -1
- package/dist/types/token-prices-service/abstract-token-prices-service.d.ts +0 -1
- package/dist/types/token-prices-service/abstract-token-prices-service.d.ts.map +1 -1
- package/dist/types/token-prices-service/codefi-v2.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4AC3X2U5.js.map +0 -1
- package/dist/chunk-73F3SN5O.mjs.map +0 -1
- package/dist/chunk-7J63MEC3.mjs.map +0 -1
- package/dist/chunk-A2DDWXMS.mjs.map +0 -1
- package/dist/chunk-A7SULHJK.js.map +0 -1
- package/dist/chunk-HLCGZGPA.mjs.map +0 -1
- package/dist/chunk-JUI3XNEF.js.map +0 -1
- package/dist/chunk-M4ZW6TYC.js.map +0 -1
- package/dist/chunk-QHWKLZUC.js.map +0 -1
- package/dist/chunk-XSXRLN3P.mjs.map +0 -1
- package/dist/chunk-Y7G5RKIF.js.map +0 -1
- /package/dist/{chunk-ASA5RLBY.mjs.map → chunk-A3FHQNJH.mjs.map} +0 -0
- /package/dist/{chunk-LZ5ZGQEX.mjs.map → chunk-AMB5TO4Q.mjs.map} +0 -0
- /package/dist/{chunk-5W5OO2Q5.mjs.map → chunk-BMMTR4HK.mjs.map} +0 -0
- /package/dist/{chunk-CGLUTXI7.js.map → chunk-F577WEB7.js.map} +0 -0
- /package/dist/{chunk-D3K5MPMW.mjs.map → chunk-GRUCF6OP.mjs.map} +0 -0
- /package/dist/{chunk-HDI4L2DD.js.map → chunk-J2NHA3RU.js.map} +0 -0
- /package/dist/{chunk-62T7RKU3.mjs.map → chunk-MQUZBTRE.mjs.map} +0 -0
- /package/dist/{chunk-MR6EF4B7.mjs.map → chunk-NY3GKW5L.mjs.map} +0 -0
- /package/dist/{chunk-U3DJJN4X.js.map → chunk-OOQ23ZM4.js.map} +0 -0
- /package/dist/{chunk-PYMUBJQX.js.map → chunk-S3OXHT4M.js.map} +0 -0
- /package/dist/{chunk-B5YY22QQ.js.map → chunk-T2UKYSM3.js.map} +0 -0
- /package/dist/{chunk-BOTVAG4A.js.map → chunk-XHVPM73D.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkVJCRI43Tjs = require('./chunk-VJCRI43T.js');
|
|
4
4
|
|
|
5
5
|
// src/Standards/NftStandards/ERC721/ERC721Standard.ts
|
|
6
6
|
var _contracts = require('@ethersproject/contracts');
|
|
@@ -133,7 +133,7 @@ var ERC721Standard = class {
|
|
|
133
133
|
_controllerutils.safelyExecute.call(void 0, () => this.getAssetName(address)),
|
|
134
134
|
tokenId ? _controllerutils.safelyExecute.call(void 0,
|
|
135
135
|
() => this.getTokenURI(address, tokenId).then(
|
|
136
|
-
(uri) => uri.startsWith("ipfs://") ?
|
|
136
|
+
(uri) => uri.startsWith("ipfs://") ? _chunkVJCRI43Tjs.getFormattedIpfsUrl.call(void 0, ipfsGateway, uri, true) : uri
|
|
137
137
|
)
|
|
138
138
|
) : void 0
|
|
139
139
|
]);
|
|
@@ -144,7 +144,7 @@ var ERC721Standard = class {
|
|
|
144
144
|
const object = await response.json();
|
|
145
145
|
image = object?.image;
|
|
146
146
|
if (image?.startsWith("ipfs://")) {
|
|
147
|
-
image =
|
|
147
|
+
image = _chunkVJCRI43Tjs.getFormattedIpfsUrl.call(void 0, ipfsGateway, image, true);
|
|
148
148
|
}
|
|
149
149
|
} catch {
|
|
150
150
|
}
|
|
@@ -175,4 +175,4 @@ var ERC721Standard = class {
|
|
|
175
175
|
|
|
176
176
|
|
|
177
177
|
exports.ERC721Standard = ERC721Standard;
|
|
178
|
-
//# sourceMappingURL=chunk-
|
|
178
|
+
//# sourceMappingURL=chunk-OOQ23ZM4.js.map
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkXHVPM73Djs = require('./chunk-XHVPM73D.js');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkT2UKYSM3js = require('./chunk-T2UKYSM3.js');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunkS3OXHT4Mjs = require('./chunk-S3OXHT4M.js');
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var _chunkVJCRI43Tjs = require('./chunk-VJCRI43T.js');
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
|
|
@@ -107,7 +107,8 @@ var TokensController = class extends _basecontroller.BaseController {
|
|
|
107
107
|
* Handles the event when the network changes.
|
|
108
108
|
*
|
|
109
109
|
* @param networkState - The changed network state.
|
|
110
|
-
* @param networkState.
|
|
110
|
+
* @param networkState.selectedNetworkClientId - The ID of the currently
|
|
111
|
+
* selected network client.
|
|
111
112
|
*/
|
|
112
113
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _onNetworkDidChange);
|
|
113
114
|
/**
|
|
@@ -242,12 +243,12 @@ var TokensController = class extends _basecontroller.BaseController {
|
|
|
242
243
|
address,
|
|
243
244
|
symbol,
|
|
244
245
|
decimals,
|
|
245
|
-
image: image ||
|
|
246
|
+
image: image || _chunkVJCRI43Tjs.formatIconUrlWithProxy.call(void 0, {
|
|
246
247
|
chainId: currentChainId,
|
|
247
248
|
tokenAddress: address
|
|
248
249
|
}),
|
|
249
250
|
isERC721,
|
|
250
|
-
aggregators:
|
|
251
|
+
aggregators: _chunkVJCRI43Tjs.formatAggregatorNames.call(void 0, tokenMetadata?.aggregators || []),
|
|
251
252
|
name
|
|
252
253
|
};
|
|
253
254
|
const previousIndex = newTokens.findIndex(
|
|
@@ -517,7 +518,7 @@ var TokensController = class extends _basecontroller.BaseController {
|
|
|
517
518
|
}
|
|
518
519
|
const provider = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getProvider, getProvider_fn).call(this, networkClientId);
|
|
519
520
|
const isErc1155 = await _controllerutils.safelyExecute.call(void 0,
|
|
520
|
-
() => new (0,
|
|
521
|
+
() => new (0, _chunkXHVPM73Djs.ERC1155Standard)(provider).contractSupportsBase1155Interface(
|
|
521
522
|
asset.address
|
|
522
523
|
)
|
|
523
524
|
);
|
|
@@ -526,7 +527,7 @@ var TokensController = class extends _basecontroller.BaseController {
|
|
|
526
527
|
`Contract ${asset.address} must match type ${type}, but was detected as ${_controllerutils.ERC1155}`
|
|
527
528
|
);
|
|
528
529
|
}
|
|
529
|
-
const erc20 = new (0,
|
|
530
|
+
const erc20 = new (0, _chunkT2UKYSM3js.ERC20Standard)(provider);
|
|
530
531
|
const [contractName, contractSymbol, contractDecimals] = await Promise.all([
|
|
531
532
|
_controllerutils.safelyExecute.call(void 0, () => erc20.getTokenName(asset.address)),
|
|
532
533
|
_controllerutils.safelyExecute.call(void 0, () => erc20.getTokenSymbol(asset.address)),
|
|
@@ -605,9 +606,13 @@ _selectedAddress = new WeakMap();
|
|
|
605
606
|
_provider = new WeakMap();
|
|
606
607
|
_abortController = new WeakMap();
|
|
607
608
|
_onNetworkDidChange = new WeakSet();
|
|
608
|
-
onNetworkDidChange_fn = function({
|
|
609
|
+
onNetworkDidChange_fn = function({ selectedNetworkClientId }) {
|
|
610
|
+
const selectedNetworkClient = this.messagingSystem.call(
|
|
611
|
+
"NetworkController:getNetworkClientById",
|
|
612
|
+
selectedNetworkClientId
|
|
613
|
+
);
|
|
609
614
|
const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state;
|
|
610
|
-
const { chainId } =
|
|
615
|
+
const { chainId } = selectedNetworkClient.configuration;
|
|
611
616
|
_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _abortController).abort();
|
|
612
617
|
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _abortController, new AbortController());
|
|
613
618
|
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _chainId, chainId);
|
|
@@ -630,14 +635,14 @@ onPreferenceControllerStateChange_fn = function({ selectedAddress }) {
|
|
|
630
635
|
_fetchTokenMetadata = new WeakSet();
|
|
631
636
|
fetchTokenMetadata_fn = async function(tokenAddress) {
|
|
632
637
|
try {
|
|
633
|
-
const token = await
|
|
638
|
+
const token = await _chunkS3OXHT4Mjs.fetchTokenMetadata.call(void 0,
|
|
634
639
|
_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _chainId),
|
|
635
640
|
tokenAddress,
|
|
636
641
|
_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _abortController).signal
|
|
637
642
|
);
|
|
638
643
|
return token;
|
|
639
644
|
} catch (error) {
|
|
640
|
-
if (error instanceof Error && error.message.includes(
|
|
645
|
+
if (error instanceof Error && error.message.includes(_chunkS3OXHT4Mjs.TOKEN_METADATA_NO_SUPPORT_ERROR)) {
|
|
641
646
|
return void 0;
|
|
642
647
|
}
|
|
643
648
|
throw error;
|
|
@@ -768,4 +773,4 @@ var TokensController_default = TokensController;
|
|
|
768
773
|
|
|
769
774
|
|
|
770
775
|
exports.getDefaultTokensState = getDefaultTokensState; exports.TokensController = TokensController; exports.TokensController_default = TokensController_default;
|
|
771
|
-
//# sourceMappingURL=chunk-
|
|
776
|
+
//# sourceMappingURL=chunk-OPPL2AVG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/TokensController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAO7B,SAAS,sBAAsB;AAC/B,OAAO,kBAAkB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAY1B,SAAS,iBAAiB;AAE1B,SAAS,aAAa;AACtB,SAAS,MAAM,cAAc;AAsD7B,IAAM,WAAW;AAAA,EACf,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,eAAe;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,WAAW;AAAA,IACT,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,kBAAkB;AAAA,IAChB,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,mBAAmB;AAAA,IACjB,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AACF;AAEA,IAAM,iBAAiB;AA8ChB,IAAM,wBAAwB,MAA6B;AAChE,SAAO;AAAA,IACL,QAAQ,CAAC;AAAA,IACT,eAAe,CAAC;AAAA,IAChB,gBAAgB,CAAC;AAAA,IACjB,WAAW,CAAC;AAAA,IACZ,kBAAkB,CAAC;AAAA,IACnB,mBAAmB,CAAC;AAAA,EACtB;AACF;AA5KA;AAiLO,IAAM,mBAAN,cAA+B,eAIpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,YAAY;AAAA,IACV,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAMG;AACD,UAAM;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,GAAG,sBAAsB;AAAA,QACzB,GAAG;AAAA,MACL;AAAA,IACF,CAAC;AA2CH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAiYN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AA6BN;AAYA;AAUA;AA6JA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwFA,uBAAM;AAj0BN,uBAAS,QAAS,IAAI,MAAM;AAE5B;AAEA;AAEA;AAEA;AAkCE,uBAAK,UAAW;AAEhB,uBAAK,WAAY;AAEjB,uBAAK,kBAAmB;AAExB,uBAAK,kBAAmB,IAAI,gBAAgB;AAE5C,SAAK,gBAAgB;AAAA,MACnB,GAAG,cAAc;AAAA,MACjB,KAAK,kBAAkB,KAAK,IAAI;AAAA,IAClC;AAEA,SAAK,gBAAgB;AAAA,MACnB;AAAA,MACA,sBAAK,0EAAmC,KAAK,IAAI;AAAA,IACnD;AAEA,SAAK,gBAAgB;AAAA,MACnB;AAAA,MACA,sBAAK,4CAAoB,KAAK,IAAI;AAAA,IACpC;AAEA,SAAK,gBAAgB;AAAA,MACnB;AAAA,MACA,CAAC,EAAE,UAAU,MAAM;AACjB,cAAM,EAAE,OAAO,IAAI,KAAK;AACxB,YAAI,OAAO,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM;AACpC,gCAAK,kDAAL,WAA4B,WAAW;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqFA,MAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAQqB;AACnB,UAAM,UAAU,mBAAK;AACrB,UAAM,kBAAkB,mBAAK;AAC7B,UAAM,cAAc,MAAM,mBAAK,QAAO,QAAQ;AAC9C,UAAM,EAAE,WAAW,kBAAkB,kBAAkB,IAAI,KAAK;AAChE,QAAI,iBAAiB;AACrB,QAAI,iBAAiB;AACnB,uBAAiB,KAAK,gBAAgB;AAAA,QACpC;AAAA,QACA;AAAA,MACF,EAAE,cAAc;AAAA,IAClB;AAEA,UAAM,iBAAiB,sBAAsB;AAC7C,UAAM,iCAAiC,mBAAmB;AAE1D,QAAI;AACF,gBAAU,qBAAqB,OAAO;AACtC,YAAM,SAAS,UAAU,cAAc,IAAI,cAAc,KAAK,CAAC;AAC/D,YAAM,gBACJ,iBAAiB,cAAc,IAAI,cAAc,KAAK,CAAC;AACzD,YAAM,iBACJ,kBAAkB,cAAc,IAAI,cAAc,KAAK,CAAC;AAC1D,YAAM,YAAqB,CAAC,GAAG,MAAM;AACrC,YAAM,CAAC,UAAU,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,QAClD,sBAAK,oCAAL,WAAqB,SAAS;AAAA;AAAA,QAE9B,sBAAK,4CAAL,WAAyB;AAAA,MAC3B,CAAC;AAED,UAAI,CAAC,mBAAmB,mBAAmB,mBAAK,WAAU;AACxD,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,WAAkB;AAAA,QACtB;AAAA,QACA;AAAA,QACA;AAAA,QACA,OACE,SACA,uBAAuB;AAAA,UACrB,SAAS;AAAA,UACT,cAAc;AAAA,QAChB,CAAC;AAAA,QACH;AAAA,QACA,aAAa,sBAAsB,eAAe,eAAe,CAAC,CAAC;AAAA,QACnE;AAAA,MACF;AACA,YAAM,gBAAgB,UAAU;AAAA,QAC9B,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM,QAAQ,YAAY;AAAA,MACjE;AACA,UAAI,kBAAkB,IAAI;AACxB,kBAAU,aAAa,IAAI;AAAA,MAC7B,OAAO;AACL,kBAAU,KAAK,QAAQ;AAAA,MACzB;AAEA,YAAM,mBAAmB,cAAc;AAAA,QACrC,CAAC,iBAAiB,aAAa,YAAY,MAAM,QAAQ,YAAY;AAAA,MACvE;AACA,YAAM,oBAAoB,eAAe;AAAA,QACvC,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM,QAAQ,YAAY;AAAA,MACjE;AAEA,YAAM,EAAE,cAAc,qBAAqB,qBAAqB,IAC9D,sBAAK,gDAAL,WAA2B;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA,oBAAoB;AAAA,QACpB,oBAAoB;AAAA,MACtB;AAEF,UAAI,WAA2C;AAAA,QAC7C,WAAW;AAAA,QACX,kBAAkB;AAAA,QAClB,mBAAmB;AAAA,MACrB;AAGA,UAAI,gCAAgC;AAClC,mBAAW;AAAA,UACT,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,gBAAgB;AAAA,QAClB;AAAA,MACF;AAEA,WAAK,OAAO,CAAC,UAAU;AACrB,eAAO,OAAO,OAAO,QAAQ;AAAA,MAC/B,CAAC;AACD,aAAO;AAAA,IACT,UAAE;AACA,kBAAY;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,gBAAyB,iBAAmC;AAC1E,UAAM,cAAc,MAAM,mBAAK,QAAO,QAAQ;AAC9C,UAAM,EAAE,QAAQ,gBAAgB,cAAc,IAAI,KAAK;AACvD,UAAM,oBAA6C,CAAC;AAEpD,UAAM,eAAe,OAAO,OAAO,CAAC,QAAQ,YAAY;AACtD,aAAO,QAAQ,OAAO,IAAI;AAC1B,aAAO;AAAA,IACT,GAAG,CAAC,CAAiC;AACrC,QAAI;AACF,qBAAe,QAAQ,CAAC,eAAe;AACrC,cAAM,EAAE,SAAS,QAAQ,UAAU,OAAO,aAAa,KAAK,IAC1D;AACF,cAAM,kBAAkB,qBAAqB,OAAO;AACpD,cAAM,iBAAwB;AAAA,UAC5B,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,qBAAa,OAAO,IAAI;AACxB,0BAAkB,QAAQ,YAAY,CAAC,IAAI;AAC3C,eAAO;AAAA,MACT,CAAC;AACD,YAAM,YAAY,OAAO,OAAO,YAAY;AAE5C,YAAM,oBAAoB,eAAe;AAAA,QACvC,CAAC,UAAU,CAAC,kBAAkB,MAAM,QAAQ,YAAY,CAAC;AAAA,MAC3D;AACA,YAAM,mBAAmB,cAAc;AAAA,QACrC,CAAC,iBAAiB,CAAC,aAAa,aAAa,YAAY,CAAC;AAAA,MAC5D;AAEA,UAAI;AACJ,UAAI,iBAAiB;AACnB,6BAAqB,KAAK,gBAAgB;AAAA,UACxC;AAAA,UACA;AAAA,QACF,EAAE,cAAc;AAAA,MAClB;AAEA,YAAM,EAAE,cAAc,sBAAsB,oBAAoB,IAC9D,sBAAK,gDAAL,WAA2B;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEF,WAAK,OAAO,CAAC,UAAU;AACrB,cAAM,SAAS;AACf,cAAM,YAAY;AAClB,cAAM,iBAAiB;AACvB,cAAM,oBAAoB;AAC1B,cAAM,gBAAgB;AACtB,cAAM,mBAAmB;AAAA,MAC3B,CAAC;AAAA,IACH,UAAE;AACA,kBAAY;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,wBAAkC;AAC7C,UAAM,EAAE,eAAe,gBAAgB,OAAO,IAAI,KAAK;AACvD,UAAM,mBAA4C,CAAC;AACnD,QAAI,mBAA6B,CAAC,GAAG,aAAa;AAElD,UAAM,4BAA4B,uBAAuB,IAAI,CAAC,YAAY;AACxE,YAAM,kBAAkB,qBAAqB,OAAO;AACpD,uBAAiB,QAAQ,YAAY,CAAC,IAAI;AAC1C,aAAO;AAAA,IACT,CAAC;AACD,uBAAmB,CAAC,GAAG,eAAe,GAAG,yBAAyB;AAClE,UAAM,oBAAoB,eAAe;AAAA,MACvC,CAAC,UAAU,CAAC,iBAAiB,MAAM,QAAQ,YAAY,CAAC;AAAA,IAC1D;AACA,UAAM,YAAY,OAAO;AAAA,MACvB,CAAC,UAAU,CAAC,iBAAiB,MAAM,QAAQ,YAAY,CAAC;AAAA,IAC1D;AAEA,UAAM,EAAE,qBAAqB,sBAAsB,aAAa,IAC9D,sBAAK,gDAAL,WAA2B;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEF,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,gBAAgB;AACtB,YAAM,SAAS;AACf,YAAM,iBAAiB;AACvB,YAAM,mBAAmB;AACzB,YAAM,oBAAoB;AAC1B,YAAM,YAAY;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,kBACJ,wBACA,kBACA;AACA,UAAM,cAAc,MAAM,mBAAK,QAAO,QAAQ;AAG9C,UAAM,UAAU,kBAAkB,WAAW,mBAAK;AAClD,UAAM,iBACJ,kBAAkB,mBAAmB,mBAAK;AAE5C,UAAM,EAAE,WAAW,mBAAmB,iBAAiB,IAAI,KAAK;AAChE,QAAI,YAAY,CAAC,GAAI,YAAY,OAAO,IAAI,cAAc,KAAK,CAAC,CAAE;AAClE,QAAI,oBAAoB;AAAA,MACtB,GAAI,oBAAoB,OAAO,IAAI,cAAc,KAAK,CAAC;AAAA,IACzD;AAEA,QAAI;AACF,6BAAuB,QAAQ,CAAC,eAAe;AAC7C,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,IAAI;AACJ,cAAM,kBAAkB,qBAAqB,OAAO;AACpD,cAAM,WAAkB;AAAA,UACtB,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,cAAM,wBAAwB,UAAU;AAAA,UACtC,CAAC,UACC,MAAM,QAAQ,YAAY,MAAM,gBAAgB,YAAY;AAAA,QAChE;AACA,YAAI,0BAA0B,IAAI;AAEhC,oBAAU,qBAAqB,IAAI;AAAA,QACrC,OAAO;AACL,gBAAM,oBACJ,mBAAmB,OAAO,IAAI,cAAc,GAAG,QAAQ,OAAO,KAC9D;AAEF,cAAI,sBAAsB,IAAI;AAE5B,kBAAM,wBAAwB,kBAAkB;AAAA,cAC9C,CAAC,UACC,MAAM,QAAQ,YAAY,MAAM,gBAAgB,YAAY;AAAA,YAChE;AACA,gBAAI,0BAA0B,IAAI;AAChC,gCAAkB,qBAAqB,IAAI;AAAA,YAC7C,OAAO;AACL,gCAAkB,KAAK,QAAQ;AAAA,YACjC;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,YAAM,EAAE,cAAc,qBAAqB,IAAI,sBAAK,gDAAL,WAC7C;AAAA,QACE;AAAA,QACA;AAAA,QACA,oBAAoB;AAAA,QACpB,oBAAoB;AAAA,MACtB;AAKF,kBAAY,eAAe,mBAAK,SAAQ,IAAI,mBAAK,iBAAgB,KAAK,CAAC;AACvE,0BACE,uBAAuB,mBAAK,SAAQ,IAAI,mBAAK,iBAAgB,KAAK,CAAC;AAErE,WAAK,OAAO,CAAC,UAAU;AACrB,cAAM,SAAS;AACf,cAAM,YAAY;AAClB,cAAM,iBAAiB;AACvB,cAAM,oBAAoB;AAAA,MAC5B,CAAC;AAAA,IACH,UAAE;AACA,kBAAY;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,gBAAgB,cAAsB;AAC1C,UAAM,WAAW,MAAM,sBAAK,oCAAL,WAAqB;AAC5C,UAAM,SAAS,CAAC,GAAG,KAAK,MAAM,MAAM;AACpC,UAAM,aAAa,OAAO,UAAU,CAAC,UAAU;AAC7C,aAAO,MAAM,QAAQ,YAAY,MAAM,aAAa,YAAY;AAAA,IAClE,CAAC;AACD,UAAM,eAAe,EAAE,GAAG,OAAO,UAAU,GAAG,SAAS;AACvD,WAAO,UAAU,IAAI;AACrB,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,SAAS;AAAA,IACjB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqGA,MAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAKkB;AAChB,QAAI,SAAS,OAAO;AAClB,YAAM,IAAI,MAAM,iBAAiB,IAAI,gBAAgB;AAAA,IACvD;AAEA,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,UAAU,cAAc,2BAA2B;AAAA,IAC3D;AAEA,QAAI,CAAC,kBAAkB,MAAM,OAAO,GAAG;AACrC,YAAM,UAAU,cAAc,oBAAoB,MAAM,OAAO,GAAG;AAAA,IACpE;AAIA,QAAI,MAAM,sBAAK,oCAAL,WAAqB,MAAM,SAAS,kBAAkB;AAC9D,YAAM,UAAU;AAAA,QACd,YAAY,MAAM,OAAO,oBAAoB,IAAI,yBAAyB,MAAM;AAAA,MAClF;AAAA,IACF;AAEA,UAAM,WAAW,sBAAK,8BAAL,WAAkB;AACnC,UAAM,YAAY,MAAM;AAAA,MAAc,MACpC,IAAI,gBAAgB,QAAQ,EAAE;AAAA,QAC5B,MAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,WAAW;AACb,YAAM,UAAU;AAAA,QACd,YAAY,MAAM,OAAO,oBAAoB,IAAI,yBAAyB,OAAO;AAAA,MACnF;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,cAAc,QAAQ;AACxC,UAAM,CAAC,cAAc,gBAAgB,gBAAgB,IAAI,MAAM,QAAQ,IAAI;AAAA,MACzE,cAAc,MAAM,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,MACrD,cAAc,MAAM,MAAM,eAAe,MAAM,OAAO,CAAC;AAAA,MACvD,cAAc,YAAY,MAAM,iBAAiB,MAAM,OAAO,CAAC;AAAA,IACjE,CAAC;AAED,UAAM,OAAO;AAIb,QAAI,CAAC,MAAM,UAAU,CAAC,gBAAgB;AACpC,YAAM,UAAU;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAEA,QACE,mBAAmB,UACnB,MAAM,WAAW,UACjB,MAAM,OAAO,YAAY,MAAM,eAAe,YAAY,GAC1D;AACA,YAAM,UAAU;AAAA,QACd,8BAA8B,MAAM,MAAM,gDAAgD,cAAc;AAAA,MAC1G;AAAA,IACF;AAEA,UAAM,SAAS,kBAAkB,MAAM;AACvC,QAAI,OAAO,MAAM,WAAW,UAAU;AACpC,YAAM,UAAU,cAAc,8BAA8B;AAAA,IAC9D;AAEA,QAAI,MAAM,OAAO,SAAS,IAAI;AAC5B,YAAM,UAAU;AAAA,QACd,mBAAmB,MAAM,MAAM;AAAA,MACjC;AAAA,IACF;AAIA,QAAI,MAAM,aAAa,UAAa,qBAAqB,QAAW;AAClE,YAAM,UAAU;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAEA,QACE,qBAAqB,UACrB,MAAM,aAAa,UACnB,OAAO,MAAM,QAAQ,MAAM,kBAC3B;AACA,YAAM,UAAU;AAAA,QACd,gCAAgC,MAAM,QAAQ,gDAAgD,gBAAgB;AAAA,MAChH;AAAA,IACF;AAEA,UAAM,cAAc,oBAAoB,MAAM;AAC9C,UAAM,cAAc,SAAS,aAAkC,EAAE;AACjE,QAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,MAAM,cAAc,GAAG;AACzE,YAAM,UAAU;AAAA,QACd,qBAAqB,WAAW;AAAA,MAClC;AAAA,IACF;AACA,UAAM,WAAW;AAEjB,UAAM,qBAAyC;AAAA,MAC7C;AAAA,MACA,IAAI,sBAAK,wCAAL;AAAA,MACJ,MAAM,KAAK,IAAI;AAAA,MACf;AAAA,MACA,oBAAoB,sBAAsB,mBAAK;AAAA,IACjD;AAEA,UAAM,sBAAK,sCAAL,WAAsB;AAE5B,UAAM,EAAE,SAAS,QAAQ,UAAU,MAAM,MAAM,IAAI;AACnD,UAAM,KAAK,SAAS;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAoB,mBAAmB;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EA+FA,qBAAqB;AACnB,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,gBAAgB,CAAC;AACvB,YAAM,mBAAmB,CAAC;AAAA,IAC5B,CAAC;AAAA,EACH;AAuBF;AAt1BW;AAET;AAEA;AAEA;AAEA;AA2EA;AAAA,wBAAmB,SAAC,EAAE,wBAAwB,GAAiB;AAC7D,QAAM,wBAAwB,KAAK,gBAAgB;AAAA,IACjD;AAAA,IACA;AAAA,EACF;AACA,QAAM,EAAE,WAAW,kBAAkB,kBAAkB,IAAI,KAAK;AAChE,QAAM,EAAE,QAAQ,IAAI,sBAAsB;AAC1C,qBAAK,kBAAiB,MAAM;AAC5B,qBAAK,kBAAmB,IAAI,gBAAgB;AAC5C,qBAAK,UAAW;AAChB,OAAK,OAAO,CAAC,UAAU;AACrB,UAAM,SAAS,UAAU,OAAO,IAAI,mBAAK,iBAAgB,KAAK,CAAC;AAC/D,UAAM,gBACJ,iBAAiB,OAAO,IAAI,mBAAK,iBAAgB,KAAK,CAAC;AACzD,UAAM,iBACJ,kBAAkB,OAAO,IAAI,mBAAK,iBAAgB,KAAK,CAAC;AAAA,EAC5D,CAAC;AACH;AAOA;AAAA,uCAAkC,SAAC,EAAE,gBAAgB,GAAqB;AACxE,QAAM,EAAE,WAAW,kBAAkB,kBAAkB,IAAI,KAAK;AAChE,qBAAK,kBAAmB;AACxB,OAAK,OAAO,CAAC,UAAU;AACrB,UAAM,SAAS,UAAU,mBAAK,SAAQ,IAAI,eAAe,KAAK,CAAC;AAC/D,UAAM,gBACJ,iBAAiB,mBAAK,SAAQ,IAAI,eAAe,KAAK,CAAC;AACzD,UAAM,iBACJ,kBAAkB,mBAAK,SAAQ,IAAI,eAAe,KAAK,CAAC;AAAA,EAC5D,CAAC;AACH;AAQM;AAAA,wBAAmB,eACvB,cACqC;AACrC,MAAI;AACF,UAAM,QAAQ,MAAM;AAAA,MAClB,mBAAK;AAAA,MACL;AAAA,MACA,mBAAK,kBAAiB;AAAA,IACxB;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QACE,iBAAiB,SACjB,MAAM,QAAQ,SAAS,+BAA+B,GACtD;AACA,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AA8WA;AAAA,2BAAsB,SACpB,WACA,gBACA;AACA,QAAM,EAAE,OAAO,IAAI,KAAK;AAExB,QAAM,YAAY,OAAO,IAAI,CAAC,UAAU;AACtC,UAAM,WAAW,UAAU,MAAM,QAAQ,YAAY,CAAC;AAEtD,WAAO,CAAC,MAAM,cAAc,KAAK,WAAW,cAAc,IACtD,EAAE,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,cAAc,EAAE,IACvD,EAAE,GAAG,MAAM;AAAA,EACjB,CAAC;AAED,OAAK,OAAO,CAAC,UAAU;AACrB,UAAM,SAAS;AAAA,EACjB,CAAC;AACH;AAUM;AAAA,oBAAe,eACnB,cACA,iBACA;AACA,QAAM,kBAAkB,qBAAqB,YAAY;AAGzD,MAAI,aAAa,eAAe,GAAG,WAAW,MAAM;AAClD,WAAO,QAAQ,QAAQ,IAAI;AAAA,EAC7B,WAAW,aAAa,eAAe,GAAG,UAAU,MAAM;AACxD,WAAO,QAAQ,QAAQ,KAAK;AAAA,EAC9B;AAEA,QAAM,gBAAgB,sBAAK,gDAAL,WACpB,cACA,WACA;AAEF,MAAI;AACF,WAAO,MAAM,cAAc,kBAAkB,mBAAmB;AAAA,EAClE,SAAS,OAAO;AAKd,WAAO;AAAA,EACT;AACF;AAEA;AAAA,iBAAY,SAAC,iBAAiD;AAC5D,SAAO,IAAI;AAAA;AAAA,IAET,kBACI,KAAK,gBAAgB;AAAA,MACnB;AAAA,MACA;AAAA,IACF,EAAE,WACF,mBAAK;AAAA,EACX;AACF;AAEA;AAAA,0BAAqB,SACnB,cACA,KACA,iBACU;AACV,QAAM,eAAe,sBAAK,8BAAL,WAAkB;AACvC,QAAM,gBAAgB,IAAI,SAAS,cAAc,KAAK,YAAY;AAClE,SAAO;AACT;AAEA;AAAA,sBAAiB,WAAW;AAC1B,SAAO,OAAO;AAChB;AA2JA;AAAA,0BAAqB,SAAC,QAMnB;AACD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,EAAE,WAAW,kBAAkB,kBAAkB,IAAI,KAAK;AAEhE,QAAM,yBAAyB,sBAAsB,mBAAK;AAC1D,QAAM,qBAAqB,sBAAsB,mBAAK;AAEtD,MAAI,eAAe;AACnB,MACE,WAAW,UACV,aACC,aACA,UAAU,kBAAkB,KAC5B,UAAU,kBAAkB,EAAE,sBAAsB,GACtD;AACA,UAAM,gBAAgB,UAAU,kBAAkB;AAClD,UAAM,mBAAmB;AAAA,MACvB,GAAG;AAAA,MACH,GAAG,EAAE,CAAC,sBAAsB,GAAG,UAAU;AAAA,IAC3C;AACA,mBAAe;AAAA,MACb,GAAG;AAAA,MACH,GAAG,EAAE,CAAC,kBAAkB,GAAG,iBAAiB;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI,sBAAsB;AAC1B,MACE,kBAAkB,UACjB,oBACC,oBACA,iBAAiB,kBAAkB,KACnC,iBAAiB,kBAAkB,EAAE,sBAAsB,GAC7D;AACA,UAAM,uBAAuB,iBAAiB,kBAAkB;AAChE,UAAM,0BAA0B;AAAA,MAC9B,GAAG;AAAA,MACH,GAAG,EAAE,CAAC,sBAAsB,GAAG,iBAAiB;AAAA,IAClD;AACA,0BAAsB;AAAA,MACpB,GAAG;AAAA,MACH,GAAG,EAAE,CAAC,kBAAkB,GAAG,wBAAwB;AAAA,IACrD;AAAA,EACF;AAEA,MAAI,uBAAuB;AAC3B,MACE,mBAAmB,UAClB,qBACC,qBACA,kBAAkB,kBAAkB,KACpC,kBAAkB,kBAAkB,EAAE,sBAAsB,GAC9D;AACA,UAAM,wBAAwB,kBAAkB,kBAAkB;AAClE,UAAM,2BAA2B;AAAA,MAC/B,GAAG;AAAA,MACH,GAAG,EAAE,CAAC,sBAAsB,GAAG,kBAAkB;AAAA,IACnD;AACA,2BAAuB;AAAA,MACrB,GAAG;AAAA,MACH,GAAG,EAAE,CAAC,kBAAkB,GAAG,yBAAyB;AAAA,IACtD;AAAA,EACF;AACA,SAAO,EAAE,cAAc,qBAAqB,qBAAqB;AACnE;AAYM;AAAA,qBAAgB,eAAC,oBAAwC;AAC7D,SAAO,KAAK,gBAAgB;AAAA,IAC1B;AAAA,IACA;AAAA,MACE,IAAI,mBAAmB;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,aAAa;AAAA,MACnB,aAAa;AAAA,QACX,IAAI,mBAAmB;AAAA,QACvB,oBAAoB,mBAAmB;AAAA,QACvC,OAAO;AAAA,UACL,SAAS,mBAAmB,MAAM;AAAA,UAClC,UAAU,mBAAmB,MAAM;AAAA,UACnC,QAAQ,mBAAmB,MAAM;AAAA,UACjC,OAAO,mBAAmB,MAAM,SAAS;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAGF,IAAO,2BAAQ","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type { AddApprovalRequest } from '@metamask/approval-controller';\nimport type {\n RestrictedControllerMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport contractsMap from '@metamask/contract-metadata';\nimport {\n toChecksumHexAddress,\n ERC721_INTERFACE_ID,\n ORIGIN_METAMASK,\n ApprovalType,\n ERC20,\n ERC721,\n ERC1155,\n isValidHexAddress,\n safelyExecute,\n} from '@metamask/controller-utils';\nimport { abiERC721 } from '@metamask/metamask-eth-abis';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerNetworkDidChangeEvent,\n NetworkState,\n Provider,\n} from '@metamask/network-controller';\nimport type {\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { v1 as random } from 'uuid';\n\nimport { formatAggregatorNames, formatIconUrlWithProxy } from './assetsUtil';\nimport { ERC20Standard } from './Standards/ERC20Standard';\nimport { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standard';\nimport {\n fetchTokenMetadata,\n TOKEN_METADATA_NO_SUPPORT_ERROR,\n} from './token-service';\nimport type {\n TokenListMap,\n TokenListStateChange,\n TokenListToken,\n} from './TokenListController';\nimport type { Token } from './TokenRatesController';\n\n/**\n * @type SuggestedAssetMeta\n *\n * Suggested asset by EIP747 meta data\n * @property id - Generated UUID associated with this suggested asset\n * @property time - Timestamp associated with this this suggested asset\n * @property type - Type type this suggested asset\n * @property asset - Asset suggested object\n * @property interactingAddress - Account address that requested watch asset\n */\ntype SuggestedAssetMeta = {\n id: string;\n time: number;\n type: string;\n asset: Token;\n interactingAddress: string;\n};\n\n/**\n * @type TokensControllerState\n *\n * Assets controller state\n * @property tokens - List of tokens associated with the active network and address pair\n * @property ignoredTokens - List of ignoredTokens associated with the active network and address pair\n * @property detectedTokens - List of detected tokens associated with the active network and address pair\n * @property allTokens - Object containing tokens by network and account\n * @property allIgnoredTokens - Object containing hidden/ignored tokens by network and account\n * @property allDetectedTokens - Object containing tokens detected with non-zero balances\n */\nexport type TokensControllerState = {\n tokens: Token[];\n ignoredTokens: string[];\n detectedTokens: Token[];\n allTokens: { [chainId: Hex]: { [key: string]: Token[] } };\n allIgnoredTokens: { [chainId: Hex]: { [key: string]: string[] } };\n allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } };\n};\n\nconst metadata = {\n tokens: {\n persist: true,\n anonymous: false,\n },\n ignoredTokens: {\n persist: true,\n anonymous: false,\n },\n detectedTokens: {\n persist: true,\n anonymous: false,\n },\n allTokens: {\n persist: true,\n anonymous: false,\n },\n allIgnoredTokens: {\n persist: true,\n anonymous: false,\n },\n allDetectedTokens: {\n persist: true,\n anonymous: false,\n },\n};\n\nconst controllerName = 'TokensController';\n\nexport type TokensControllerActions =\n | TokensControllerGetStateAction\n | TokensControllerAddDetectedTokensAction;\n\nexport type TokensControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokensControllerState\n>;\n\nexport type TokensControllerAddDetectedTokensAction = {\n type: `${typeof controllerName}:addDetectedTokens`;\n handler: TokensController['addDetectedTokens'];\n};\n\n/**\n * The external actions available to the {@link TokensController}.\n */\nexport type AllowedActions =\n | AddApprovalRequest\n | NetworkControllerGetNetworkClientByIdAction;\n\nexport type TokensControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n TokensControllerState\n>;\n\nexport type TokensControllerEvents = TokensControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | NetworkControllerNetworkDidChangeEvent\n | PreferencesControllerStateChangeEvent\n | TokenListStateChange;\n\n/**\n * The messenger of the {@link TokensController}.\n */\nexport type TokensControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokensControllerActions | AllowedActions,\n TokensControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport const getDefaultTokensState = (): TokensControllerState => {\n return {\n tokens: [],\n ignoredTokens: [],\n detectedTokens: [],\n allTokens: {},\n allIgnoredTokens: {},\n allDetectedTokens: {},\n };\n};\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class TokensController extends BaseController<\n typeof controllerName,\n TokensControllerState,\n TokensControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n #chainId: Hex;\n\n #selectedAddress: string;\n\n #provider: Provider | undefined;\n\n #abortController: AbortController;\n\n /**\n * Tokens controller options\n * @param options - Constructor options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.selectedAddress - Vault selected address\n * @param options.provider - Network provider.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messenger.\n */\n constructor({\n chainId: initialChainId,\n selectedAddress,\n provider,\n state,\n messenger,\n }: {\n chainId: Hex;\n selectedAddress: string;\n provider: Provider | undefined;\n state?: Partial<TokensControllerState>;\n messenger: TokensControllerMessenger;\n }) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokensState(),\n ...state,\n },\n });\n\n this.#chainId = initialChainId;\n\n this.#provider = provider;\n\n this.#selectedAddress = selectedAddress;\n\n this.#abortController = new AbortController();\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:addDetectedTokens` as const,\n this.addDetectedTokens.bind(this),\n );\n\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferenceControllerStateChange.bind(this),\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:networkDidChange',\n this.#onNetworkDidChange.bind(this),\n );\n\n this.messagingSystem.subscribe(\n 'TokenListController:stateChange',\n ({ tokenList }) => {\n const { tokens } = this.state;\n if (tokens.length && !tokens[0].name) {\n this.#updateTokensAttribute(tokenList, 'name');\n }\n },\n );\n }\n\n /**\n * Handles the event when the network changes.\n *\n * @param networkState - The changed network state.\n * @param networkState.selectedNetworkClientId - The ID of the currently\n * selected network client.\n */\n #onNetworkDidChange({ selectedNetworkClientId }: NetworkState) {\n const selectedNetworkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state;\n const { chainId } = selectedNetworkClient.configuration;\n this.#abortController.abort();\n this.#abortController = new AbortController();\n this.#chainId = chainId;\n this.update((state) => {\n state.tokens = allTokens[chainId]?.[this.#selectedAddress] || [];\n state.ignoredTokens =\n allIgnoredTokens[chainId]?.[this.#selectedAddress] || [];\n state.detectedTokens =\n allDetectedTokens[chainId]?.[this.#selectedAddress] || [];\n });\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.selectedAddress - The current selected address of the preference controller.\n */\n #onPreferenceControllerStateChange({ selectedAddress }: PreferencesState) {\n const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state;\n this.#selectedAddress = selectedAddress;\n this.update((state) => {\n state.tokens = allTokens[this.#chainId]?.[selectedAddress] ?? [];\n state.ignoredTokens =\n allIgnoredTokens[this.#chainId]?.[selectedAddress] ?? [];\n state.detectedTokens =\n allDetectedTokens[this.#chainId]?.[selectedAddress] ?? [];\n });\n }\n\n /**\n * Fetch metadata for a token.\n *\n * @param tokenAddress - The address of the token.\n * @returns The token metadata.\n */\n async #fetchTokenMetadata(\n tokenAddress: string,\n ): Promise<TokenListToken | undefined> {\n try {\n const token = await fetchTokenMetadata<TokenListToken>(\n this.#chainId,\n tokenAddress,\n this.#abortController.signal,\n );\n return token;\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(TOKEN_METADATA_NO_SUPPORT_ERROR)\n ) {\n return undefined;\n }\n throw error;\n }\n }\n\n /**\n * Adds a token to the stored token list.\n *\n * @param options - The method argument object.\n * @param options.address - Hex address of the token contract.\n * @param options.symbol - Symbol of the token.\n * @param options.decimals - Number of decimals the token uses.\n * @param options.name - Name of the token.\n * @param options.image - Image of the token.\n * @param options.interactingAddress - The address of the account to add a token to.\n * @param options.networkClientId - Network Client ID.\n * @returns Current token list.\n */\n async addToken({\n address,\n symbol,\n decimals,\n name,\n image,\n interactingAddress,\n networkClientId,\n }: {\n address: string;\n symbol: string;\n decimals: number;\n name?: string;\n image?: string;\n interactingAddress?: string;\n networkClientId?: NetworkClientId;\n }): Promise<Token[]> {\n const chainId = this.#chainId;\n const selectedAddress = this.#selectedAddress;\n const releaseLock = await this.#mutex.acquire();\n const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state;\n let currentChainId = chainId;\n if (networkClientId) {\n currentChainId = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).configuration.chainId;\n }\n\n const accountAddress = interactingAddress || selectedAddress;\n const isInteractingWithWalletAccount = accountAddress === selectedAddress;\n\n try {\n address = toChecksumHexAddress(address);\n const tokens = allTokens[currentChainId]?.[accountAddress] || [];\n const ignoredTokens =\n allIgnoredTokens[currentChainId]?.[accountAddress] || [];\n const detectedTokens =\n allDetectedTokens[currentChainId]?.[accountAddress] || [];\n const newTokens: Token[] = [...tokens];\n const [isERC721, tokenMetadata] = await Promise.all([\n this.#detectIsERC721(address, networkClientId),\n // TODO parameterize the token metadata fetch by networkClientId\n this.#fetchTokenMetadata(address),\n ]);\n // TODO remove this once this method is fully parameterized by networkClientId\n if (!networkClientId && currentChainId !== this.#chainId) {\n throw new Error(\n 'TokensController Error: Switched networks while adding token',\n );\n }\n const newEntry: Token = {\n address,\n symbol,\n decimals,\n image:\n image ||\n formatIconUrlWithProxy({\n chainId: currentChainId,\n tokenAddress: address,\n }),\n isERC721,\n aggregators: formatAggregatorNames(tokenMetadata?.aggregators || []),\n name,\n };\n const previousIndex = newTokens.findIndex(\n (token) => token.address.toLowerCase() === address.toLowerCase(),\n );\n if (previousIndex !== -1) {\n newTokens[previousIndex] = newEntry;\n } else {\n newTokens.push(newEntry);\n }\n\n const newIgnoredTokens = ignoredTokens.filter(\n (tokenAddress) => tokenAddress.toLowerCase() !== address.toLowerCase(),\n );\n const newDetectedTokens = detectedTokens.filter(\n (token) => token.address.toLowerCase() !== address.toLowerCase(),\n );\n\n const { newAllTokens, newAllIgnoredTokens, newAllDetectedTokens } =\n this.#getNewAllTokensState({\n newTokens,\n newIgnoredTokens,\n newDetectedTokens,\n interactingAddress: accountAddress,\n interactingChainId: currentChainId,\n });\n\n let newState: Partial<TokensControllerState> = {\n allTokens: newAllTokens,\n allIgnoredTokens: newAllIgnoredTokens,\n allDetectedTokens: newAllDetectedTokens,\n };\n\n // Only update active tokens if user is interacting with their active wallet account.\n if (isInteractingWithWalletAccount) {\n newState = {\n ...newState,\n tokens: newTokens,\n ignoredTokens: newIgnoredTokens,\n detectedTokens: newDetectedTokens,\n };\n }\n\n this.update((state) => {\n Object.assign(state, newState);\n });\n return newTokens;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Add a batch of tokens.\n *\n * @param tokensToImport - Array of tokens to import.\n * @param networkClientId - Optional network client ID used to determine interacting chain ID.\n */\n async addTokens(tokensToImport: Token[], networkClientId?: NetworkClientId) {\n const releaseLock = await this.#mutex.acquire();\n const { tokens, detectedTokens, ignoredTokens } = this.state;\n const importedTokensMap: { [key: string]: true } = {};\n // Used later to dedupe imported tokens\n const newTokensMap = tokens.reduce((output, current) => {\n output[current.address] = current;\n return output;\n }, {} as { [address: string]: Token });\n try {\n tokensToImport.forEach((tokenToAdd) => {\n const { address, symbol, decimals, image, aggregators, name } =\n tokenToAdd;\n const checksumAddress = toChecksumHexAddress(address);\n const formattedToken: Token = {\n address: checksumAddress,\n symbol,\n decimals,\n image,\n aggregators,\n name,\n };\n newTokensMap[address] = formattedToken;\n importedTokensMap[address.toLowerCase()] = true;\n return formattedToken;\n });\n const newTokens = Object.values(newTokensMap);\n\n const newDetectedTokens = detectedTokens.filter(\n (token) => !importedTokensMap[token.address.toLowerCase()],\n );\n const newIgnoredTokens = ignoredTokens.filter(\n (tokenAddress) => !newTokensMap[tokenAddress.toLowerCase()],\n );\n\n let interactingChainId;\n if (networkClientId) {\n interactingChainId = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).configuration.chainId;\n }\n\n const { newAllTokens, newAllDetectedTokens, newAllIgnoredTokens } =\n this.#getNewAllTokensState({\n newTokens,\n newDetectedTokens,\n newIgnoredTokens,\n interactingChainId,\n });\n\n this.update((state) => {\n state.tokens = newTokens;\n state.allTokens = newAllTokens;\n state.detectedTokens = newDetectedTokens;\n state.allDetectedTokens = newAllDetectedTokens;\n state.ignoredTokens = newIgnoredTokens;\n state.allIgnoredTokens = newAllIgnoredTokens;\n });\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Ignore a batch of tokens.\n *\n * @param tokenAddressesToIgnore - Array of token addresses to ignore.\n */\n ignoreTokens(tokenAddressesToIgnore: string[]) {\n const { ignoredTokens, detectedTokens, tokens } = this.state;\n const ignoredTokensMap: { [key: string]: true } = {};\n let newIgnoredTokens: string[] = [...ignoredTokens];\n\n const checksummedTokenAddresses = tokenAddressesToIgnore.map((address) => {\n const checksumAddress = toChecksumHexAddress(address);\n ignoredTokensMap[address.toLowerCase()] = true;\n return checksumAddress;\n });\n newIgnoredTokens = [...ignoredTokens, ...checksummedTokenAddresses];\n const newDetectedTokens = detectedTokens.filter(\n (token) => !ignoredTokensMap[token.address.toLowerCase()],\n );\n const newTokens = tokens.filter(\n (token) => !ignoredTokensMap[token.address.toLowerCase()],\n );\n\n const { newAllIgnoredTokens, newAllDetectedTokens, newAllTokens } =\n this.#getNewAllTokensState({\n newIgnoredTokens,\n newDetectedTokens,\n newTokens,\n });\n\n this.update((state) => {\n state.ignoredTokens = newIgnoredTokens;\n state.tokens = newTokens;\n state.detectedTokens = newDetectedTokens;\n state.allIgnoredTokens = newAllIgnoredTokens;\n state.allDetectedTokens = newAllDetectedTokens;\n state.allTokens = newAllTokens;\n });\n }\n\n /**\n * Adds a batch of detected tokens to the stored token list.\n *\n * @param incomingDetectedTokens - Array of detected tokens to be added or updated.\n * @param detectionDetails - An object containing the chain ID and address of the currently selected network on which the incomingDetectedTokens were detected.\n * @param detectionDetails.selectedAddress - the account address on which the incomingDetectedTokens were detected.\n * @param detectionDetails.chainId - the chainId on which the incomingDetectedTokens were detected.\n */\n async addDetectedTokens(\n incomingDetectedTokens: Token[],\n detectionDetails?: { selectedAddress: string; chainId: Hex },\n ) {\n const releaseLock = await this.#mutex.acquire();\n\n // Get existing tokens for the chain + account\n const chainId = detectionDetails?.chainId ?? this.#chainId;\n const accountAddress =\n detectionDetails?.selectedAddress ?? this.#selectedAddress;\n\n const { allTokens, allDetectedTokens, allIgnoredTokens } = this.state;\n let newTokens = [...(allTokens?.[chainId]?.[accountAddress] ?? [])];\n let newDetectedTokens = [\n ...(allDetectedTokens?.[chainId]?.[accountAddress] ?? []),\n ];\n\n try {\n incomingDetectedTokens.forEach((tokenToAdd) => {\n const {\n address,\n symbol,\n decimals,\n image,\n aggregators,\n isERC721,\n name,\n } = tokenToAdd;\n const checksumAddress = toChecksumHexAddress(address);\n const newEntry: Token = {\n address: checksumAddress,\n symbol,\n decimals,\n image,\n isERC721,\n aggregators,\n name,\n };\n const previousImportedIndex = newTokens.findIndex(\n (token) =>\n token.address.toLowerCase() === checksumAddress.toLowerCase(),\n );\n if (previousImportedIndex !== -1) {\n // Update existing data of imported token\n newTokens[previousImportedIndex] = newEntry;\n } else {\n const ignoredTokenIndex =\n allIgnoredTokens?.[chainId]?.[accountAddress]?.indexOf(address) ??\n -1;\n\n if (ignoredTokenIndex === -1) {\n // Add detected token\n const previousDetectedIndex = newDetectedTokens.findIndex(\n (token) =>\n token.address.toLowerCase() === checksumAddress.toLowerCase(),\n );\n if (previousDetectedIndex !== -1) {\n newDetectedTokens[previousDetectedIndex] = newEntry;\n } else {\n newDetectedTokens.push(newEntry);\n }\n }\n }\n });\n\n const { newAllTokens, newAllDetectedTokens } = this.#getNewAllTokensState(\n {\n newTokens,\n newDetectedTokens,\n interactingAddress: accountAddress,\n interactingChainId: chainId,\n },\n );\n\n // We may be detecting tokens on a different chain/account pair than are currently configured.\n // Re-point `tokens` and `detectedTokens` to keep them referencing the current chain/account.\n newTokens = newAllTokens?.[this.#chainId]?.[this.#selectedAddress] || [];\n newDetectedTokens =\n newAllDetectedTokens?.[this.#chainId]?.[this.#selectedAddress] || [];\n\n this.update((state) => {\n state.tokens = newTokens;\n state.allTokens = newAllTokens;\n state.detectedTokens = newDetectedTokens;\n state.allDetectedTokens = newAllDetectedTokens;\n });\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Adds isERC721 field to token object. This is called when a user attempts to add tokens that\n * were previously added which do not yet had isERC721 field.\n *\n * @param tokenAddress - The contract address of the token requiring the isERC721 field added.\n * @returns The new token object with the added isERC721 field.\n */\n async updateTokenType(tokenAddress: string) {\n const isERC721 = await this.#detectIsERC721(tokenAddress);\n const tokens = [...this.state.tokens];\n const tokenIndex = tokens.findIndex((token) => {\n return token.address.toLowerCase() === tokenAddress.toLowerCase();\n });\n const updatedToken = { ...tokens[tokenIndex], isERC721 };\n tokens[tokenIndex] = updatedToken;\n this.update((state) => {\n state.tokens = tokens;\n });\n return updatedToken;\n }\n\n /**\n * This is a function that updates the tokens name for the tokens name if it is not defined.\n *\n * @param tokenList - Represents the fetched token list from service API\n * @param tokenAttribute - Represents the token attribute that we want to update on the token list\n */\n #updateTokensAttribute(\n tokenList: TokenListMap,\n tokenAttribute: keyof Token & keyof TokenListToken,\n ) {\n const { tokens } = this.state;\n\n const newTokens = tokens.map((token) => {\n const newToken = tokenList[token.address.toLowerCase()];\n\n return !token[tokenAttribute] && newToken?.[tokenAttribute]\n ? { ...token, [tokenAttribute]: newToken[tokenAttribute] }\n : { ...token };\n });\n\n this.update((state) => {\n state.tokens = newTokens;\n });\n }\n\n /**\n * Detects whether or not a token is ERC-721 compatible.\n *\n * @param tokenAddress - The token contract address.\n * @param networkClientId - Optional network client ID to fetch contract info with.\n * @returns A boolean indicating whether the token address passed in supports the EIP-721\n * interface.\n */\n async #detectIsERC721(\n tokenAddress: string,\n networkClientId?: NetworkClientId,\n ) {\n const checksumAddress = toChecksumHexAddress(tokenAddress);\n // if this token is already in our contract metadata map we don't need\n // to check against the contract\n if (contractsMap[checksumAddress]?.erc721 === true) {\n return Promise.resolve(true);\n } else if (contractsMap[checksumAddress]?.erc20 === true) {\n return Promise.resolve(false);\n }\n\n const tokenContract = this.#createEthersContract(\n tokenAddress,\n abiERC721,\n networkClientId,\n );\n try {\n return await tokenContract.supportsInterface(ERC721_INTERFACE_ID);\n } catch (error) {\n // currently we see a variety of errors across different networks when\n // token contracts are not ERC721 compatible. We need to figure out a better\n // way of differentiating token interface types but for now if we get an error\n // we have to assume the token is not ERC721 compatible.\n return false;\n }\n }\n\n #getProvider(networkClientId?: NetworkClientId): Web3Provider {\n return new Web3Provider(\n // @ts-expect-error TODO: remove this annotation once the `Eip1193Provider` class is released\n networkClientId\n ? this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).provider\n : this.#provider,\n );\n }\n\n #createEthersContract(\n tokenAddress: string,\n abi: string,\n networkClientId?: NetworkClientId,\n ): Contract {\n const web3provider = this.#getProvider(networkClientId);\n const tokenContract = new Contract(tokenAddress, abi, web3provider);\n return tokenContract;\n }\n\n #generateRandomId(): string {\n return random();\n }\n\n /**\n * Adds a new suggestedAsset to the list of watched assets.\n * Parameters will be validated according to the asset type being watched.\n *\n * @param options - The method options.\n * @param options.asset - The asset to be watched. For now only ERC20 tokens are accepted.\n * @param options.type - The asset type.\n * @param options.interactingAddress - The address of the account that is requesting to watch the asset.\n * @param options.networkClientId - Network Client ID.\n * @returns A promise that resolves if the asset was watched successfully, and rejects otherwise.\n */\n async watchAsset({\n asset,\n type,\n interactingAddress,\n networkClientId,\n }: {\n asset: Token;\n type: string;\n interactingAddress?: string;\n networkClientId?: NetworkClientId;\n }): Promise<void> {\n if (type !== ERC20) {\n throw new Error(`Asset of type ${type} not supported`);\n }\n\n if (!asset.address) {\n throw rpcErrors.invalidParams('Address must be specified');\n }\n\n if (!isValidHexAddress(asset.address)) {\n throw rpcErrors.invalidParams(`Invalid address \"${asset.address}\"`);\n }\n\n // Validate contract\n\n if (await this.#detectIsERC721(asset.address, networkClientId)) {\n throw rpcErrors.invalidParams(\n `Contract ${asset.address} must match type ${type}, but was detected as ${ERC721}`,\n );\n }\n\n const provider = this.#getProvider(networkClientId);\n const isErc1155 = await safelyExecute(() =>\n new ERC1155Standard(provider).contractSupportsBase1155Interface(\n asset.address,\n ),\n );\n if (isErc1155) {\n throw rpcErrors.invalidParams(\n `Contract ${asset.address} must match type ${type}, but was detected as ${ERC1155}`,\n );\n }\n\n const erc20 = new ERC20Standard(provider);\n const [contractName, contractSymbol, contractDecimals] = await Promise.all([\n safelyExecute(() => erc20.getTokenName(asset.address)),\n safelyExecute(() => erc20.getTokenSymbol(asset.address)),\n safelyExecute(async () => erc20.getTokenDecimals(asset.address)),\n ]);\n\n asset.name = contractName;\n\n // Validate symbol\n\n if (!asset.symbol && !contractSymbol) {\n throw rpcErrors.invalidParams(\n 'A symbol is required, but was not found in either the request or contract',\n );\n }\n\n if (\n contractSymbol !== undefined &&\n asset.symbol !== undefined &&\n asset.symbol.toUpperCase() !== contractSymbol.toUpperCase()\n ) {\n throw rpcErrors.invalidParams(\n `The symbol in the request (${asset.symbol}) does not match the symbol in the contract (${contractSymbol})`,\n );\n }\n\n asset.symbol = contractSymbol ?? asset.symbol;\n if (typeof asset.symbol !== 'string') {\n throw rpcErrors.invalidParams(`Invalid symbol: not a string`);\n }\n\n if (asset.symbol.length > 11) {\n throw rpcErrors.invalidParams(\n `Invalid symbol \"${asset.symbol}\": longer than 11 characters`,\n );\n }\n\n // Validate decimals\n\n if (asset.decimals === undefined && contractDecimals === undefined) {\n throw rpcErrors.invalidParams(\n 'Decimals are required, but were not found in either the request or contract',\n );\n }\n\n if (\n contractDecimals !== undefined &&\n asset.decimals !== undefined &&\n String(asset.decimals) !== contractDecimals\n ) {\n throw rpcErrors.invalidParams(\n `The decimals in the request (${asset.decimals}) do not match the decimals in the contract (${contractDecimals})`,\n );\n }\n\n const decimalsStr = contractDecimals ?? asset.decimals;\n const decimalsNum = parseInt(decimalsStr as unknown as string, 10);\n if (!Number.isInteger(decimalsNum) || decimalsNum > 36 || decimalsNum < 0) {\n throw rpcErrors.invalidParams(\n `Invalid decimals \"${decimalsStr}\": must be an integer 0 <= 36`,\n );\n }\n asset.decimals = decimalsNum;\n\n const suggestedAssetMeta: SuggestedAssetMeta = {\n asset,\n id: this.#generateRandomId(),\n time: Date.now(),\n type,\n interactingAddress: interactingAddress || this.#selectedAddress,\n };\n\n await this.#requestApproval(suggestedAssetMeta);\n\n const { address, symbol, decimals, name, image } = asset;\n await this.addToken({\n address,\n symbol,\n decimals,\n name,\n image,\n interactingAddress: suggestedAssetMeta.interactingAddress,\n networkClientId,\n });\n }\n\n /**\n * Takes a new tokens and ignoredTokens array for the current network/account combination\n * and returns new allTokens and allIgnoredTokens state to update to.\n *\n * @param params - Object that holds token params.\n * @param params.newTokens - The new tokens to set for the current network and selected account.\n * @param params.newIgnoredTokens - The new ignored tokens to set for the current network and selected account.\n * @param params.newDetectedTokens - The new detected tokens to set for the current network and selected account.\n * @param params.interactingAddress - The account address to use to store the tokens.\n * @param params.interactingChainId - The chainId to use to store the tokens.\n * @returns The updated `allTokens` and `allIgnoredTokens` state.\n */\n #getNewAllTokensState(params: {\n newTokens?: Token[];\n newIgnoredTokens?: string[];\n newDetectedTokens?: Token[];\n interactingAddress?: string;\n interactingChainId?: Hex;\n }) {\n const {\n newTokens,\n newIgnoredTokens,\n newDetectedTokens,\n interactingAddress,\n interactingChainId,\n } = params;\n const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state;\n\n const userAddressToAddTokens = interactingAddress ?? this.#selectedAddress;\n const chainIdToAddTokens = interactingChainId ?? this.#chainId;\n\n let newAllTokens = allTokens;\n if (\n newTokens?.length ||\n (newTokens &&\n allTokens &&\n allTokens[chainIdToAddTokens] &&\n allTokens[chainIdToAddTokens][userAddressToAddTokens])\n ) {\n const networkTokens = allTokens[chainIdToAddTokens];\n const newNetworkTokens = {\n ...networkTokens,\n ...{ [userAddressToAddTokens]: newTokens },\n };\n newAllTokens = {\n ...allTokens,\n ...{ [chainIdToAddTokens]: newNetworkTokens },\n };\n }\n\n let newAllIgnoredTokens = allIgnoredTokens;\n if (\n newIgnoredTokens?.length ||\n (newIgnoredTokens &&\n allIgnoredTokens &&\n allIgnoredTokens[chainIdToAddTokens] &&\n allIgnoredTokens[chainIdToAddTokens][userAddressToAddTokens])\n ) {\n const networkIgnoredTokens = allIgnoredTokens[chainIdToAddTokens];\n const newIgnoredNetworkTokens = {\n ...networkIgnoredTokens,\n ...{ [userAddressToAddTokens]: newIgnoredTokens },\n };\n newAllIgnoredTokens = {\n ...allIgnoredTokens,\n ...{ [chainIdToAddTokens]: newIgnoredNetworkTokens },\n };\n }\n\n let newAllDetectedTokens = allDetectedTokens;\n if (\n newDetectedTokens?.length ||\n (newDetectedTokens &&\n allDetectedTokens &&\n allDetectedTokens[chainIdToAddTokens] &&\n allDetectedTokens[chainIdToAddTokens][userAddressToAddTokens])\n ) {\n const networkDetectedTokens = allDetectedTokens[chainIdToAddTokens];\n const newDetectedNetworkTokens = {\n ...networkDetectedTokens,\n ...{ [userAddressToAddTokens]: newDetectedTokens },\n };\n newAllDetectedTokens = {\n ...allDetectedTokens,\n ...{ [chainIdToAddTokens]: newDetectedNetworkTokens },\n };\n }\n return { newAllTokens, newAllIgnoredTokens, newAllDetectedTokens };\n }\n\n /**\n * Removes all tokens from the ignored list.\n */\n clearIgnoredTokens() {\n this.update((state) => {\n state.ignoredTokens = [];\n state.allIgnoredTokens = {};\n });\n }\n\n async #requestApproval(suggestedAssetMeta: SuggestedAssetMeta) {\n return this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n id: suggestedAssetMeta.id,\n origin: ORIGIN_METAMASK,\n type: ApprovalType.WatchAsset,\n requestData: {\n id: suggestedAssetMeta.id,\n interactingAddress: suggestedAssetMeta.interactingAddress,\n asset: {\n address: suggestedAssetMeta.asset.address,\n decimals: suggestedAssetMeta.asset.decimals,\n symbol: suggestedAssetMeta.asset.symbol,\n image: suggestedAssetMeta.asset.image || null,\n },\n },\n },\n true,\n );\n }\n}\n\nexport default TokensController;\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkVJCRI43Tjs = require('./chunk-VJCRI43T.js');
|
|
4
4
|
|
|
5
5
|
// src/token-service.ts
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ async function fetchTokenListByChainId(chainId, abortSignal, { timeout = default
|
|
|
38
38
|
return void 0;
|
|
39
39
|
}
|
|
40
40
|
async function fetchTokenMetadata(chainId, tokenAddress, abortSignal, { timeout = defaultTimeout } = {}) {
|
|
41
|
-
if (!
|
|
41
|
+
if (!_chunkVJCRI43Tjs.isTokenListSupportedForNetwork.call(void 0, chainId)) {
|
|
42
42
|
throw new Error(TOKEN_METADATA_NO_SUPPORT_ERROR);
|
|
43
43
|
}
|
|
44
44
|
const tokenMetadataURL = getTokenMetadataURL(chainId, tokenAddress);
|
|
@@ -82,4 +82,4 @@ async function parseJsonResponse(apiResponse) {
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
exports.TOKEN_END_POINT_API = TOKEN_END_POINT_API; exports.TOKEN_METADATA_NO_SUPPORT_ERROR = TOKEN_METADATA_NO_SUPPORT_ERROR; exports.fetchTokenListByChainId = fetchTokenListByChainId; exports.fetchTokenMetadata = fetchTokenMetadata;
|
|
85
|
-
//# sourceMappingURL=chunk-
|
|
85
|
+
//# sourceMappingURL=chunk-S3OXHT4M.js.map
|
|
@@ -182,7 +182,7 @@ async function fetchTokenContractExchangeRates({
|
|
|
182
182
|
(obj, [tokenAddress, tokenPrice]) => {
|
|
183
183
|
return {
|
|
184
184
|
...obj,
|
|
185
|
-
[toChecksumHexAddress(tokenAddress)]: tokenPrice?.
|
|
185
|
+
[toChecksumHexAddress(tokenAddress)]: tokenPrice?.price
|
|
186
186
|
};
|
|
187
187
|
},
|
|
188
188
|
{}
|
|
@@ -206,4 +206,4 @@ export {
|
|
|
206
206
|
reduceInBatchesSerially,
|
|
207
207
|
fetchTokenContractExchangeRates
|
|
208
208
|
};
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-SXFTKOHZ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/assetsUtil.ts"],"sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport {\n convertHexToDecimal,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { Hex } from '@metamask/utils';\nimport { remove0x } from '@metamask/utils';\nimport BN from 'bn.js';\nimport { CID } from 'multiformats/cid';\n\nimport type { Nft, NftMetadata } from './NftController';\nimport type { AbstractTokenPricesService } from './token-prices-service';\nimport { type ContractExchangeRates } from './TokenRatesController';\n\n/**\n * The maximum number of token addresses that should be sent to the Price API in\n * a single request.\n */\nexport const TOKEN_PRICES_BATCH_SIZE = 30;\n\n/**\n * Compares nft metadata entries to any nft entry.\n * We need this method when comparing a new fetched nft metadata, in case a entry changed to a defined value,\n * there's a need to update the nft in state.\n *\n * @param newNftMetadata - Nft metadata object.\n * @param nft - Nft object to compare with.\n * @returns Whether there are differences.\n */\nexport function compareNftMetadata(newNftMetadata: NftMetadata, nft: Nft) {\n const keys: (keyof NftMetadata)[] = [\n 'image',\n 'backgroundColor',\n 'imagePreview',\n 'imageThumbnail',\n 'imageOriginal',\n 'animation',\n 'animationOriginal',\n 'externalLink',\n 'tokenURI',\n ];\n const differentValues = keys.reduce((value, key) => {\n if (newNftMetadata[key] && newNftMetadata[key] !== nft[key]) {\n return value + 1;\n }\n return value;\n }, 0);\n return differentValues > 0;\n}\n\nconst aggregatorNameByKey: Record<string, string> = {\n aave: 'Aave',\n bancor: 'Bancor',\n cmc: 'CMC',\n cryptocom: 'Crypto.com',\n coinGecko: 'CoinGecko',\n oneInch: '1inch',\n paraswap: 'Paraswap',\n pmm: 'PMM',\n zapper: 'Zapper',\n zerion: 'Zerion',\n zeroEx: '0x',\n synthetix: 'Synthetix',\n yearn: 'Yearn',\n apeswap: 'ApeSwap',\n binanceDex: 'BinanceDex',\n pancakeTop100: 'PancakeTop100',\n pancakeExtended: 'PancakeExtended',\n balancer: 'Balancer',\n quickswap: 'QuickSwap',\n matcha: 'Matcha',\n pangolinDex: 'PangolinDex',\n pangolinDexStableCoin: 'PangolinDexStableCoin',\n pangolinDexAvaxBridge: 'PangolinDexAvaxBridge',\n traderJoe: 'TraderJoe',\n airswapLight: 'AirswapLight',\n kleros: 'Kleros',\n};\n\n/**\n * Formats aggregator names to presentable format.\n *\n * @param aggregators - List of token list names in camelcase.\n * @returns Formatted aggregator names.\n */\nexport const formatAggregatorNames = (aggregators: string[]) => {\n return aggregators.map(\n (key) =>\n aggregatorNameByKey[key] ||\n `${key[0].toUpperCase()}${key.substring(1, key.length)}`,\n );\n};\n\n/**\n * Format token list assets to use image proxy from Codefi.\n *\n * @param params - Object that contains chainID and tokenAddress.\n * @param params.chainId - ChainID of network in 0x-prefixed hexadecimal format.\n * @param params.tokenAddress - Address of token in mixed or lowercase.\n * @returns Formatted image url\n */\nexport const formatIconUrlWithProxy = ({\n chainId,\n tokenAddress,\n}: {\n chainId: Hex;\n tokenAddress: string;\n}) => {\n const chainIdDecimal = convertHexToDecimal(chainId).toString();\n return `https://static.cx.metamask.io/api/v1/tokenIcons/${chainIdDecimal}/${tokenAddress.toLowerCase()}.png`;\n};\n\n/**\n * Networks where token detection is supported - Values are in hex format\n */\nexport enum SupportedTokenDetectionNetworks {\n mainnet = '0x1', // decimal: 1\n bsc = '0x38', // decimal: 56\n polygon = '0x89', // decimal: 137\n avax = '0xa86a', // decimal: 43114\n aurora = '0x4e454152', // decimal: 1313161554\n linea_goerli = '0xe704', // decimal: 59140\n linea_mainnet = '0xe708', // decimal: 59144\n arbitrum = '0xa4b1', // decimal: 42161\n optimism = '0xa', // decimal: 10\n base = '0x2105', // decimal: 8453\n zksync = '0x144', // decimal: 324\n cronos = '0x19', // decimal: 25\n celo = '0xa4ec', // decimal: 42220\n gnosis = '0x64', // decimal: 100\n fantom = '0xfa', // decimal: 250\n polygon_zkevm = '0x44d', // decimal: 1101\n moonbeam = '0x504', // decimal: 1284\n moonriver = '0x505', // decimal: 1285\n}\n\n/**\n * Check if token detection is enabled for certain networks.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports token detection\n */\nexport function isTokenDetectionSupportedForNetwork(chainId: Hex): boolean {\n return Object.values<Hex>(SupportedTokenDetectionNetworks).includes(chainId);\n}\n\n/**\n * Check if token list polling is enabled for a given network.\n * Currently this method is used to support e2e testing for consumers of this package.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports tokenlists\n */\nexport function isTokenListSupportedForNetwork(chainId: Hex): boolean {\n return isTokenDetectionSupportedForNetwork(chainId);\n}\n\n/**\n * Removes IPFS protocol prefix from input string.\n *\n * @param ipfsUrl - An IPFS url (e.g. ipfs://{content id})\n * @returns IPFS content identifier and (possibly) path in a string\n * @throws Will throw if the url passed is not IPFS.\n */\nexport function removeIpfsProtocolPrefix(ipfsUrl: string) {\n if (ipfsUrl.startsWith('ipfs://ipfs/')) {\n return ipfsUrl.replace('ipfs://ipfs/', '');\n } else if (ipfsUrl.startsWith('ipfs://')) {\n return ipfsUrl.replace('ipfs://', '');\n }\n // this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)\n throw new Error('this method should not be used with non ipfs urls');\n}\n\n/**\n * Extracts content identifier and path from an input string.\n *\n * @param ipfsUrl - An IPFS URL minus the IPFS protocol prefix\n * @returns IFPS content identifier (cid) and sub path as string.\n * @throws Will throw if the url passed is not ipfs.\n */\nexport function getIpfsCIDv1AndPath(ipfsUrl: string): {\n cid: string;\n path?: string;\n} {\n const url = removeIpfsProtocolPrefix(ipfsUrl);\n\n // check if there is a path\n // (CID is everything preceding first forward slash, path is everything after)\n const index = url.indexOf('/');\n const cid = index !== -1 ? url.substring(0, index) : url;\n const path = index !== -1 ? url.substring(index) : undefined;\n\n // We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)\n // because most cid v0s appear to be incompatible with IPFS subdomains\n return {\n cid: CID.parse(cid).toV1().toString(),\n path,\n };\n}\n\n/**\n * Formats URL correctly for use retrieving assets hosted on IPFS.\n *\n * @param ipfsGateway - The users preferred IPFS gateway (full URL or just host).\n * @param ipfsUrl - The IFPS URL pointed at the asset.\n * @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.\n * @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.\n */\nexport function getFormattedIpfsUrl(\n ipfsGateway: string,\n ipfsUrl: string,\n subdomainSupported: boolean,\n): string {\n const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway));\n if (subdomainSupported) {\n const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl);\n return `${protocol}//${cid}.ipfs.${host}${path ?? ''}`;\n }\n const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl);\n return `${origin}/ipfs/${cidAndPath}`;\n}\n\n/**\n * Adds URL protocol prefix to input URL string if missing.\n *\n * @param urlString - An IPFS URL.\n * @returns A URL with a https:// prepended.\n */\nexport function addUrlProtocolPrefix(urlString: string): string {\n if (!urlString.match(/(^http:\\/\\/)|(^https:\\/\\/)/u)) {\n return `https://${urlString}`;\n }\n return urlString;\n}\n\n/**\n * Converts an Ethers BigNumber to a BN.\n *\n * @param bigNumber - An Ethers BigNumber instance.\n * @returns A BN object.\n */\nexport function ethersBigNumberToBN(bigNumber: BigNumber): BN {\n return new BN(remove0x(bigNumber.toHexString()), 'hex');\n}\n\n/**\n * Partitions a list of values into groups that are at most `batchSize` in\n * length.\n *\n * @param values - The list of values.\n * @param args - The remaining arguments.\n * @param args.batchSize - The desired maximum number of values per batch.\n * @returns The list of batches.\n */\nexport function divideIntoBatches<Value>(\n values: Value[],\n { batchSize }: { batchSize: number },\n): Value[][] {\n const batches = [];\n for (let i = 0; i < values.length; i += batchSize) {\n batches.push(values.slice(i, i + batchSize));\n }\n return batches;\n}\n\n/**\n * Constructs an object from processing batches of the given values\n * sequentially.\n *\n * @param args - The arguments to this function.\n * @param args.values - A list of values to iterate over.\n * @param args.batchSize - The maximum number of values in each batch.\n * @param args.eachBatch - A function to call for each batch. This function is\n * similar to the function that `Array.prototype.reduce` takes, in that it\n * receives the object that is being built, each batch in the list of batches\n * and the index, and should return an updated version of the object.\n * @param args.initialResult - The initial value of the final data structure,\n * i.e., the value that will be fed into the first call of `eachBatch`.\n * @returns The built object.\n */\nexport async function reduceInBatchesSerially<\n Value,\n Result extends Record<PropertyKey, unknown>,\n>({\n values,\n batchSize,\n eachBatch,\n initialResult,\n}: {\n values: Value[];\n batchSize: number;\n eachBatch: (\n workingResult: Partial<Result>,\n batch: Value[],\n index: number,\n ) => Partial<Result> | Promise<Partial<Result>>;\n initialResult: Partial<Result>;\n}): Promise<Result> {\n const batches = divideIntoBatches(values, { batchSize });\n let workingResult = initialResult;\n for (const [index, batch] of batches.entries()) {\n workingResult = await eachBatch(workingResult, batch, index);\n }\n // There's no way around this — we have to assume that in the end, the result\n // matches the intended type.\n const finalResult = workingResult as Result;\n return finalResult;\n}\n\n/**\n * Retrieves token prices for a set of contract addresses in a specific currency and chainId.\n *\n * @param args - The arguments to function.\n * @param args.tokenPricesService - An object in charge of retrieving token prices.\n * @param args.nativeCurrency - The native currency to request price in.\n * @param args.tokenAddresses - The list of contract addresses.\n * @param args.chainId - The chainId of the tokens.\n * @returns The prices for the requested tokens.\n */\nexport async function fetchTokenContractExchangeRates({\n tokenPricesService,\n nativeCurrency,\n tokenAddresses,\n chainId,\n}: {\n tokenPricesService: AbstractTokenPricesService;\n nativeCurrency: string;\n tokenAddresses: Hex[];\n chainId: Hex;\n}): Promise<ContractExchangeRates> {\n const isChainIdSupported =\n tokenPricesService.validateChainIdSupported(chainId);\n const isCurrencySupported =\n tokenPricesService.validateCurrencySupported(nativeCurrency);\n\n if (!isChainIdSupported || !isCurrencySupported) {\n return {};\n }\n\n const tokenPricesByTokenAddress = await reduceInBatchesSerially<\n Hex,\n Awaited<ReturnType<AbstractTokenPricesService['fetchTokenPrices']>>\n >({\n values: [...tokenAddresses].sort(),\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n eachBatch: async (allTokenPricesByTokenAddress, batch) => {\n const tokenPricesByTokenAddressForBatch =\n await tokenPricesService.fetchTokenPrices({\n tokenAddresses: batch,\n chainId,\n currency: nativeCurrency,\n });\n\n return {\n ...allTokenPricesByTokenAddress,\n ...tokenPricesByTokenAddressForBatch,\n };\n },\n initialResult: {},\n });\n\n return Object.entries(tokenPricesByTokenAddress).reduce(\n (obj, [tokenAddress, tokenPrice]) => {\n return {\n ...obj,\n [toChecksumHexAddress(tokenAddress)]: tokenPrice?.value,\n };\n },\n {},\n );\n}\n"],"mappings":"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","names":["SupportedTokenDetectionNetworks"]}
|
|
1
|
+
{"version":3,"sources":["../src/assetsUtil.ts"],"sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport {\n convertHexToDecimal,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { Hex } from '@metamask/utils';\nimport { remove0x } from '@metamask/utils';\nimport BN from 'bn.js';\nimport { CID } from 'multiformats/cid';\n\nimport type { Nft, NftMetadata } from './NftController';\nimport type { AbstractTokenPricesService } from './token-prices-service';\nimport { type ContractExchangeRates } from './TokenRatesController';\n\n/**\n * The maximum number of token addresses that should be sent to the Price API in\n * a single request.\n */\nexport const TOKEN_PRICES_BATCH_SIZE = 30;\n\n/**\n * Compares nft metadata entries to any nft entry.\n * We need this method when comparing a new fetched nft metadata, in case a entry changed to a defined value,\n * there's a need to update the nft in state.\n *\n * @param newNftMetadata - Nft metadata object.\n * @param nft - Nft object to compare with.\n * @returns Whether there are differences.\n */\nexport function compareNftMetadata(newNftMetadata: NftMetadata, nft: Nft) {\n const keys: (keyof NftMetadata)[] = [\n 'image',\n 'backgroundColor',\n 'imagePreview',\n 'imageThumbnail',\n 'imageOriginal',\n 'animation',\n 'animationOriginal',\n 'externalLink',\n 'tokenURI',\n ];\n const differentValues = keys.reduce((value, key) => {\n if (newNftMetadata[key] && newNftMetadata[key] !== nft[key]) {\n return value + 1;\n }\n return value;\n }, 0);\n return differentValues > 0;\n}\n\nconst aggregatorNameByKey: Record<string, string> = {\n aave: 'Aave',\n bancor: 'Bancor',\n cmc: 'CMC',\n cryptocom: 'Crypto.com',\n coinGecko: 'CoinGecko',\n oneInch: '1inch',\n paraswap: 'Paraswap',\n pmm: 'PMM',\n zapper: 'Zapper',\n zerion: 'Zerion',\n zeroEx: '0x',\n synthetix: 'Synthetix',\n yearn: 'Yearn',\n apeswap: 'ApeSwap',\n binanceDex: 'BinanceDex',\n pancakeTop100: 'PancakeTop100',\n pancakeExtended: 'PancakeExtended',\n balancer: 'Balancer',\n quickswap: 'QuickSwap',\n matcha: 'Matcha',\n pangolinDex: 'PangolinDex',\n pangolinDexStableCoin: 'PangolinDexStableCoin',\n pangolinDexAvaxBridge: 'PangolinDexAvaxBridge',\n traderJoe: 'TraderJoe',\n airswapLight: 'AirswapLight',\n kleros: 'Kleros',\n};\n\n/**\n * Formats aggregator names to presentable format.\n *\n * @param aggregators - List of token list names in camelcase.\n * @returns Formatted aggregator names.\n */\nexport const formatAggregatorNames = (aggregators: string[]) => {\n return aggregators.map(\n (key) =>\n aggregatorNameByKey[key] ||\n `${key[0].toUpperCase()}${key.substring(1, key.length)}`,\n );\n};\n\n/**\n * Format token list assets to use image proxy from Codefi.\n *\n * @param params - Object that contains chainID and tokenAddress.\n * @param params.chainId - ChainID of network in 0x-prefixed hexadecimal format.\n * @param params.tokenAddress - Address of token in mixed or lowercase.\n * @returns Formatted image url\n */\nexport const formatIconUrlWithProxy = ({\n chainId,\n tokenAddress,\n}: {\n chainId: Hex;\n tokenAddress: string;\n}) => {\n const chainIdDecimal = convertHexToDecimal(chainId).toString();\n return `https://static.cx.metamask.io/api/v1/tokenIcons/${chainIdDecimal}/${tokenAddress.toLowerCase()}.png`;\n};\n\n/**\n * Networks where token detection is supported - Values are in hex format\n */\nexport enum SupportedTokenDetectionNetworks {\n mainnet = '0x1', // decimal: 1\n bsc = '0x38', // decimal: 56\n polygon = '0x89', // decimal: 137\n avax = '0xa86a', // decimal: 43114\n aurora = '0x4e454152', // decimal: 1313161554\n linea_goerli = '0xe704', // decimal: 59140\n linea_mainnet = '0xe708', // decimal: 59144\n arbitrum = '0xa4b1', // decimal: 42161\n optimism = '0xa', // decimal: 10\n base = '0x2105', // decimal: 8453\n zksync = '0x144', // decimal: 324\n cronos = '0x19', // decimal: 25\n celo = '0xa4ec', // decimal: 42220\n gnosis = '0x64', // decimal: 100\n fantom = '0xfa', // decimal: 250\n polygon_zkevm = '0x44d', // decimal: 1101\n moonbeam = '0x504', // decimal: 1284\n moonriver = '0x505', // decimal: 1285\n}\n\n/**\n * Check if token detection is enabled for certain networks.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports token detection\n */\nexport function isTokenDetectionSupportedForNetwork(chainId: Hex): boolean {\n return Object.values<Hex>(SupportedTokenDetectionNetworks).includes(chainId);\n}\n\n/**\n * Check if token list polling is enabled for a given network.\n * Currently this method is used to support e2e testing for consumers of this package.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports tokenlists\n */\nexport function isTokenListSupportedForNetwork(chainId: Hex): boolean {\n return isTokenDetectionSupportedForNetwork(chainId);\n}\n\n/**\n * Removes IPFS protocol prefix from input string.\n *\n * @param ipfsUrl - An IPFS url (e.g. ipfs://{content id})\n * @returns IPFS content identifier and (possibly) path in a string\n * @throws Will throw if the url passed is not IPFS.\n */\nexport function removeIpfsProtocolPrefix(ipfsUrl: string) {\n if (ipfsUrl.startsWith('ipfs://ipfs/')) {\n return ipfsUrl.replace('ipfs://ipfs/', '');\n } else if (ipfsUrl.startsWith('ipfs://')) {\n return ipfsUrl.replace('ipfs://', '');\n }\n // this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)\n throw new Error('this method should not be used with non ipfs urls');\n}\n\n/**\n * Extracts content identifier and path from an input string.\n *\n * @param ipfsUrl - An IPFS URL minus the IPFS protocol prefix\n * @returns IFPS content identifier (cid) and sub path as string.\n * @throws Will throw if the url passed is not ipfs.\n */\nexport function getIpfsCIDv1AndPath(ipfsUrl: string): {\n cid: string;\n path?: string;\n} {\n const url = removeIpfsProtocolPrefix(ipfsUrl);\n\n // check if there is a path\n // (CID is everything preceding first forward slash, path is everything after)\n const index = url.indexOf('/');\n const cid = index !== -1 ? url.substring(0, index) : url;\n const path = index !== -1 ? url.substring(index) : undefined;\n\n // We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)\n // because most cid v0s appear to be incompatible with IPFS subdomains\n return {\n cid: CID.parse(cid).toV1().toString(),\n path,\n };\n}\n\n/**\n * Formats URL correctly for use retrieving assets hosted on IPFS.\n *\n * @param ipfsGateway - The users preferred IPFS gateway (full URL or just host).\n * @param ipfsUrl - The IFPS URL pointed at the asset.\n * @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.\n * @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.\n */\nexport function getFormattedIpfsUrl(\n ipfsGateway: string,\n ipfsUrl: string,\n subdomainSupported: boolean,\n): string {\n const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway));\n if (subdomainSupported) {\n const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl);\n return `${protocol}//${cid}.ipfs.${host}${path ?? ''}`;\n }\n const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl);\n return `${origin}/ipfs/${cidAndPath}`;\n}\n\n/**\n * Adds URL protocol prefix to input URL string if missing.\n *\n * @param urlString - An IPFS URL.\n * @returns A URL with a https:// prepended.\n */\nexport function addUrlProtocolPrefix(urlString: string): string {\n if (!urlString.match(/(^http:\\/\\/)|(^https:\\/\\/)/u)) {\n return `https://${urlString}`;\n }\n return urlString;\n}\n\n/**\n * Converts an Ethers BigNumber to a BN.\n *\n * @param bigNumber - An Ethers BigNumber instance.\n * @returns A BN object.\n */\nexport function ethersBigNumberToBN(bigNumber: BigNumber): BN {\n return new BN(remove0x(bigNumber.toHexString()), 'hex');\n}\n\n/**\n * Partitions a list of values into groups that are at most `batchSize` in\n * length.\n *\n * @param values - The list of values.\n * @param args - The remaining arguments.\n * @param args.batchSize - The desired maximum number of values per batch.\n * @returns The list of batches.\n */\nexport function divideIntoBatches<Value>(\n values: Value[],\n { batchSize }: { batchSize: number },\n): Value[][] {\n const batches = [];\n for (let i = 0; i < values.length; i += batchSize) {\n batches.push(values.slice(i, i + batchSize));\n }\n return batches;\n}\n\n/**\n * Constructs an object from processing batches of the given values\n * sequentially.\n *\n * @param args - The arguments to this function.\n * @param args.values - A list of values to iterate over.\n * @param args.batchSize - The maximum number of values in each batch.\n * @param args.eachBatch - A function to call for each batch. This function is\n * similar to the function that `Array.prototype.reduce` takes, in that it\n * receives the object that is being built, each batch in the list of batches\n * and the index, and should return an updated version of the object.\n * @param args.initialResult - The initial value of the final data structure,\n * i.e., the value that will be fed into the first call of `eachBatch`.\n * @returns The built object.\n */\nexport async function reduceInBatchesSerially<\n Value,\n Result extends Record<PropertyKey, unknown>,\n>({\n values,\n batchSize,\n eachBatch,\n initialResult,\n}: {\n values: Value[];\n batchSize: number;\n eachBatch: (\n workingResult: Partial<Result>,\n batch: Value[],\n index: number,\n ) => Partial<Result> | Promise<Partial<Result>>;\n initialResult: Partial<Result>;\n}): Promise<Result> {\n const batches = divideIntoBatches(values, { batchSize });\n let workingResult = initialResult;\n for (const [index, batch] of batches.entries()) {\n workingResult = await eachBatch(workingResult, batch, index);\n }\n // There's no way around this — we have to assume that in the end, the result\n // matches the intended type.\n const finalResult = workingResult as Result;\n return finalResult;\n}\n\n/**\n * Retrieves token prices for a set of contract addresses in a specific currency and chainId.\n *\n * @param args - The arguments to function.\n * @param args.tokenPricesService - An object in charge of retrieving token prices.\n * @param args.nativeCurrency - The native currency to request price in.\n * @param args.tokenAddresses - The list of contract addresses.\n * @param args.chainId - The chainId of the tokens.\n * @returns The prices for the requested tokens.\n */\nexport async function fetchTokenContractExchangeRates({\n tokenPricesService,\n nativeCurrency,\n tokenAddresses,\n chainId,\n}: {\n tokenPricesService: AbstractTokenPricesService;\n nativeCurrency: string;\n tokenAddresses: Hex[];\n chainId: Hex;\n}): Promise<ContractExchangeRates> {\n const isChainIdSupported =\n tokenPricesService.validateChainIdSupported(chainId);\n const isCurrencySupported =\n tokenPricesService.validateCurrencySupported(nativeCurrency);\n\n if (!isChainIdSupported || !isCurrencySupported) {\n return {};\n }\n\n const tokenPricesByTokenAddress = await reduceInBatchesSerially<\n Hex,\n Awaited<ReturnType<AbstractTokenPricesService['fetchTokenPrices']>>\n >({\n values: [...tokenAddresses].sort(),\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n eachBatch: async (allTokenPricesByTokenAddress, batch) => {\n const tokenPricesByTokenAddressForBatch =\n await tokenPricesService.fetchTokenPrices({\n tokenAddresses: batch,\n chainId,\n currency: nativeCurrency,\n });\n\n return {\n ...allTokenPricesByTokenAddress,\n ...tokenPricesByTokenAddressForBatch,\n };\n },\n initialResult: {},\n });\n\n return Object.entries(tokenPricesByTokenAddress).reduce(\n (obj, [tokenAddress, tokenPrice]) => {\n return {\n ...obj,\n [toChecksumHexAddress(tokenAddress)]: tokenPrice?.price,\n };\n },\n {},\n );\n}\n"],"mappings":"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","names":["SupportedTokenDetectionNetworks"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkVJCRI43Tjs = require('./chunk-VJCRI43T.js');
|
|
4
4
|
|
|
5
5
|
// src/Standards/ERC20Standard.ts
|
|
6
6
|
var _util = require('@ethereumjs/util');
|
|
@@ -23,7 +23,7 @@ var ERC20Standard = class {
|
|
|
23
23
|
async getBalanceOf(address, selectedAddress) {
|
|
24
24
|
const contract = new (0, _contracts.Contract)(address, _metamaskethabis.abiERC20, this.provider);
|
|
25
25
|
const balance = await contract.balanceOf(selectedAddress);
|
|
26
|
-
return
|
|
26
|
+
return _chunkVJCRI43Tjs.ethersBigNumberToBN.call(void 0, balance);
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Query for the decimals for a given ERC20 asset.
|
|
@@ -112,4 +112,4 @@ var ERC20Standard = class {
|
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
exports.ERC20Standard = ERC20Standard;
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
115
|
+
//# sourceMappingURL=chunk-T2UKYSM3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/AccountTrackerController.ts"],"sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { query, safelyExecuteWithTimeout } from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type { Provider } from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkController,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingControllerV1 } from '@metamask/polling-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { assert } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep } from 'lodash';\n\n/**\n * @type AccountInformation\n *\n * Account information object\n * @property balance - Hex string of an account balancec in wei\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface AccountInformation {\n balance: string;\n}\n\n/**\n * @type AccountTrackerConfig\n *\n * Account tracker controller configuration\n * @property provider - Provider used to create a new underlying EthQuery instance\n */\n// This interface was created before this ESLint rule was added.\n// Remove in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface AccountTrackerConfig extends BaseConfig {\n interval: number;\n provider?: Provider;\n}\n\n/**\n * @type AccountTrackerState\n *\n * Account tracker controller state\n * @property accounts - Map of addresses to account information\n */\n// This interface was created before this ESLint rule was added.\n// Remove in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface AccountTrackerState extends BaseState {\n accounts: { [address: string]: AccountInformation };\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n}\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingControllerV1<\n AccountTrackerConfig,\n AccountTrackerState\n> {\n private _provider?: Provider;\n\n private readonly refreshMutex = new Mutex();\n\n private handle?: ReturnType<typeof setTimeout>;\n\n private syncAccounts(newChainId: string) {\n const accounts = { ...this.state.accounts };\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n\n const existing = Object.keys(accounts);\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n\n const addresses = Object.keys(this.getIdentities());\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n newAddresses.forEach((address) => {\n accounts[address] = { balance: '0x0' };\n });\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n oldAddresses.forEach((address) => {\n delete accounts[address];\n });\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n this.update({ accounts, accountsByChainId });\n }\n\n /**\n * Name of this controller used during composition\n */\n override name = 'AccountTrackerController' as const;\n\n private readonly getIdentities: () => PreferencesState['identities'];\n\n private readonly getSelectedAddress: () => PreferencesState['selectedAddress'];\n\n private readonly getMultiAccountBalancesEnabled: () => PreferencesState['isMultiAccountBalancesEnabled'];\n\n private readonly getCurrentChainId: () => Hex;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.\n * @param options.getIdentities - Gets the identities from the Preferences store.\n * @param options.getSelectedAddress - Gets the selected address from the Preferences store.\n * @param options.getMultiAccountBalancesEnabled - Gets the multi account balances enabled flag from the Preferences store.\n * @param options.getCurrentChainId - Gets the chain ID for the current network from the Network store.\n * @param options.getNetworkClientById - Gets the network client with the given id from the NetworkController.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n onPreferencesStateChange,\n getIdentities,\n getSelectedAddress,\n getMultiAccountBalancesEnabled,\n getCurrentChainId,\n getNetworkClientById,\n }: {\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n getIdentities: () => PreferencesState['identities'];\n getSelectedAddress: () => PreferencesState['selectedAddress'];\n getMultiAccountBalancesEnabled: () => PreferencesState['isMultiAccountBalancesEnabled'];\n getCurrentChainId: () => Hex;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n },\n config?: Partial<AccountTrackerConfig>,\n state?: Partial<AccountTrackerState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n interval: 10000,\n };\n this.defaultState = {\n accounts: {},\n accountsByChainId: {\n [getCurrentChainId()]: {},\n },\n };\n this.initialize();\n this.setIntervalLength(this.config.interval);\n this.getIdentities = getIdentities;\n this.getSelectedAddress = getSelectedAddress;\n this.getMultiAccountBalancesEnabled = getMultiAccountBalancesEnabled;\n this.getCurrentChainId = getCurrentChainId;\n this.getNetworkClientById = getNetworkClientById;\n onPreferencesStateChange(() => {\n this.refresh();\n });\n this.poll();\n }\n\n /**\n * Sets a new provider.\n *\n * TODO: Replace this wth a method.\n *\n * @param provider - Provider used to create a new underlying EthQuery instance.\n */\n set provider(provider: Provider) {\n this._provider = provider;\n }\n\n get provider() {\n throw new Error('Property only used for setting');\n }\n\n /**\n * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId): {\n chainId: string;\n ethQuery?: EthQuery;\n } {\n if (networkClientId) {\n const networkClient = this.getNetworkClientById(networkClientId);\n\n return {\n chainId: networkClient.configuration.chainId,\n ethQuery: new EthQuery(networkClient.provider),\n };\n }\n\n return {\n chainId: this.getCurrentChainId(),\n ethQuery: this._provider ? new EthQuery(this._provider) : undefined,\n };\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval trigger a 'refresh'.\n */\n async poll(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.handle && clearTimeout(this.handle);\n await this.refresh();\n this.handle = setTimeout(() => {\n this.poll(this.config.interval);\n }, this.config.interval);\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param networkClientId - The network client ID used to get balances.\n */\n async _executePoll(networkClientId: string): Promise<void> {\n this.refresh(networkClientId);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n */\n refresh = async (networkClientId?: NetworkClientId) => {\n const releaseLock = await this.refreshMutex.acquire();\n try {\n const { chainId, ethQuery } =\n this.#getCorrectNetworkClient(networkClientId);\n this.syncAccounts(chainId);\n const { accounts, accountsByChainId } = this.state;\n const isMultiAccountBalancesEnabled =\n this.getMultiAccountBalancesEnabled();\n\n const accountsToUpdate = isMultiAccountBalancesEnabled\n ? Object.keys(accounts)\n : [this.getSelectedAddress()];\n\n const accountsForChain = { ...accountsByChainId[chainId] };\n for (const address of accountsToUpdate) {\n const balance = await this.getBalanceFromChain(address, ethQuery);\n if (balance) {\n accountsForChain[address] = {\n balance,\n };\n }\n }\n\n this.update({\n ...(chainId === this.getCurrentChainId() && {\n accounts: accountsForChain,\n }),\n accountsByChainId: {\n ...this.state.accountsByChainId,\n [chainId]: accountsForChain,\n },\n });\n } finally {\n releaseLock();\n }\n };\n\n /**\n * Fetches the balance of a given address from the blockchain.\n *\n * @param address - The account address to fetch the balance for.\n * @param ethQuery - The EthQuery instance to query getBalnce with.\n * @returns A promise that resolves to the balance in a hex string format.\n */\n private async getBalanceFromChain(\n address: string,\n ethQuery?: EthQuery,\n ): Promise<string | undefined> {\n return await safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n return await query(ethQuery, 'getBalance', [address]);\n });\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<Record<string, { balance: string }>> {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n return await Promise.all(\n addresses.map((address): Promise<[string, string] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n return [address, balance];\n });\n }),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n },\n };\n }, {});\n });\n }\n}\n\nexport default AccountTrackerController;\n"],"mappings":"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","names":[]}
|
|
@@ -298,10 +298,8 @@ var CodefiTokenPricesServiceV2 = class {
|
|
|
298
298
|
if (!marketData) {
|
|
299
299
|
return obj;
|
|
300
300
|
}
|
|
301
|
-
const { price } = marketData;
|
|
302
301
|
const token = {
|
|
303
302
|
tokenAddress,
|
|
304
|
-
value: price,
|
|
305
303
|
currency,
|
|
306
304
|
...marketData
|
|
307
305
|
};
|
|
@@ -345,4 +343,4 @@ _tokenPricePolicy = new WeakMap();
|
|
|
345
343
|
|
|
346
344
|
|
|
347
345
|
exports.SUPPORTED_CURRENCIES = SUPPORTED_CURRENCIES; exports.ZERO_ADDRESS = ZERO_ADDRESS; exports.SUPPORTED_CHAIN_IDS = SUPPORTED_CHAIN_IDS; exports.CodefiTokenPricesServiceV2 = CodefiTokenPricesServiceV2;
|
|
348
|
-
//# sourceMappingURL=chunk-
|
|
346
|
+
//# sourceMappingURL=chunk-TN227P74.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/token-prices-service/codefi-v2.ts"],"names":[],"mappings":";;;;;;;AAAA,SAAS,mBAAmB;AAE5B,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYA,IAAM,uBAAuB;AAAA;AAAA,EAElC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;AAQO,IAAM,eACX;AAgBK,IAAM,sBAAsB;AAAA;AAAA,EAEjC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;AAYA,IAAM,WAAW;AAEjB,IAAM,8BAA8B;AAEpC,IAAM,gDACH,IAAI,+BAA+B;AAEtC,IAAM,6BAA6B;AAlQnC;AAuVO,IAAM,6BAAN,MAGP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBE,YAAY;AAAA,IACV,oBAAoB;AAAA,IACpB,UAAU;AAAA,IACV,6BAA6B;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,uBAAuB,KAAK,KAAK;AAAA,EACnC,IAOI,CAAC,GAAG;AAhCR;AAmCE,UAAM,cAAc,MAAM,WAAW;AAAA,MACnC,aAAa;AAAA,MACb,SAAS,IAAI,mBAAmB;AAAA,IAClC,CAAC;AACD,UAAM,uBAAuB,eAAe,WAAW;AAAA,MACrD,eAAe;AAAA,MACf,SAAS,IAAI,mBAAmB,0BAA0B;AAAA,IAC5D,CAAC;AACD,QAAI,SAAS;AACX,2BAAqB,QAAQ,OAAO;AAAA,IACtC;AACA,QAAI,YAAY;AACd,kBAAY,SAAS,MAAM;AACzB,YAAI,qBAAqB,UAAU,aAAa,QAAQ;AACtD,qBAAW;AAAA,QACb;AAAA,MACF,CAAC;AACD,kBAAY,UAAU,CAAC,EAAE,SAAS,MAAM;AACtC,YACE,qBAAqB,UAAU,aAAa,UAC5C,WAAW,mBACX;AACA,qBAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AACA,uBAAK,mBAAoB,KAAK,aAAa,oBAAoB;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,iBAAiB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIwE;AACtE,UAAM,kBAAkB,YAAY,OAAO;AAE3C,UAAM,MAAM,IAAI,IAAI,GAAG,QAAQ,WAAW,eAAe,cAAc;AACvE,QAAI,aAAa;AAAA,MACf;AAAA,MACA,CAAC,cAAc,GAAG,cAAc,EAAE,KAAK,GAAG;AAAA,IAC5C;AACA,QAAI,aAAa,OAAO,cAAc,QAAQ;AAC9C,QAAI,aAAa,OAAO,qBAAqB,MAAM;AAEnD,UAAM,uBACJ,MAAM,mBAAK,mBAAkB;AAAA,MAAQ,MACnC,YAAY,KAAK,EAAE,SAAS,EAAE,iBAAiB,WAAW,EAAE,CAAC;AAAA,IAC/D;AAEF,WAAO,CAAC,cAAc,GAAG,cAAc,EAAE;AAAA,MACvC,CACE,KACA,iBACG;AAGH,cAAM,yBACJ,aAAa,YAAY;AAE3B,cAAM,aAAa,qBAAqB,sBAAsB;AAE9D,YAAI,CAAC,YAAY;AACf,iBAAO;AAAA,QACT;AAEA,cAAM,QAA4C;AAAA,UAChD;AAAA,UACA;AAAA,UACA,GAAG;AAAA,QACL;AAEA,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,YAAY,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,MACA,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,yBAAyB,SAA+C;AACtE,UAAM,oBAAuC;AAC7C,WAAO,OAAO,YAAY,YAAY,kBAAkB,SAAS,OAAO;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,0BAA0B,UAAkD;AAC1E,UAAM,sBAAyC;AAC/C,WACE,OAAO,aAAa,YACpB,oBAAoB,SAAS,SAAS,YAAY,CAAC;AAAA,EAEvD;AACF;AA5JE","sourcesContent":["import { handleFetch } from '@metamask/controller-utils';\nimport type { Hex } from '@metamask/utils';\nimport { hexToNumber } from '@metamask/utils';\nimport {\n circuitBreaker,\n ConsecutiveBreaker,\n ExponentialBackoff,\n handleAll,\n type IPolicy,\n retry,\n wrap,\n CircuitState,\n} from 'cockatiel';\n\nimport type {\n AbstractTokenPricesService,\n TokenPrice,\n TokenPricesByTokenAddress,\n} from './abstract-token-prices-service';\n\n/**\n * The list of currencies that can be supplied as the `vsCurrency` parameter to\n * the `/spot-prices` endpoint, in lowercase form.\n */\nexport const SUPPORTED_CURRENCIES = [\n // Bitcoin\n 'btc',\n // Ether\n 'eth',\n // Litecoin\n 'ltc',\n // Bitcoin Cash\n 'bch',\n // Binance Coin\n 'bnb',\n // EOS\n 'eos',\n // XRP\n 'xrp',\n // Lumens\n 'xlm',\n // Chainlink\n 'link',\n // Polkadot\n 'dot',\n // Yearn.finance\n 'yfi',\n // US Dollar\n 'usd',\n // United Arab Emirates Dirham\n 'aed',\n // Argentine Peso\n 'ars',\n // Australian Dollar\n 'aud',\n // Bangladeshi Taka\n 'bdt',\n // Bahraini Dinar\n 'bhd',\n // Bermudian Dollar\n 'bmd',\n // Brazil Real\n 'brl',\n // Canadian Dollar\n 'cad',\n // Swiss Franc\n 'chf',\n // Chilean Peso\n 'clp',\n // Chinese Yuan\n 'cny',\n // Czech Koruna\n 'czk',\n // Danish Krone\n 'dkk',\n // Euro\n 'eur',\n // British Pound Sterling\n 'gbp',\n // Hong Kong Dollar\n 'hkd',\n // Hungarian Forint\n 'huf',\n // Indonesian Rupiah\n 'idr',\n // Israeli New Shekel\n 'ils',\n // Indian Rupee\n 'inr',\n // Japanese Yen\n 'jpy',\n // South Korean Won\n 'krw',\n // Kuwaiti Dinar\n 'kwd',\n // Sri Lankan Rupee\n 'lkr',\n // Burmese Kyat\n 'mmk',\n // Mexican Peso\n 'mxn',\n // Malaysian Ringgit\n 'myr',\n // Nigerian Naira\n 'ngn',\n // Norwegian Krone\n 'nok',\n // New Zealand Dollar\n 'nzd',\n // Philippine Peso\n 'php',\n // Pakistani Rupee\n 'pkr',\n // Polish Zloty\n 'pln',\n // Russian Ruble\n 'rub',\n // Saudi Riyal\n 'sar',\n // Swedish Krona\n 'sek',\n // Singapore Dollar\n 'sgd',\n // Thai Baht\n 'thb',\n // Turkish Lira\n 'try',\n // New Taiwan Dollar\n 'twd',\n // Ukrainian hryvnia\n 'uah',\n // Venezuelan bolívar fuerte\n 'vef',\n // Vietnamese đồng\n 'vnd',\n // South African Rand\n 'zar',\n // IMF Special Drawing Rights\n 'xdr',\n // Silver - Troy Ounce\n 'xag',\n // Gold - Troy Ounce\n 'xau',\n // Bits\n 'bits',\n // Satoshi\n 'sats',\n] as const;\n\n/**\n * Represents the zero address, commonly used as a placeholder in blockchain transactions.\n * In the context of fetching market data, the zero address is utilized to retrieve information\n * specifically for native currencies. This allows for a standardized approach to query market\n * data for blockchain-native assets without a specific contract address.\n */\nexport const ZERO_ADDRESS: Hex =\n '0x0000000000000000000000000000000000000000' as const;\n\n/**\n * A currency that can be supplied as the `vsCurrency` parameter to\n * the `/spot-prices` endpoint. Covers both uppercase and lowercase versions.\n */\ntype SupportedCurrency =\n | (typeof SUPPORTED_CURRENCIES)[number]\n | Uppercase<(typeof SUPPORTED_CURRENCIES)[number]>;\n\n/**\n * The list of chain IDs that can be supplied in the URL for the `/spot-prices`\n * endpoint, but in hexadecimal form (for consistency with how we represent\n * chain IDs in other places).\n * @see Used by {@link CodefiTokenPricesServiceV2} to validate that a given chain ID is supported by V2 of the Codefi Price API.\n */\nexport const SUPPORTED_CHAIN_IDS = [\n // Ethereum Mainnet\n '0x1',\n // OP Mainnet\n '0xa',\n // Cronos Mainnet\n '0x19',\n // BNB Smart Chain Mainnet\n '0x38',\n // Syscoin Mainnet\n '0x39',\n // OKXChain Mainnet\n '0x42',\n // Hoo Smart Chain\n '0x46',\n // Meter Mainnet\n '0x52',\n // TomoChain\n '0x58',\n // Gnosis\n '0x64',\n // Velas EVM Mainnet\n '0x6a',\n // Fuse Mainnet\n '0x7a',\n // Huobi ECO Chain Mainnet\n '0x80',\n // Polygon Mainnet\n '0x89',\n // Fantom Opera\n '0xfa',\n // Boba Network\n '0x120',\n // KCC Mainnet\n '0x141',\n // zkSync Era Mainnet\n '0x144',\n // Theta Mainnet\n '0x169',\n // Metis Andromeda Mainnet\n '0x440',\n // Moonbeam\n '0x504',\n // Moonriver\n '0x505',\n // Base\n '0x2105',\n // Shiden\n '0x150',\n // Smart Bitcoin Cash\n '0x2710',\n // Arbitrum One\n '0xa4b1',\n // Celo Mainnet\n '0xa4ec',\n // Oasis Emerald\n '0xa516',\n // Avalanche C-Chain\n '0xa86a',\n // Polis Mainnet\n '0x518af',\n // Aurora Mainnet\n '0x4e454152',\n // Harmony Mainnet Shard 0\n '0x63564c40',\n // Linea Mainnet\n '0xe708',\n] as const;\n\n/**\n * A chain ID that can be supplied in the URL for the `/spot-prices` endpoint,\n * but in hexadecimal form (for consistency with how we represent chain IDs in\n * other places).\n */\ntype SupportedChainId = (typeof SUPPORTED_CHAIN_IDS)[number];\n\n/**\n * All requests to V2 of the Price API start with this.\n */\nconst BASE_URL = 'https://price.api.cx.metamask.io/v2';\n\nconst DEFAULT_TOKEN_PRICE_RETRIES = 3;\n// Each update attempt will result (1 + retries) calls if the server is down\nconst DEFAULT_TOKEN_PRICE_MAX_CONSECUTIVE_FAILURES =\n (1 + DEFAULT_TOKEN_PRICE_RETRIES) * 3;\n\nconst DEFAULT_DEGRADED_THRESHOLD = 5_000;\n\n/**\n * The shape of the data that the /spot-prices endpoint returns.\n */\ntype MarketData = {\n /**\n * The all-time highest price of the token.\n */\n allTimeHigh: number;\n /**\n * The all-time lowest price of the token.\n */\n allTimeLow: number;\n /**\n * The number of tokens currently in circulation.\n */\n circulatingSupply: number;\n /**\n * The market cap calculated using the diluted supply.\n */\n dilutedMarketCap: number;\n /**\n * The highest price of the token in the last 24 hours.\n */\n high1d: number;\n /**\n * The lowest price of the token in the last 24 hours.\n */\n low1d: number;\n /**\n * The current market capitalization of the token.\n */\n marketCap: number;\n /**\n * The percentage change in market capitalization over the last 24 hours.\n */\n marketCapPercentChange1d: number;\n /**\n * The current price of the token.\n */\n price: number;\n /**\n * The absolute change in price over the last 24 hours.\n */\n priceChange1d: number;\n /**\n * The percentage change in price over the last 24 hours.\n */\n pricePercentChange1d: number;\n /**\n * The percentage change in price over the last hour.\n */\n pricePercentChange1h: number;\n /**\n * The percentage change in price over the last year.\n */\n pricePercentChange1y: number;\n /**\n * The percentage change in price over the last 7 days.\n */\n pricePercentChange7d: number;\n /**\n * The percentage change in price over the last 14 days.\n */\n pricePercentChange14d: number;\n /**\n * The percentage change in price over the last 30 days.\n */\n pricePercentChange30d: number;\n /**\n * The percentage change in price over the last 200 days.\n */\n pricePercentChange200d: number;\n /**\n * The total trading volume of the token in the last 24 hours.\n */\n totalVolume: number;\n};\n\ntype MarketDataByTokenAddress = { [address: Hex]: MarketData };\n/**\n * This version of the token prices service uses V2 of the Codefi Price API to\n * fetch token prices.\n */\nexport class CodefiTokenPricesServiceV2\n implements\n AbstractTokenPricesService<SupportedChainId, Hex, SupportedCurrency>\n{\n #tokenPricePolicy: IPolicy;\n\n /**\n * Construct a Codefi Token Price Service.\n *\n * @param options - Constructor options\n * @param options.degradedThreshold - The threshold between \"normal\" and \"degrated\" service,\n * in milliseconds.\n * @param options.retries - Number of retry attempts for each token price update.\n * @param options.maximumConsecutiveFailures - The maximum number of consecutive failures\n * allowed before breaking the circuit and pausing further updates.\n * @param options.onBreak - An event handler for when the circuit breaks, useful for capturing\n * metrics about network failures.\n * @param options.onDegraded - An event handler for when the circuit remains closed, but requests\n * are failing or resolving too slowly (i.e. resolving more slowly than the `degradedThreshold).\n * @param options.circuitBreakDuration - The amount of time to wait when the circuit breaks\n * from too many consecutive failures.\n */\n constructor({\n degradedThreshold = DEFAULT_DEGRADED_THRESHOLD,\n retries = DEFAULT_TOKEN_PRICE_RETRIES,\n maximumConsecutiveFailures = DEFAULT_TOKEN_PRICE_MAX_CONSECUTIVE_FAILURES,\n onBreak,\n onDegraded,\n circuitBreakDuration = 30 * 60 * 1000,\n }: {\n degradedThreshold?: number;\n retries?: number;\n maximumConsecutiveFailures?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n circuitBreakDuration?: number;\n } = {}) {\n // Construct a policy that will retry each update, and halt further updates\n // for a certain period after too many consecutive failures.\n const retryPolicy = retry(handleAll, {\n maxAttempts: retries,\n backoff: new ExponentialBackoff(),\n });\n const circuitBreakerPolicy = circuitBreaker(handleAll, {\n halfOpenAfter: circuitBreakDuration,\n breaker: new ConsecutiveBreaker(maximumConsecutiveFailures),\n });\n if (onBreak) {\n circuitBreakerPolicy.onBreak(onBreak);\n }\n if (onDegraded) {\n retryPolicy.onGiveUp(() => {\n if (circuitBreakerPolicy.state === CircuitState.Closed) {\n onDegraded();\n }\n });\n retryPolicy.onSuccess(({ duration }) => {\n if (\n circuitBreakerPolicy.state === CircuitState.Closed &&\n duration > degradedThreshold\n ) {\n onDegraded();\n }\n });\n }\n this.#tokenPricePolicy = wrap(retryPolicy, circuitBreakerPolicy);\n }\n\n /**\n * Retrieves prices in the given currency for the tokens identified by the\n * given addresses which are expected to live on the given chain.\n *\n * @param args - The arguments to function.\n * @param args.chainId - An EIP-155 chain ID.\n * @param args.tokenAddresses - Addresses for tokens that live on the chain.\n * @param args.currency - The desired currency of the token prices.\n * @returns The prices for the requested tokens.\n */\n async fetchTokenPrices({\n chainId,\n tokenAddresses,\n currency,\n }: {\n chainId: SupportedChainId;\n tokenAddresses: Hex[];\n currency: SupportedCurrency;\n }): Promise<Partial<TokenPricesByTokenAddress<Hex, SupportedCurrency>>> {\n const chainIdAsNumber = hexToNumber(chainId);\n\n const url = new URL(`${BASE_URL}/chains/${chainIdAsNumber}/spot-prices`);\n url.searchParams.append(\n 'tokenAddresses',\n [ZERO_ADDRESS, ...tokenAddresses].join(','),\n );\n url.searchParams.append('vsCurrency', currency);\n url.searchParams.append('includeMarketData', 'true');\n\n const addressCryptoDataMap: MarketDataByTokenAddress =\n await this.#tokenPricePolicy.execute(() =>\n handleFetch(url, { headers: { 'Cache-Control': 'no-cache' } }),\n );\n\n return [ZERO_ADDRESS, ...tokenAddresses].reduce(\n (\n obj: Partial<TokenPricesByTokenAddress<Hex, SupportedCurrency>>,\n tokenAddress,\n ) => {\n // The Price API lowercases both currency and token addresses, so we have\n // to keep track of them and make sure we return the original versions.\n const lowercasedTokenAddress =\n tokenAddress.toLowerCase() as Lowercase<Hex>;\n\n const marketData = addressCryptoDataMap[lowercasedTokenAddress];\n\n if (!marketData) {\n return obj;\n }\n\n const token: TokenPrice<Hex, SupportedCurrency> = {\n tokenAddress,\n currency,\n ...marketData,\n };\n\n return {\n ...obj,\n [tokenAddress]: token,\n };\n },\n {},\n ) as Partial<TokenPricesByTokenAddress<Hex, SupportedCurrency>>;\n }\n\n /**\n * Type guard for whether the API can return token prices for the given chain\n * ID.\n *\n * @param chainId - The chain ID to check.\n * @returns True if the API supports the chain ID, false otherwise.\n */\n validateChainIdSupported(chainId: unknown): chainId is SupportedChainId {\n const supportedChainIds: readonly string[] = SUPPORTED_CHAIN_IDS;\n return typeof chainId === 'string' && supportedChainIds.includes(chainId);\n }\n\n /**\n * Type guard for whether the API can return token prices in the given\n * currency.\n *\n * @param currency - The currency to check. If a string, can be either\n * lowercase or uppercase.\n * @returns True if the API supports the currency, false otherwise.\n */\n validateCurrencySupported(currency: unknown): currency is SupportedCurrency {\n const supportedCurrencies: readonly string[] = SUPPORTED_CURRENCIES;\n return (\n typeof currency === 'string' &&\n supportedCurrencies.includes(currency.toLowerCase())\n );\n }\n}\n"]}
|
|
@@ -182,7 +182,7 @@ async function fetchTokenContractExchangeRates({
|
|
|
182
182
|
(obj, [tokenAddress, tokenPrice]) => {
|
|
183
183
|
return {
|
|
184
184
|
...obj,
|
|
185
|
-
[_controllerutils.toChecksumHexAddress.call(void 0, tokenAddress)]: tokenPrice?.
|
|
185
|
+
[_controllerutils.toChecksumHexAddress.call(void 0, tokenAddress)]: tokenPrice?.price
|
|
186
186
|
};
|
|
187
187
|
},
|
|
188
188
|
{}
|
|
@@ -206,4 +206,4 @@ async function fetchTokenContractExchangeRates({
|
|
|
206
206
|
|
|
207
207
|
|
|
208
208
|
exports.TOKEN_PRICES_BATCH_SIZE = TOKEN_PRICES_BATCH_SIZE; exports.compareNftMetadata = compareNftMetadata; exports.formatAggregatorNames = formatAggregatorNames; exports.formatIconUrlWithProxy = formatIconUrlWithProxy; exports.SupportedTokenDetectionNetworks = SupportedTokenDetectionNetworks; exports.isTokenDetectionSupportedForNetwork = isTokenDetectionSupportedForNetwork; exports.isTokenListSupportedForNetwork = isTokenListSupportedForNetwork; exports.removeIpfsProtocolPrefix = removeIpfsProtocolPrefix; exports.getIpfsCIDv1AndPath = getIpfsCIDv1AndPath; exports.getFormattedIpfsUrl = getFormattedIpfsUrl; exports.addUrlProtocolPrefix = addUrlProtocolPrefix; exports.ethersBigNumberToBN = ethersBigNumberToBN; exports.divideIntoBatches = divideIntoBatches; exports.reduceInBatchesSerially = reduceInBatchesSerially; exports.fetchTokenContractExchangeRates = fetchTokenContractExchangeRates;
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-VJCRI43T.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/assetsUtil.ts"],"names":["SupportedTokenDetectionNetworks"],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport {\n convertHexToDecimal,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { Hex } from '@metamask/utils';\nimport { remove0x } from '@metamask/utils';\nimport BN from 'bn.js';\nimport { CID } from 'multiformats/cid';\n\nimport type { Nft, NftMetadata } from './NftController';\nimport type { AbstractTokenPricesService } from './token-prices-service';\nimport { type ContractExchangeRates } from './TokenRatesController';\n\n/**\n * The maximum number of token addresses that should be sent to the Price API in\n * a single request.\n */\nexport const TOKEN_PRICES_BATCH_SIZE = 30;\n\n/**\n * Compares nft metadata entries to any nft entry.\n * We need this method when comparing a new fetched nft metadata, in case a entry changed to a defined value,\n * there's a need to update the nft in state.\n *\n * @param newNftMetadata - Nft metadata object.\n * @param nft - Nft object to compare with.\n * @returns Whether there are differences.\n */\nexport function compareNftMetadata(newNftMetadata: NftMetadata, nft: Nft) {\n const keys: (keyof NftMetadata)[] = [\n 'image',\n 'backgroundColor',\n 'imagePreview',\n 'imageThumbnail',\n 'imageOriginal',\n 'animation',\n 'animationOriginal',\n 'externalLink',\n 'tokenURI',\n ];\n const differentValues = keys.reduce((value, key) => {\n if (newNftMetadata[key] && newNftMetadata[key] !== nft[key]) {\n return value + 1;\n }\n return value;\n }, 0);\n return differentValues > 0;\n}\n\nconst aggregatorNameByKey: Record<string, string> = {\n aave: 'Aave',\n bancor: 'Bancor',\n cmc: 'CMC',\n cryptocom: 'Crypto.com',\n coinGecko: 'CoinGecko',\n oneInch: '1inch',\n paraswap: 'Paraswap',\n pmm: 'PMM',\n zapper: 'Zapper',\n zerion: 'Zerion',\n zeroEx: '0x',\n synthetix: 'Synthetix',\n yearn: 'Yearn',\n apeswap: 'ApeSwap',\n binanceDex: 'BinanceDex',\n pancakeTop100: 'PancakeTop100',\n pancakeExtended: 'PancakeExtended',\n balancer: 'Balancer',\n quickswap: 'QuickSwap',\n matcha: 'Matcha',\n pangolinDex: 'PangolinDex',\n pangolinDexStableCoin: 'PangolinDexStableCoin',\n pangolinDexAvaxBridge: 'PangolinDexAvaxBridge',\n traderJoe: 'TraderJoe',\n airswapLight: 'AirswapLight',\n kleros: 'Kleros',\n};\n\n/**\n * Formats aggregator names to presentable format.\n *\n * @param aggregators - List of token list names in camelcase.\n * @returns Formatted aggregator names.\n */\nexport const formatAggregatorNames = (aggregators: string[]) => {\n return aggregators.map(\n (key) =>\n aggregatorNameByKey[key] ||\n `${key[0].toUpperCase()}${key.substring(1, key.length)}`,\n );\n};\n\n/**\n * Format token list assets to use image proxy from Codefi.\n *\n * @param params - Object that contains chainID and tokenAddress.\n * @param params.chainId - ChainID of network in 0x-prefixed hexadecimal format.\n * @param params.tokenAddress - Address of token in mixed or lowercase.\n * @returns Formatted image url\n */\nexport const formatIconUrlWithProxy = ({\n chainId,\n tokenAddress,\n}: {\n chainId: Hex;\n tokenAddress: string;\n}) => {\n const chainIdDecimal = convertHexToDecimal(chainId).toString();\n return `https://static.cx.metamask.io/api/v1/tokenIcons/${chainIdDecimal}/${tokenAddress.toLowerCase()}.png`;\n};\n\n/**\n * Networks where token detection is supported - Values are in hex format\n */\nexport enum SupportedTokenDetectionNetworks {\n mainnet = '0x1', // decimal: 1\n bsc = '0x38', // decimal: 56\n polygon = '0x89', // decimal: 137\n avax = '0xa86a', // decimal: 43114\n aurora = '0x4e454152', // decimal: 1313161554\n linea_goerli = '0xe704', // decimal: 59140\n linea_mainnet = '0xe708', // decimal: 59144\n arbitrum = '0xa4b1', // decimal: 42161\n optimism = '0xa', // decimal: 10\n base = '0x2105', // decimal: 8453\n zksync = '0x144', // decimal: 324\n cronos = '0x19', // decimal: 25\n celo = '0xa4ec', // decimal: 42220\n gnosis = '0x64', // decimal: 100\n fantom = '0xfa', // decimal: 250\n polygon_zkevm = '0x44d', // decimal: 1101\n moonbeam = '0x504', // decimal: 1284\n moonriver = '0x505', // decimal: 1285\n}\n\n/**\n * Check if token detection is enabled for certain networks.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports token detection\n */\nexport function isTokenDetectionSupportedForNetwork(chainId: Hex): boolean {\n return Object.values<Hex>(SupportedTokenDetectionNetworks).includes(chainId);\n}\n\n/**\n * Check if token list polling is enabled for a given network.\n * Currently this method is used to support e2e testing for consumers of this package.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports tokenlists\n */\nexport function isTokenListSupportedForNetwork(chainId: Hex): boolean {\n return isTokenDetectionSupportedForNetwork(chainId);\n}\n\n/**\n * Removes IPFS protocol prefix from input string.\n *\n * @param ipfsUrl - An IPFS url (e.g. ipfs://{content id})\n * @returns IPFS content identifier and (possibly) path in a string\n * @throws Will throw if the url passed is not IPFS.\n */\nexport function removeIpfsProtocolPrefix(ipfsUrl: string) {\n if (ipfsUrl.startsWith('ipfs://ipfs/')) {\n return ipfsUrl.replace('ipfs://ipfs/', '');\n } else if (ipfsUrl.startsWith('ipfs://')) {\n return ipfsUrl.replace('ipfs://', '');\n }\n // this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)\n throw new Error('this method should not be used with non ipfs urls');\n}\n\n/**\n * Extracts content identifier and path from an input string.\n *\n * @param ipfsUrl - An IPFS URL minus the IPFS protocol prefix\n * @returns IFPS content identifier (cid) and sub path as string.\n * @throws Will throw if the url passed is not ipfs.\n */\nexport function getIpfsCIDv1AndPath(ipfsUrl: string): {\n cid: string;\n path?: string;\n} {\n const url = removeIpfsProtocolPrefix(ipfsUrl);\n\n // check if there is a path\n // (CID is everything preceding first forward slash, path is everything after)\n const index = url.indexOf('/');\n const cid = index !== -1 ? url.substring(0, index) : url;\n const path = index !== -1 ? url.substring(index) : undefined;\n\n // We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)\n // because most cid v0s appear to be incompatible with IPFS subdomains\n return {\n cid: CID.parse(cid).toV1().toString(),\n path,\n };\n}\n\n/**\n * Formats URL correctly for use retrieving assets hosted on IPFS.\n *\n * @param ipfsGateway - The users preferred IPFS gateway (full URL or just host).\n * @param ipfsUrl - The IFPS URL pointed at the asset.\n * @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.\n * @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.\n */\nexport function getFormattedIpfsUrl(\n ipfsGateway: string,\n ipfsUrl: string,\n subdomainSupported: boolean,\n): string {\n const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway));\n if (subdomainSupported) {\n const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl);\n return `${protocol}//${cid}.ipfs.${host}${path ?? ''}`;\n }\n const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl);\n return `${origin}/ipfs/${cidAndPath}`;\n}\n\n/**\n * Adds URL protocol prefix to input URL string if missing.\n *\n * @param urlString - An IPFS URL.\n * @returns A URL with a https:// prepended.\n */\nexport function addUrlProtocolPrefix(urlString: string): string {\n if (!urlString.match(/(^http:\\/\\/)|(^https:\\/\\/)/u)) {\n return `https://${urlString}`;\n }\n return urlString;\n}\n\n/**\n * Converts an Ethers BigNumber to a BN.\n *\n * @param bigNumber - An Ethers BigNumber instance.\n * @returns A BN object.\n */\nexport function ethersBigNumberToBN(bigNumber: BigNumber): BN {\n return new BN(remove0x(bigNumber.toHexString()), 'hex');\n}\n\n/**\n * Partitions a list of values into groups that are at most `batchSize` in\n * length.\n *\n * @param values - The list of values.\n * @param args - The remaining arguments.\n * @param args.batchSize - The desired maximum number of values per batch.\n * @returns The list of batches.\n */\nexport function divideIntoBatches<Value>(\n values: Value[],\n { batchSize }: { batchSize: number },\n): Value[][] {\n const batches = [];\n for (let i = 0; i < values.length; i += batchSize) {\n batches.push(values.slice(i, i + batchSize));\n }\n return batches;\n}\n\n/**\n * Constructs an object from processing batches of the given values\n * sequentially.\n *\n * @param args - The arguments to this function.\n * @param args.values - A list of values to iterate over.\n * @param args.batchSize - The maximum number of values in each batch.\n * @param args.eachBatch - A function to call for each batch. This function is\n * similar to the function that `Array.prototype.reduce` takes, in that it\n * receives the object that is being built, each batch in the list of batches\n * and the index, and should return an updated version of the object.\n * @param args.initialResult - The initial value of the final data structure,\n * i.e., the value that will be fed into the first call of `eachBatch`.\n * @returns The built object.\n */\nexport async function reduceInBatchesSerially<\n Value,\n Result extends Record<PropertyKey, unknown>,\n>({\n values,\n batchSize,\n eachBatch,\n initialResult,\n}: {\n values: Value[];\n batchSize: number;\n eachBatch: (\n workingResult: Partial<Result>,\n batch: Value[],\n index: number,\n ) => Partial<Result> | Promise<Partial<Result>>;\n initialResult: Partial<Result>;\n}): Promise<Result> {\n const batches = divideIntoBatches(values, { batchSize });\n let workingResult = initialResult;\n for (const [index, batch] of batches.entries()) {\n workingResult = await eachBatch(workingResult, batch, index);\n }\n // There's no way around this — we have to assume that in the end, the result\n // matches the intended type.\n const finalResult = workingResult as Result;\n return finalResult;\n}\n\n/**\n * Retrieves token prices for a set of contract addresses in a specific currency and chainId.\n *\n * @param args - The arguments to function.\n * @param args.tokenPricesService - An object in charge of retrieving token prices.\n * @param args.nativeCurrency - The native currency to request price in.\n * @param args.tokenAddresses - The list of contract addresses.\n * @param args.chainId - The chainId of the tokens.\n * @returns The prices for the requested tokens.\n */\nexport async function fetchTokenContractExchangeRates({\n tokenPricesService,\n nativeCurrency,\n tokenAddresses,\n chainId,\n}: {\n tokenPricesService: AbstractTokenPricesService;\n nativeCurrency: string;\n tokenAddresses: Hex[];\n chainId: Hex;\n}): Promise<ContractExchangeRates> {\n const isChainIdSupported =\n tokenPricesService.validateChainIdSupported(chainId);\n const isCurrencySupported =\n tokenPricesService.validateCurrencySupported(nativeCurrency);\n\n if (!isChainIdSupported || !isCurrencySupported) {\n return {};\n }\n\n const tokenPricesByTokenAddress = await reduceInBatchesSerially<\n Hex,\n Awaited<ReturnType<AbstractTokenPricesService['fetchTokenPrices']>>\n >({\n values: [...tokenAddresses].sort(),\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n eachBatch: async (allTokenPricesByTokenAddress, batch) => {\n const tokenPricesByTokenAddressForBatch =\n await tokenPricesService.fetchTokenPrices({\n tokenAddresses: batch,\n chainId,\n currency: nativeCurrency,\n });\n\n return {\n ...allTokenPricesByTokenAddress,\n ...tokenPricesByTokenAddressForBatch,\n };\n },\n initialResult: {},\n });\n\n return Object.entries(tokenPricesByTokenAddress).reduce(\n (obj, [tokenAddress, tokenPrice]) => {\n return {\n ...obj,\n [toChecksumHexAddress(tokenAddress)]: tokenPrice?.value,\n };\n },\n {},\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/assetsUtil.ts"],"names":["SupportedTokenDetectionNetworks"],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport {\n convertHexToDecimal,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { Hex } from '@metamask/utils';\nimport { remove0x } from '@metamask/utils';\nimport BN from 'bn.js';\nimport { CID } from 'multiformats/cid';\n\nimport type { Nft, NftMetadata } from './NftController';\nimport type { AbstractTokenPricesService } from './token-prices-service';\nimport { type ContractExchangeRates } from './TokenRatesController';\n\n/**\n * The maximum number of token addresses that should be sent to the Price API in\n * a single request.\n */\nexport const TOKEN_PRICES_BATCH_SIZE = 30;\n\n/**\n * Compares nft metadata entries to any nft entry.\n * We need this method when comparing a new fetched nft metadata, in case a entry changed to a defined value,\n * there's a need to update the nft in state.\n *\n * @param newNftMetadata - Nft metadata object.\n * @param nft - Nft object to compare with.\n * @returns Whether there are differences.\n */\nexport function compareNftMetadata(newNftMetadata: NftMetadata, nft: Nft) {\n const keys: (keyof NftMetadata)[] = [\n 'image',\n 'backgroundColor',\n 'imagePreview',\n 'imageThumbnail',\n 'imageOriginal',\n 'animation',\n 'animationOriginal',\n 'externalLink',\n 'tokenURI',\n ];\n const differentValues = keys.reduce((value, key) => {\n if (newNftMetadata[key] && newNftMetadata[key] !== nft[key]) {\n return value + 1;\n }\n return value;\n }, 0);\n return differentValues > 0;\n}\n\nconst aggregatorNameByKey: Record<string, string> = {\n aave: 'Aave',\n bancor: 'Bancor',\n cmc: 'CMC',\n cryptocom: 'Crypto.com',\n coinGecko: 'CoinGecko',\n oneInch: '1inch',\n paraswap: 'Paraswap',\n pmm: 'PMM',\n zapper: 'Zapper',\n zerion: 'Zerion',\n zeroEx: '0x',\n synthetix: 'Synthetix',\n yearn: 'Yearn',\n apeswap: 'ApeSwap',\n binanceDex: 'BinanceDex',\n pancakeTop100: 'PancakeTop100',\n pancakeExtended: 'PancakeExtended',\n balancer: 'Balancer',\n quickswap: 'QuickSwap',\n matcha: 'Matcha',\n pangolinDex: 'PangolinDex',\n pangolinDexStableCoin: 'PangolinDexStableCoin',\n pangolinDexAvaxBridge: 'PangolinDexAvaxBridge',\n traderJoe: 'TraderJoe',\n airswapLight: 'AirswapLight',\n kleros: 'Kleros',\n};\n\n/**\n * Formats aggregator names to presentable format.\n *\n * @param aggregators - List of token list names in camelcase.\n * @returns Formatted aggregator names.\n */\nexport const formatAggregatorNames = (aggregators: string[]) => {\n return aggregators.map(\n (key) =>\n aggregatorNameByKey[key] ||\n `${key[0].toUpperCase()}${key.substring(1, key.length)}`,\n );\n};\n\n/**\n * Format token list assets to use image proxy from Codefi.\n *\n * @param params - Object that contains chainID and tokenAddress.\n * @param params.chainId - ChainID of network in 0x-prefixed hexadecimal format.\n * @param params.tokenAddress - Address of token in mixed or lowercase.\n * @returns Formatted image url\n */\nexport const formatIconUrlWithProxy = ({\n chainId,\n tokenAddress,\n}: {\n chainId: Hex;\n tokenAddress: string;\n}) => {\n const chainIdDecimal = convertHexToDecimal(chainId).toString();\n return `https://static.cx.metamask.io/api/v1/tokenIcons/${chainIdDecimal}/${tokenAddress.toLowerCase()}.png`;\n};\n\n/**\n * Networks where token detection is supported - Values are in hex format\n */\nexport enum SupportedTokenDetectionNetworks {\n mainnet = '0x1', // decimal: 1\n bsc = '0x38', // decimal: 56\n polygon = '0x89', // decimal: 137\n avax = '0xa86a', // decimal: 43114\n aurora = '0x4e454152', // decimal: 1313161554\n linea_goerli = '0xe704', // decimal: 59140\n linea_mainnet = '0xe708', // decimal: 59144\n arbitrum = '0xa4b1', // decimal: 42161\n optimism = '0xa', // decimal: 10\n base = '0x2105', // decimal: 8453\n zksync = '0x144', // decimal: 324\n cronos = '0x19', // decimal: 25\n celo = '0xa4ec', // decimal: 42220\n gnosis = '0x64', // decimal: 100\n fantom = '0xfa', // decimal: 250\n polygon_zkevm = '0x44d', // decimal: 1101\n moonbeam = '0x504', // decimal: 1284\n moonriver = '0x505', // decimal: 1285\n}\n\n/**\n * Check if token detection is enabled for certain networks.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports token detection\n */\nexport function isTokenDetectionSupportedForNetwork(chainId: Hex): boolean {\n return Object.values<Hex>(SupportedTokenDetectionNetworks).includes(chainId);\n}\n\n/**\n * Check if token list polling is enabled for a given network.\n * Currently this method is used to support e2e testing for consumers of this package.\n *\n * @param chainId - ChainID of network\n * @returns Whether the current network supports tokenlists\n */\nexport function isTokenListSupportedForNetwork(chainId: Hex): boolean {\n return isTokenDetectionSupportedForNetwork(chainId);\n}\n\n/**\n * Removes IPFS protocol prefix from input string.\n *\n * @param ipfsUrl - An IPFS url (e.g. ipfs://{content id})\n * @returns IPFS content identifier and (possibly) path in a string\n * @throws Will throw if the url passed is not IPFS.\n */\nexport function removeIpfsProtocolPrefix(ipfsUrl: string) {\n if (ipfsUrl.startsWith('ipfs://ipfs/')) {\n return ipfsUrl.replace('ipfs://ipfs/', '');\n } else if (ipfsUrl.startsWith('ipfs://')) {\n return ipfsUrl.replace('ipfs://', '');\n }\n // this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)\n throw new Error('this method should not be used with non ipfs urls');\n}\n\n/**\n * Extracts content identifier and path from an input string.\n *\n * @param ipfsUrl - An IPFS URL minus the IPFS protocol prefix\n * @returns IFPS content identifier (cid) and sub path as string.\n * @throws Will throw if the url passed is not ipfs.\n */\nexport function getIpfsCIDv1AndPath(ipfsUrl: string): {\n cid: string;\n path?: string;\n} {\n const url = removeIpfsProtocolPrefix(ipfsUrl);\n\n // check if there is a path\n // (CID is everything preceding first forward slash, path is everything after)\n const index = url.indexOf('/');\n const cid = index !== -1 ? url.substring(0, index) : url;\n const path = index !== -1 ? url.substring(index) : undefined;\n\n // We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)\n // because most cid v0s appear to be incompatible with IPFS subdomains\n return {\n cid: CID.parse(cid).toV1().toString(),\n path,\n };\n}\n\n/**\n * Formats URL correctly for use retrieving assets hosted on IPFS.\n *\n * @param ipfsGateway - The users preferred IPFS gateway (full URL or just host).\n * @param ipfsUrl - The IFPS URL pointed at the asset.\n * @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.\n * @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.\n */\nexport function getFormattedIpfsUrl(\n ipfsGateway: string,\n ipfsUrl: string,\n subdomainSupported: boolean,\n): string {\n const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway));\n if (subdomainSupported) {\n const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl);\n return `${protocol}//${cid}.ipfs.${host}${path ?? ''}`;\n }\n const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl);\n return `${origin}/ipfs/${cidAndPath}`;\n}\n\n/**\n * Adds URL protocol prefix to input URL string if missing.\n *\n * @param urlString - An IPFS URL.\n * @returns A URL with a https:// prepended.\n */\nexport function addUrlProtocolPrefix(urlString: string): string {\n if (!urlString.match(/(^http:\\/\\/)|(^https:\\/\\/)/u)) {\n return `https://${urlString}`;\n }\n return urlString;\n}\n\n/**\n * Converts an Ethers BigNumber to a BN.\n *\n * @param bigNumber - An Ethers BigNumber instance.\n * @returns A BN object.\n */\nexport function ethersBigNumberToBN(bigNumber: BigNumber): BN {\n return new BN(remove0x(bigNumber.toHexString()), 'hex');\n}\n\n/**\n * Partitions a list of values into groups that are at most `batchSize` in\n * length.\n *\n * @param values - The list of values.\n * @param args - The remaining arguments.\n * @param args.batchSize - The desired maximum number of values per batch.\n * @returns The list of batches.\n */\nexport function divideIntoBatches<Value>(\n values: Value[],\n { batchSize }: { batchSize: number },\n): Value[][] {\n const batches = [];\n for (let i = 0; i < values.length; i += batchSize) {\n batches.push(values.slice(i, i + batchSize));\n }\n return batches;\n}\n\n/**\n * Constructs an object from processing batches of the given values\n * sequentially.\n *\n * @param args - The arguments to this function.\n * @param args.values - A list of values to iterate over.\n * @param args.batchSize - The maximum number of values in each batch.\n * @param args.eachBatch - A function to call for each batch. This function is\n * similar to the function that `Array.prototype.reduce` takes, in that it\n * receives the object that is being built, each batch in the list of batches\n * and the index, and should return an updated version of the object.\n * @param args.initialResult - The initial value of the final data structure,\n * i.e., the value that will be fed into the first call of `eachBatch`.\n * @returns The built object.\n */\nexport async function reduceInBatchesSerially<\n Value,\n Result extends Record<PropertyKey, unknown>,\n>({\n values,\n batchSize,\n eachBatch,\n initialResult,\n}: {\n values: Value[];\n batchSize: number;\n eachBatch: (\n workingResult: Partial<Result>,\n batch: Value[],\n index: number,\n ) => Partial<Result> | Promise<Partial<Result>>;\n initialResult: Partial<Result>;\n}): Promise<Result> {\n const batches = divideIntoBatches(values, { batchSize });\n let workingResult = initialResult;\n for (const [index, batch] of batches.entries()) {\n workingResult = await eachBatch(workingResult, batch, index);\n }\n // There's no way around this — we have to assume that in the end, the result\n // matches the intended type.\n const finalResult = workingResult as Result;\n return finalResult;\n}\n\n/**\n * Retrieves token prices for a set of contract addresses in a specific currency and chainId.\n *\n * @param args - The arguments to function.\n * @param args.tokenPricesService - An object in charge of retrieving token prices.\n * @param args.nativeCurrency - The native currency to request price in.\n * @param args.tokenAddresses - The list of contract addresses.\n * @param args.chainId - The chainId of the tokens.\n * @returns The prices for the requested tokens.\n */\nexport async function fetchTokenContractExchangeRates({\n tokenPricesService,\n nativeCurrency,\n tokenAddresses,\n chainId,\n}: {\n tokenPricesService: AbstractTokenPricesService;\n nativeCurrency: string;\n tokenAddresses: Hex[];\n chainId: Hex;\n}): Promise<ContractExchangeRates> {\n const isChainIdSupported =\n tokenPricesService.validateChainIdSupported(chainId);\n const isCurrencySupported =\n tokenPricesService.validateCurrencySupported(nativeCurrency);\n\n if (!isChainIdSupported || !isCurrencySupported) {\n return {};\n }\n\n const tokenPricesByTokenAddress = await reduceInBatchesSerially<\n Hex,\n Awaited<ReturnType<AbstractTokenPricesService['fetchTokenPrices']>>\n >({\n values: [...tokenAddresses].sort(),\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n eachBatch: async (allTokenPricesByTokenAddress, batch) => {\n const tokenPricesByTokenAddressForBatch =\n await tokenPricesService.fetchTokenPrices({\n tokenAddresses: batch,\n chainId,\n currency: nativeCurrency,\n });\n\n return {\n ...allTokenPricesByTokenAddress,\n ...tokenPricesByTokenAddressForBatch,\n };\n },\n initialResult: {},\n });\n\n return Object.entries(tokenPricesByTokenAddress).reduce(\n (obj, [tokenAddress, tokenPrice]) => {\n return {\n ...obj,\n [toChecksumHexAddress(tokenAddress)]: tokenPrice?.price,\n };\n },\n {},\n );\n}\n"]}
|