@metamask/assets-controllers 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/LICENSE +20 -0
- package/README.md +30 -0
- package/dist/AccountTrackerController.d.ts +88 -0
- package/dist/AccountTrackerController.js +138 -0
- package/dist/AccountTrackerController.js.map +1 -0
- package/dist/AssetsContractController.d.ts +176 -0
- package/dist/AssetsContractController.js +314 -0
- package/dist/AssetsContractController.js.map +1 -0
- package/dist/CurrencyRateController.d.ts +98 -0
- package/dist/CurrencyRateController.js +193 -0
- package/dist/CurrencyRateController.js.map +1 -0
- package/dist/NftController.d.ts +409 -0
- package/dist/NftController.js +835 -0
- package/dist/NftController.js.map +1 -0
- package/dist/NftDetectionController.d.ts +179 -0
- package/dist/NftDetectionController.js +204 -0
- package/dist/NftDetectionController.js.map +1 -0
- package/dist/Standards/ERC20Standard.d.ts +42 -0
- package/dist/Standards/ERC20Standard.js +121 -0
- package/dist/Standards/ERC20Standard.js.map +1 -0
- package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.d.ts +78 -0
- package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.js +148 -0
- package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.js.map +1 -0
- package/dist/Standards/NftStandards/ERC721/ERC721Standard.d.ts +88 -0
- package/dist/Standards/NftStandards/ERC721/ERC721Standard.js +182 -0
- package/dist/Standards/NftStandards/ERC721/ERC721Standard.js.map +1 -0
- package/dist/Standards/standards-types.d.ts +14 -0
- package/dist/Standards/standards-types.js +3 -0
- package/dist/Standards/standards-types.js.map +1 -0
- package/dist/TokenBalancesController.d.ts +69 -0
- package/dist/TokenBalancesController.js +94 -0
- package/dist/TokenBalancesController.js.map +1 -0
- package/dist/TokenDetectionController.d.ts +84 -0
- package/dist/TokenDetectionController.js +185 -0
- package/dist/TokenDetectionController.js.map +1 -0
- package/dist/TokenListController.d.ts +114 -0
- package/dist/TokenListController.js +256 -0
- package/dist/TokenListController.js.map +1 -0
- package/dist/TokenRatesController.d.ts +167 -0
- package/dist/TokenRatesController.js +284 -0
- package/dist/TokenRatesController.js.map +1 -0
- package/dist/TokensController.d.ts +238 -0
- package/dist/TokensController.js +530 -0
- package/dist/TokensController.js.map +1 -0
- package/dist/assetsUtil.d.ts +106 -0
- package/dist/assetsUtil.js +228 -0
- package/dist/assetsUtil.js.map +1 -0
- package/dist/crypto-compare.d.ts +12 -0
- package/dist/crypto-compare.js +67 -0
- package/dist/crypto-compare.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/token-service.d.ts +29 -0
- package/dist/token-service.js +134 -0
- package/dist/token-service.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ERC721Standard.js","sourceRoot":"","sources":["../../../../src/Standards/NftStandards/ERC721/ERC721Standard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mEAAwD;AACxD,wDAAoD;AACpD,iEAMoC;AAEpC,oDAA0D;AAE1D,MAAa,cAAc;IAGzB,YAAY,QAAsB;QAIlC;;;;;WAKG;QACH,sCAAiC,GAAG,CAClC,OAAe,EACG,EAAE;YACpB,OAAO,IAAI,CAAC,yBAAyB,CACnC,OAAO,EACP,+CAA4B,CAC7B,CAAC;QACJ,CAAC,CAAA,CAAC;QAEF;;;;;WAKG;QACH,wCAAmC,GAAG,CACpC,OAAe,EACG,EAAE;YACpB,OAAO,IAAI,CAAC,yBAAyB,CACnC,OAAO,EACP,iDAA8B,CAC/B,CAAC;QACJ,CAAC,CAAA,CAAC;QAEF;;;;;WAKG;QACH,qCAAgC,GAAG,CACjC,OAAe,EACG,EAAE;YACpB,OAAO,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,sCAAmB,CAAC,CAAC;QACtE,CAAC,CAAA,CAAC;QAEF;;;;;;;WAOG;QACH,kBAAa,GAAG,CACd,OAAe,EACf,eAAuB,EACvB,KAAa,EACI,EAAE;YACnB,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAAC,OAAO,EAAE,6BAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjE,OAAO,QAAQ,CAAC,mBAAmB,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC,CAAA,CAAC;QAEF;;;;;;WAMG;QACH,gBAAW,GAAG,CAAO,OAAe,EAAE,OAAe,EAAmB,EAAE;YACxE,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAAC,OAAO,EAAE,6BAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjE,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,iCAAiC,CACnE,OAAO,CACR,CAAC;YACF,IAAI,CAAC,gBAAgB,EAAE;gBACrB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;aACzE;YACD,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC,CAAA,CAAC;QAEF;;;;;WAKG;QACH,iBAAY,GAAG,CAAO,OAAe,EAAmB,EAAE;YACxD,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAAC,OAAO,EAAE,6BAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC,CAAA,CAAC;QAEF;;;;;WAKG;QACH,mBAAc,GAAG,CAAO,OAAe,EAAmB,EAAE;YAC1D,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAAC,OAAO,EAAE,6BAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjE,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3B,CAAC,CAAA,CAAC;QAcF;;;;;;WAMG;QACK,8BAAyB,GAAG,CAClC,OAAe,EACf,WAAmB,EACD,EAAE;YACpB,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAAC,OAAO,EAAE,6BAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjE,IAAI;gBACF,OAAO,MAAM,QAAQ,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;aACtD;YAAC,OAAO,GAAQ,EAAE;gBACjB,iCAAiC;gBACjC,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE;oBACjD,OAAO,KAAK,CAAC;iBACd;gBACD,MAAM,GAAG,CAAC;aACX;QACH,CAAC,CAAA,CAAC;QAEF;;;;;;;WAOG;QACH,eAAU,GAAG,CACX,OAAe,EACf,WAAmB,EACnB,OAAgB,EAOf,EAAE;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gCAAgC,CAAC,OAAO,CAAC,CAAC;YACtE,IAAI,CAAC,QAAQ,EAAE;gBACb,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;aACvD;YAED,IAAI,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC;YAElC,wGAAwG;YACxG,IAAI;gBACF,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;aAC7C;YAAC,WAAM;gBACN,SAAS;aACV;YAED,IAAI;gBACF,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;aACzC;YAAC,WAAM;gBACN,SAAS;aACV;YAED,IAAI,OAAO,EAAE;gBACX,IAAI;oBACF,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBACpD,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;wBAClC,QAAQ,GAAG,IAAA,gCAAmB,EAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;qBAC7D;oBAED,MAAM,QAAQ,GAAG,MAAM,IAAA,+BAAY,EAAC,QAAQ,CAAC,CAAC;oBAC9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACrC,KAAK,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,KAAK,CAAC;oBACtB,IAAI,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,UAAU,CAAC,SAAS,CAAC,EAAE;wBAChC,KAAK,GAAG,IAAA,gCAAmB,EAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;qBACvD;iBACF;gBAAC,WAAM;oBACN,SAAS;iBACV;aACF;YAED,OAAO;gBACL,QAAQ,EAAE,yBAAM;gBAChB,QAAQ;gBACR,MAAM;gBACN,IAAI;gBACJ,KAAK;aACN,CAAC;QACJ,CAAC,CAAA,CAAC;QAzMA,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAqGD;;;;;;OAMG;IACG,UAAU,CAAC,OAAe,EAAE,OAAe;;YAC/C,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAAC,OAAO,EAAE,6BAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjE,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;KAAA;CA0FF;AA9MD,wCA8MC","sourcesContent":["import { abiERC721 } from '@metamask/metamask-eth-abis';\nimport { Contract } from '@ethersproject/contracts';\nimport {\n timeoutFetch,\n ERC721_INTERFACE_ID,\n ERC721_METADATA_INTERFACE_ID,\n ERC721_ENUMERABLE_INTERFACE_ID,\n ERC721,\n} from '@metamask/controller-utils';\nimport { Web3Provider } from '@ethersproject/providers';\nimport { getFormattedIpfsUrl } from '../../../assetsUtil';\n\nexport class ERC721Standard {\n private provider: Web3Provider;\n\n constructor(provider: Web3Provider) {\n this.provider = provider;\n }\n\n /**\n * Query if contract implements ERC721Metadata interface.\n *\n * @param address - ERC721 asset contract address.\n * @returns Promise resolving to whether the contract implements ERC721Metadata interface.\n */\n contractSupportsMetadataInterface = async (\n address: string,\n ): Promise<boolean> => {\n return this.contractSupportsInterface(\n address,\n ERC721_METADATA_INTERFACE_ID,\n );\n };\n\n /**\n * Query if contract implements ERC721Enumerable interface.\n *\n * @param address - ERC721 asset contract address.\n * @returns Promise resolving to whether the contract implements ERC721Enumerable interface.\n */\n contractSupportsEnumerableInterface = async (\n address: string,\n ): Promise<boolean> => {\n return this.contractSupportsInterface(\n address,\n ERC721_ENUMERABLE_INTERFACE_ID,\n );\n };\n\n /**\n * Query if contract implements ERC721 interface.\n *\n * @param address - ERC721 asset contract address.\n * @returns Promise resolving to whether the contract implements ERC721 interface.\n */\n contractSupportsBase721Interface = async (\n address: string,\n ): Promise<boolean> => {\n return this.contractSupportsInterface(address, ERC721_INTERFACE_ID);\n };\n\n /**\n * Enumerate assets assigned to an owner.\n *\n * @param address - ERC721 asset contract address.\n * @param selectedAddress - Current account public address.\n * @param index - An NFT counter less than `balanceOf(selectedAddress)`.\n * @returns Promise resolving to token identifier for the 'index'th asset assigned to 'selectedAddress'.\n */\n getNftTokenId = async (\n address: string,\n selectedAddress: string,\n index: number,\n ): Promise<string> => {\n const contract = new Contract(address, abiERC721, this.provider);\n return contract.tokenOfOwnerByIndex(selectedAddress, index);\n };\n\n /**\n * Query for tokenURI for a given asset.\n *\n * @param address - ERC721 asset contract address.\n * @param tokenId - ERC721 asset identifier.\n * @returns Promise resolving to the 'tokenURI'.\n */\n getTokenURI = async (address: string, tokenId: string): Promise<string> => {\n const contract = new Contract(address, abiERC721, this.provider);\n const supportsMetadata = await this.contractSupportsMetadataInterface(\n address,\n );\n if (!supportsMetadata) {\n throw new Error('Contract does not support ERC721 metadata interface.');\n }\n return contract.tokenURI(tokenId);\n };\n\n /**\n * Query for name for a given asset.\n *\n * @param address - ERC721 asset contract address.\n * @returns Promise resolving to the 'name'.\n */\n getAssetName = async (address: string): Promise<string> => {\n const contract = new Contract(address, abiERC721, this.provider);\n return contract.name();\n };\n\n /**\n * Query for symbol for a given asset.\n *\n * @param address - ERC721 asset contract address.\n * @returns Promise resolving to the 'symbol'.\n */\n getAssetSymbol = async (address: string): Promise<string> => {\n const contract = new Contract(address, abiERC721, this.provider);\n return contract.symbol();\n };\n\n /**\n * Query for owner for a given ERC721 asset.\n *\n * @param address - ERC721 asset contract address.\n * @param tokenId - ERC721 asset identifier.\n * @returns Promise resolving to the owner address.\n */\n async getOwnerOf(address: string, tokenId: string): Promise<string> {\n const contract = new Contract(address, abiERC721, this.provider);\n return contract.ownerOf(tokenId);\n }\n\n /**\n * Query if a contract implements an interface.\n *\n * @param address - Asset contract address.\n * @param interfaceId - Interface identifier.\n * @returns Promise resolving to whether the contract implements `interfaceID`.\n */\n private contractSupportsInterface = async (\n address: string,\n interfaceId: string,\n ): Promise<boolean> => {\n const contract = new Contract(address, abiERC721, this.provider);\n try {\n return await contract.supportsInterface(interfaceId);\n } catch (err: any) {\n // Mirror previous implementation\n if (err.message.includes('call revert exception')) {\n return false;\n }\n throw err;\n }\n };\n\n /**\n * Query if a contract implements an interface.\n *\n * @param address - Asset contract address.\n * @param ipfsGateway - The user's preferred IPFS gateway.\n * @param tokenId - tokenId of a given token in the contract.\n * @returns Promise resolving an object containing the standard, tokenURI, symbol and name of the given contract/tokenId pair.\n */\n getDetails = async (\n address: string,\n ipfsGateway: string,\n tokenId?: string,\n ): Promise<{\n standard: string;\n tokenURI: string | undefined;\n symbol: string | undefined;\n name: string | undefined;\n image: string | undefined;\n }> => {\n const isERC721 = await this.contractSupportsBase721Interface(address);\n if (!isERC721) {\n throw new Error(\"This isn't a valid ERC721 contract\");\n }\n\n let tokenURI, image, symbol, name;\n\n // TODO upgrade to use Promise.allSettled for name/symbol when we can refactor to use es2020 in tsconfig\n try {\n symbol = await this.getAssetSymbol(address);\n } catch {\n // ignore\n }\n\n try {\n name = await this.getAssetName(address);\n } catch {\n // ignore\n }\n\n if (tokenId) {\n try {\n tokenURI = await this.getTokenURI(address, tokenId);\n if (tokenURI.startsWith('ipfs://')) {\n tokenURI = getFormattedIpfsUrl(ipfsGateway, tokenURI, true);\n }\n\n const response = await timeoutFetch(tokenURI);\n const object = await response.json();\n image = object?.image;\n if (image?.startsWith('ipfs://')) {\n image = getFormattedIpfsUrl(ipfsGateway, image, true);\n }\n } catch {\n // ignore\n }\n }\n\n return {\n standard: ERC721,\n tokenURI,\n symbol,\n name,\n image,\n };\n };\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { abiERC20, abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis';
|
|
2
|
+
declare type Contract = {
|
|
3
|
+
at(address: string): any;
|
|
4
|
+
};
|
|
5
|
+
export declare type Web3 = {
|
|
6
|
+
eth: {
|
|
7
|
+
call(payload: {
|
|
8
|
+
to: string;
|
|
9
|
+
data: string;
|
|
10
|
+
}, block: undefined, callback: (error: Error, result: string) => void): void;
|
|
11
|
+
contract(abi: typeof abiERC20 | typeof abiERC721 | typeof abiERC1155): Contract;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standards-types.js","sourceRoot":"","sources":["../../src/Standards/standards-types.ts"],"names":[],"mappings":"","sourcesContent":["import { abiERC20, abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis';\n\ntype Contract = {\n at(address: string): any;\n};\n\nexport type Web3 = {\n eth: {\n call(\n payload: { to: string; data: string },\n block: undefined,\n callback: (error: Error, result: string) => void,\n ): void;\n contract(\n abi: typeof abiERC20 | typeof abiERC721 | typeof abiERC1155,\n ): Contract;\n };\n};\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/// <reference types="bn.js" />
|
|
2
|
+
import { BN } from 'ethereumjs-util';
|
|
3
|
+
import { BaseController, BaseConfig, BaseState } from '@metamask/base-controller';
|
|
4
|
+
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
5
|
+
import { Token } from './TokenRatesController';
|
|
6
|
+
import { TokensState } from './TokensController';
|
|
7
|
+
import type { AssetsContractController } from './AssetsContractController';
|
|
8
|
+
export { BN };
|
|
9
|
+
/**
|
|
10
|
+
* @type TokenBalancesConfig
|
|
11
|
+
*
|
|
12
|
+
* Token balances controller configuration
|
|
13
|
+
* @property interval - Polling interval used to fetch new token balances
|
|
14
|
+
* @property tokens - List of tokens to track balances for
|
|
15
|
+
*/
|
|
16
|
+
export interface TokenBalancesConfig extends BaseConfig {
|
|
17
|
+
interval: number;
|
|
18
|
+
tokens: Token[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @type TokenBalancesState
|
|
22
|
+
*
|
|
23
|
+
* Token balances controller state
|
|
24
|
+
* @property contractBalances - Hash of token contract addresses to balances
|
|
25
|
+
*/
|
|
26
|
+
export interface TokenBalancesState extends BaseState {
|
|
27
|
+
contractBalances: {
|
|
28
|
+
[address: string]: BN;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Controller that passively polls on a set interval token balances
|
|
33
|
+
* for tokens stored in the TokensController
|
|
34
|
+
*/
|
|
35
|
+
export declare class TokenBalancesController extends BaseController<TokenBalancesConfig, TokenBalancesState> {
|
|
36
|
+
private handle?;
|
|
37
|
+
/**
|
|
38
|
+
* Name of this controller used during composition
|
|
39
|
+
*/
|
|
40
|
+
name: string;
|
|
41
|
+
private getSelectedAddress;
|
|
42
|
+
private getERC20BalanceOf;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a TokenBalancesController instance.
|
|
45
|
+
*
|
|
46
|
+
* @param options - The controller options.
|
|
47
|
+
* @param options.onTokensStateChange - Allows subscribing to assets controller state changes.
|
|
48
|
+
* @param options.getSelectedAddress - Gets the current selected address.
|
|
49
|
+
* @param options.getERC20BalanceOf - Gets the balance of the given account at the given contract address.
|
|
50
|
+
* @param config - Initial options used to configure this controller.
|
|
51
|
+
* @param state - Initial state to set on this controller.
|
|
52
|
+
*/
|
|
53
|
+
constructor({ onTokensStateChange, getSelectedAddress, getERC20BalanceOf, }: {
|
|
54
|
+
onTokensStateChange: (listener: (tokenState: TokensState) => void) => void;
|
|
55
|
+
getSelectedAddress: () => PreferencesState['selectedAddress'];
|
|
56
|
+
getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];
|
|
57
|
+
}, config?: Partial<TokenBalancesConfig>, state?: Partial<TokenBalancesState>);
|
|
58
|
+
/**
|
|
59
|
+
* Starts a new polling interval.
|
|
60
|
+
*
|
|
61
|
+
* @param interval - Polling interval used to fetch new token balances.
|
|
62
|
+
*/
|
|
63
|
+
poll(interval?: number): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Updates balances for all tokens.
|
|
66
|
+
*/
|
|
67
|
+
updateBalances(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
export default TokenBalancesController;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TokenBalancesController = exports.BN = void 0;
|
|
13
|
+
const ethereumjs_util_1 = require("ethereumjs-util");
|
|
14
|
+
Object.defineProperty(exports, "BN", { enumerable: true, get: function () { return ethereumjs_util_1.BN; } });
|
|
15
|
+
const base_controller_1 = require("@metamask/base-controller");
|
|
16
|
+
const controller_utils_1 = require("@metamask/controller-utils");
|
|
17
|
+
/**
|
|
18
|
+
* Controller that passively polls on a set interval token balances
|
|
19
|
+
* for tokens stored in the TokensController
|
|
20
|
+
*/
|
|
21
|
+
class TokenBalancesController extends base_controller_1.BaseController {
|
|
22
|
+
/**
|
|
23
|
+
* Creates a TokenBalancesController instance.
|
|
24
|
+
*
|
|
25
|
+
* @param options - The controller options.
|
|
26
|
+
* @param options.onTokensStateChange - Allows subscribing to assets controller state changes.
|
|
27
|
+
* @param options.getSelectedAddress - Gets the current selected address.
|
|
28
|
+
* @param options.getERC20BalanceOf - Gets the balance of the given account at the given contract address.
|
|
29
|
+
* @param config - Initial options used to configure this controller.
|
|
30
|
+
* @param state - Initial state to set on this controller.
|
|
31
|
+
*/
|
|
32
|
+
constructor({ onTokensStateChange, getSelectedAddress, getERC20BalanceOf, }, config, state) {
|
|
33
|
+
super(config, state);
|
|
34
|
+
/**
|
|
35
|
+
* Name of this controller used during composition
|
|
36
|
+
*/
|
|
37
|
+
this.name = 'TokenBalancesController';
|
|
38
|
+
this.defaultConfig = {
|
|
39
|
+
interval: 180000,
|
|
40
|
+
tokens: [],
|
|
41
|
+
};
|
|
42
|
+
this.defaultState = { contractBalances: {} };
|
|
43
|
+
this.initialize();
|
|
44
|
+
onTokensStateChange(({ tokens, detectedTokens }) => {
|
|
45
|
+
this.configure({ tokens: [...tokens, ...detectedTokens] });
|
|
46
|
+
this.updateBalances();
|
|
47
|
+
});
|
|
48
|
+
this.getSelectedAddress = getSelectedAddress;
|
|
49
|
+
this.getERC20BalanceOf = getERC20BalanceOf;
|
|
50
|
+
this.poll();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Starts a new polling interval.
|
|
54
|
+
*
|
|
55
|
+
* @param interval - Polling interval used to fetch new token balances.
|
|
56
|
+
*/
|
|
57
|
+
poll(interval) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
interval && this.configure({ interval }, false, false);
|
|
60
|
+
this.handle && clearTimeout(this.handle);
|
|
61
|
+
yield (0, controller_utils_1.safelyExecute)(() => this.updateBalances());
|
|
62
|
+
this.handle = setTimeout(() => {
|
|
63
|
+
this.poll(this.config.interval);
|
|
64
|
+
}, this.config.interval);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Updates balances for all tokens.
|
|
69
|
+
*/
|
|
70
|
+
updateBalances() {
|
|
71
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
if (this.disabled) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const { tokens } = this.config;
|
|
76
|
+
const newContractBalances = {};
|
|
77
|
+
for (const i in tokens) {
|
|
78
|
+
const { address } = tokens[i];
|
|
79
|
+
try {
|
|
80
|
+
newContractBalances[address] = yield this.getERC20BalanceOf(address, this.getSelectedAddress());
|
|
81
|
+
tokens[i].balanceError = null;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
newContractBalances[address] = new ethereumjs_util_1.BN(0);
|
|
85
|
+
tokens[i].balanceError = error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this.update({ contractBalances: newContractBalances });
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.TokenBalancesController = TokenBalancesController;
|
|
93
|
+
exports.default = TokenBalancesController;
|
|
94
|
+
//# sourceMappingURL=TokenBalancesController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.js","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAqC;AAa5B,mFAbA,oBAAE,OAaA;AAZX,+DAImC;AACnC,iEAA2D;AA+B3D;;;GAGG;AACH,MAAa,uBAAwB,SAAQ,gCAG5C;IAYC;;;;;;;;;OASG;IACH,YACE,EACE,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAOlB,EACD,MAAqC,EACrC,KAAmC;QAEnC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAlCvB;;WAEG;QACM,SAAI,GAAG,yBAAyB,CAAC;QAgCxC,IAAI,CAAC,aAAa,GAAG;YACnB,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,EAAE;SACX,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE;YACjD,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;;;OAIG;IACG,IAAI,CAAC,QAAiB;;YAC1B,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACvD,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzC,MAAM,IAAA,gCAAa,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;KAAA;IAED;;OAEG;IACG,cAAc;;YAClB,IAAI,IAAI,CAAC,QAAQ,EAAE;gBACjB,OAAO;aACR;YACD,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,mBAAmB,GAA8B,EAAE,CAAC;YAC1D,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE;gBACtB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI;oBACF,mBAAmB,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,iBAAiB,CACzD,OAAO,EACP,IAAI,CAAC,kBAAkB,EAAE,CAC1B,CAAC;oBACF,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC;iBAC/B;gBAAC,OAAO,KAAK,EAAE;oBACd,mBAAmB,CAAC,OAAO,CAAC,GAAG,IAAI,oBAAE,CAAC,CAAC,CAAC,CAAC;oBACzC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC;iBAChC;aACF;YACD,IAAI,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC,CAAC;QACzD,CAAC;KAAA;CACF;AA9FD,0DA8FC;AAED,kBAAe,uBAAuB,CAAC","sourcesContent":["import { BN } from 'ethereumjs-util';\nimport {\n BaseController,\n BaseConfig,\n BaseState,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport { Token } from './TokenRatesController';\nimport { TokensState } from './TokensController';\nimport type { AssetsContractController } from './AssetsContractController';\n\n// TODO: Remove this export in the next major release\nexport { BN };\n\n/**\n * @type TokenBalancesConfig\n *\n * Token balances controller configuration\n * @property interval - Polling interval used to fetch new token balances\n * @property tokens - List of tokens to track balances for\n */\nexport interface TokenBalancesConfig extends BaseConfig {\n interval: number;\n tokens: Token[];\n}\n\n/**\n * @type TokenBalancesState\n *\n * Token balances controller state\n * @property contractBalances - Hash of token contract addresses to balances\n */\nexport interface TokenBalancesState extends BaseState {\n contractBalances: { [address: string]: BN };\n}\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends BaseController<\n TokenBalancesConfig,\n TokenBalancesState\n> {\n private handle?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenBalancesController';\n\n private getSelectedAddress: () => PreferencesState['selectedAddress'];\n\n private getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];\n\n /**\n * Creates a TokenBalancesController instance.\n *\n * @param options - The controller options.\n * @param options.onTokensStateChange - Allows subscribing to assets controller state changes.\n * @param options.getSelectedAddress - Gets the current selected address.\n * @param options.getERC20BalanceOf - Gets the balance of the given account at the given contract address.\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 onTokensStateChange,\n getSelectedAddress,\n getERC20BalanceOf,\n }: {\n onTokensStateChange: (\n listener: (tokenState: TokensState) => void,\n ) => void;\n getSelectedAddress: () => PreferencesState['selectedAddress'];\n getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];\n },\n config?: Partial<TokenBalancesConfig>,\n state?: Partial<TokenBalancesState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n interval: 180000,\n tokens: [],\n };\n this.defaultState = { contractBalances: {} };\n this.initialize();\n onTokensStateChange(({ tokens, detectedTokens }) => {\n this.configure({ tokens: [...tokens, ...detectedTokens] });\n this.updateBalances();\n });\n this.getSelectedAddress = getSelectedAddress;\n this.getERC20BalanceOf = getERC20BalanceOf;\n this.poll();\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token balances.\n */\n async poll(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.handle && clearTimeout(this.handle);\n await safelyExecute(() => this.updateBalances());\n this.handle = setTimeout(() => {\n this.poll(this.config.interval);\n }, this.config.interval);\n }\n\n /**\n * Updates balances for all tokens.\n */\n async updateBalances() {\n if (this.disabled) {\n return;\n }\n const { tokens } = this.config;\n const newContractBalances: { [address: string]: BN } = {};\n for (const i in tokens) {\n const { address } = tokens[i];\n try {\n newContractBalances[address] = await this.getERC20BalanceOf(\n address,\n this.getSelectedAddress(),\n );\n tokens[i].balanceError = null;\n } catch (error) {\n newContractBalances[address] = new BN(0);\n tokens[i].balanceError = error;\n }\n }\n this.update({ contractBalances: newContractBalances });\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { BaseController, BaseConfig, BaseState } from '@metamask/base-controller';
|
|
2
|
+
import type { NetworkState } from '@metamask/network-controller';
|
|
3
|
+
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
4
|
+
import type { TokensController, TokensState } from './TokensController';
|
|
5
|
+
import type { AssetsContractController } from './AssetsContractController';
|
|
6
|
+
import { TokenListState } from './TokenListController';
|
|
7
|
+
/**
|
|
8
|
+
* @type TokenDetectionConfig
|
|
9
|
+
*
|
|
10
|
+
* TokenDetection configuration
|
|
11
|
+
* @property interval - Polling interval used to fetch new token rates
|
|
12
|
+
* @property selectedAddress - Vault selected address
|
|
13
|
+
* @property chainId - The chain ID of the current network
|
|
14
|
+
* @property isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController
|
|
15
|
+
* @property isDetectionEnabledForNetwork - Boolean to track if detected is enabled for current network
|
|
16
|
+
*/
|
|
17
|
+
export interface TokenDetectionConfig extends BaseConfig {
|
|
18
|
+
interval: number;
|
|
19
|
+
selectedAddress: string;
|
|
20
|
+
chainId: string;
|
|
21
|
+
isDetectionEnabledFromPreferences: boolean;
|
|
22
|
+
isDetectionEnabledForNetwork: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Controller that passively polls on a set interval for Tokens auto detection
|
|
26
|
+
*/
|
|
27
|
+
export declare class TokenDetectionController extends BaseController<TokenDetectionConfig, BaseState> {
|
|
28
|
+
private intervalId?;
|
|
29
|
+
/**
|
|
30
|
+
* Name of this controller used during composition
|
|
31
|
+
*/
|
|
32
|
+
name: string;
|
|
33
|
+
private getBalancesInSingleCall;
|
|
34
|
+
private addDetectedTokens;
|
|
35
|
+
private getTokensState;
|
|
36
|
+
private getTokenListState;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a TokenDetectionController instance.
|
|
39
|
+
*
|
|
40
|
+
* @param options - The controller options.
|
|
41
|
+
* @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.
|
|
42
|
+
* @param options.onNetworkStateChange - Allows subscribing to network controller state changes.
|
|
43
|
+
* @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.
|
|
44
|
+
* @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.
|
|
45
|
+
* @param options.addDetectedTokens - Add a list of detected tokens.
|
|
46
|
+
* @param options.getTokenListState - Gets the current state of the TokenList controller.
|
|
47
|
+
* @param options.getTokensState - Gets the current state of the Tokens controller.
|
|
48
|
+
* @param options.getNetworkState - Gets the state of the network controller.
|
|
49
|
+
* @param options.getPreferencesState - Gets the state of the preferences controller.
|
|
50
|
+
* @param config - Initial options used to configure this controller.
|
|
51
|
+
* @param state - Initial state to set on this controller.
|
|
52
|
+
*/
|
|
53
|
+
constructor({ onPreferencesStateChange, onNetworkStateChange, onTokenListStateChange, getBalancesInSingleCall, addDetectedTokens, getTokenListState, getTokensState, getNetworkState, getPreferencesState, }: {
|
|
54
|
+
onPreferencesStateChange: (listener: (preferencesState: PreferencesState) => void) => void;
|
|
55
|
+
onNetworkStateChange: (listener: (networkState: NetworkState) => void) => void;
|
|
56
|
+
onTokenListStateChange: (listener: (tokenListState: TokenListState) => void) => void;
|
|
57
|
+
getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];
|
|
58
|
+
addDetectedTokens: TokensController['addDetectedTokens'];
|
|
59
|
+
getTokenListState: () => TokenListState;
|
|
60
|
+
getTokensState: () => TokensState;
|
|
61
|
+
getNetworkState: () => NetworkState;
|
|
62
|
+
getPreferencesState: () => PreferencesState;
|
|
63
|
+
}, config?: Partial<TokenDetectionConfig>, state?: Partial<BaseState>);
|
|
64
|
+
/**
|
|
65
|
+
* Start polling for detected tokens.
|
|
66
|
+
*/
|
|
67
|
+
start(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Stop polling for detected tokens.
|
|
70
|
+
*/
|
|
71
|
+
stop(): void;
|
|
72
|
+
private stopPolling;
|
|
73
|
+
/**
|
|
74
|
+
* Starts a new polling interval.
|
|
75
|
+
*
|
|
76
|
+
* @param interval - An interval on which to poll.
|
|
77
|
+
*/
|
|
78
|
+
private startPolling;
|
|
79
|
+
/**
|
|
80
|
+
* Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.
|
|
81
|
+
*/
|
|
82
|
+
detectTokens(): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
export default TokenDetectionController;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TokenDetectionController = void 0;
|
|
13
|
+
const base_controller_1 = require("@metamask/base-controller");
|
|
14
|
+
const controller_utils_1 = require("@metamask/controller-utils");
|
|
15
|
+
const assetsUtil_1 = require("./assetsUtil");
|
|
16
|
+
const DEFAULT_INTERVAL = 180000;
|
|
17
|
+
/**
|
|
18
|
+
* Controller that passively polls on a set interval for Tokens auto detection
|
|
19
|
+
*/
|
|
20
|
+
class TokenDetectionController extends base_controller_1.BaseController {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a TokenDetectionController instance.
|
|
23
|
+
*
|
|
24
|
+
* @param options - The controller options.
|
|
25
|
+
* @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.
|
|
26
|
+
* @param options.onNetworkStateChange - Allows subscribing to network controller state changes.
|
|
27
|
+
* @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.
|
|
28
|
+
* @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.
|
|
29
|
+
* @param options.addDetectedTokens - Add a list of detected tokens.
|
|
30
|
+
* @param options.getTokenListState - Gets the current state of the TokenList controller.
|
|
31
|
+
* @param options.getTokensState - Gets the current state of the Tokens controller.
|
|
32
|
+
* @param options.getNetworkState - Gets the state of the network controller.
|
|
33
|
+
* @param options.getPreferencesState - Gets the state of the preferences controller.
|
|
34
|
+
* @param config - Initial options used to configure this controller.
|
|
35
|
+
* @param state - Initial state to set on this controller.
|
|
36
|
+
*/
|
|
37
|
+
constructor({ onPreferencesStateChange, onNetworkStateChange, onTokenListStateChange, getBalancesInSingleCall, addDetectedTokens, getTokenListState, getTokensState, getNetworkState, getPreferencesState, }, config, state) {
|
|
38
|
+
const { provider: { chainId: defaultChainId }, } = getNetworkState();
|
|
39
|
+
const { useTokenDetection: defaultUseTokenDetection } = getPreferencesState();
|
|
40
|
+
super(config, state);
|
|
41
|
+
/**
|
|
42
|
+
* Name of this controller used during composition
|
|
43
|
+
*/
|
|
44
|
+
this.name = 'TokenDetectionController';
|
|
45
|
+
this.defaultConfig = Object.assign({ interval: DEFAULT_INTERVAL, selectedAddress: '', disabled: true, chainId: defaultChainId, isDetectionEnabledFromPreferences: defaultUseTokenDetection, isDetectionEnabledForNetwork: (0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(defaultChainId) }, config);
|
|
46
|
+
this.initialize();
|
|
47
|
+
this.getTokensState = getTokensState;
|
|
48
|
+
this.getTokenListState = getTokenListState;
|
|
49
|
+
this.addDetectedTokens = addDetectedTokens;
|
|
50
|
+
this.getBalancesInSingleCall = getBalancesInSingleCall;
|
|
51
|
+
onTokenListStateChange(({ tokenList }) => {
|
|
52
|
+
const hasTokens = Object.keys(tokenList).length;
|
|
53
|
+
if (hasTokens) {
|
|
54
|
+
this.detectTokens();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
onPreferencesStateChange(({ selectedAddress, useTokenDetection }) => {
|
|
58
|
+
const { selectedAddress: currentSelectedAddress, isDetectionEnabledFromPreferences, } = this.config;
|
|
59
|
+
const isSelectedAddressChanged = selectedAddress !== currentSelectedAddress;
|
|
60
|
+
const isDetectionChangedFromPreferences = isDetectionEnabledFromPreferences !== useTokenDetection;
|
|
61
|
+
this.configure({
|
|
62
|
+
isDetectionEnabledFromPreferences: useTokenDetection,
|
|
63
|
+
selectedAddress,
|
|
64
|
+
});
|
|
65
|
+
if (useTokenDetection &&
|
|
66
|
+
(isSelectedAddressChanged || isDetectionChangedFromPreferences)) {
|
|
67
|
+
this.detectTokens();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
onNetworkStateChange(({ provider: { chainId } }) => {
|
|
71
|
+
const { chainId: currentChainId } = this.config;
|
|
72
|
+
const isDetectionEnabledForNetwork = (0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(chainId);
|
|
73
|
+
const isChainIdChanged = currentChainId !== chainId;
|
|
74
|
+
this.configure({
|
|
75
|
+
chainId,
|
|
76
|
+
isDetectionEnabledForNetwork,
|
|
77
|
+
});
|
|
78
|
+
if (isDetectionEnabledForNetwork && isChainIdChanged) {
|
|
79
|
+
this.detectTokens();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Start polling for detected tokens.
|
|
85
|
+
*/
|
|
86
|
+
start() {
|
|
87
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
this.configure({ disabled: false });
|
|
89
|
+
yield this.startPolling();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Stop polling for detected tokens.
|
|
94
|
+
*/
|
|
95
|
+
stop() {
|
|
96
|
+
this.configure({ disabled: true });
|
|
97
|
+
this.stopPolling();
|
|
98
|
+
}
|
|
99
|
+
stopPolling() {
|
|
100
|
+
if (this.intervalId) {
|
|
101
|
+
clearInterval(this.intervalId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Starts a new polling interval.
|
|
106
|
+
*
|
|
107
|
+
* @param interval - An interval on which to poll.
|
|
108
|
+
*/
|
|
109
|
+
startPolling(interval) {
|
|
110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
interval && this.configure({ interval }, false, false);
|
|
112
|
+
this.stopPolling();
|
|
113
|
+
yield this.detectTokens();
|
|
114
|
+
this.intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
yield this.detectTokens();
|
|
116
|
+
}), this.config.interval);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.
|
|
121
|
+
*/
|
|
122
|
+
detectTokens() {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
const { disabled, isDetectionEnabledForNetwork, isDetectionEnabledFromPreferences, } = this.config;
|
|
125
|
+
if (disabled ||
|
|
126
|
+
!isDetectionEnabledForNetwork ||
|
|
127
|
+
!isDetectionEnabledFromPreferences) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const { tokens } = this.getTokensState();
|
|
131
|
+
const { selectedAddress } = this.config;
|
|
132
|
+
const tokensAddresses = tokens.map(
|
|
133
|
+
/* istanbul ignore next*/ (token) => token.address.toLowerCase());
|
|
134
|
+
const { tokenList } = this.getTokenListState();
|
|
135
|
+
const tokensToDetect = [];
|
|
136
|
+
for (const address in tokenList) {
|
|
137
|
+
if (!tokensAddresses.includes(address)) {
|
|
138
|
+
tokensToDetect.push(address);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const sliceOfTokensToDetect = [];
|
|
142
|
+
sliceOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);
|
|
143
|
+
sliceOfTokensToDetect[1] = tokensToDetect.slice(1000, tokensToDetect.length - 1);
|
|
144
|
+
/* istanbul ignore else */
|
|
145
|
+
if (!selectedAddress) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
for (const tokensSlice of sliceOfTokensToDetect) {
|
|
149
|
+
if (tokensSlice.length === 0) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
yield (0, controller_utils_1.safelyExecute)(() => __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
const balances = yield this.getBalancesInSingleCall(selectedAddress, tokensSlice);
|
|
154
|
+
const tokensToAdd = [];
|
|
155
|
+
for (const tokenAddress in balances) {
|
|
156
|
+
let ignored;
|
|
157
|
+
/* istanbul ignore else */
|
|
158
|
+
const { ignoredTokens } = this.getTokensState();
|
|
159
|
+
if (ignoredTokens.length) {
|
|
160
|
+
ignored = ignoredTokens.find((ignoredTokenAddress) => ignoredTokenAddress === (0, controller_utils_1.toChecksumHexAddress)(tokenAddress));
|
|
161
|
+
}
|
|
162
|
+
const caseInsensitiveTokenKey = Object.keys(tokenList).find((i) => i.toLowerCase() === tokenAddress.toLowerCase()) || '';
|
|
163
|
+
if (ignored === undefined) {
|
|
164
|
+
const { decimals, symbol, aggregators, iconUrl } = tokenList[caseInsensitiveTokenKey];
|
|
165
|
+
tokensToAdd.push({
|
|
166
|
+
address: tokenAddress,
|
|
167
|
+
decimals,
|
|
168
|
+
symbol,
|
|
169
|
+
aggregators,
|
|
170
|
+
image: iconUrl,
|
|
171
|
+
isERC721: false,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (tokensToAdd.length) {
|
|
176
|
+
yield this.addDetectedTokens(tokensToAdd);
|
|
177
|
+
}
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.TokenDetectionController = TokenDetectionController;
|
|
184
|
+
exports.default = TokenDetectionController;
|
|
185
|
+
//# sourceMappingURL=TokenDetectionController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenDetectionController.js","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseController,\n BaseConfig,\n BaseState,\n} from '@metamask/base-controller';\nimport type { NetworkState } from '@metamask/network-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport {\n safelyExecute,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport { isTokenDetectionSupportedForNetwork } from './assetsUtil';\nimport type { TokensController, TokensState } from './TokensController';\nimport type { AssetsContractController } from './AssetsContractController';\nimport { Token } from './TokenRatesController';\nimport { TokenListState } from './TokenListController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type TokenDetectionConfig\n *\n * TokenDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property selectedAddress - Vault selected address\n * @property chainId - The chain ID of the current network\n * @property isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController\n * @property isDetectionEnabledForNetwork - Boolean to track if detected is enabled for current network\n */\nexport interface TokenDetectionConfig extends BaseConfig {\n interval: number;\n selectedAddress: string;\n chainId: string;\n isDetectionEnabledFromPreferences: boolean;\n isDetectionEnabledForNetwork: boolean;\n}\n\n/**\n * Controller that passively polls on a set interval for Tokens auto detection\n */\nexport class TokenDetectionController extends BaseController<\n TokenDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenDetectionController';\n\n private getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n\n private addDetectedTokens: TokensController['addDetectedTokens'];\n\n private getTokensState: () => TokensState;\n\n private getTokenListState: () => TokenListState;\n\n /**\n * Creates a TokenDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.\n * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.\n * @param options.addDetectedTokens - Add a list of detected tokens.\n * @param options.getTokenListState - Gets the current state of the TokenList controller.\n * @param options.getTokensState - Gets the current state of the Tokens controller.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPreferencesState - Gets the state of the preferences controller.\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 onNetworkStateChange,\n onTokenListStateChange,\n getBalancesInSingleCall,\n addDetectedTokens,\n getTokenListState,\n getTokensState,\n getNetworkState,\n getPreferencesState,\n }: {\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n onTokenListStateChange: (\n listener: (tokenListState: TokenListState) => void,\n ) => void;\n getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n addDetectedTokens: TokensController['addDetectedTokens'];\n getTokenListState: () => TokenListState;\n getTokensState: () => TokensState;\n getNetworkState: () => NetworkState;\n getPreferencesState: () => PreferencesState;\n },\n config?: Partial<TokenDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n const {\n provider: { chainId: defaultChainId },\n } = getNetworkState();\n const { useTokenDetection: defaultUseTokenDetection } =\n getPreferencesState();\n\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n selectedAddress: '',\n disabled: true,\n chainId: defaultChainId,\n isDetectionEnabledFromPreferences: defaultUseTokenDetection,\n isDetectionEnabledForNetwork:\n isTokenDetectionSupportedForNetwork(defaultChainId),\n ...config,\n };\n\n this.initialize();\n this.getTokensState = getTokensState;\n this.getTokenListState = getTokenListState;\n this.addDetectedTokens = addDetectedTokens;\n this.getBalancesInSingleCall = getBalancesInSingleCall;\n\n onTokenListStateChange(({ tokenList }) => {\n const hasTokens = Object.keys(tokenList).length;\n\n if (hasTokens) {\n this.detectTokens();\n }\n });\n\n onPreferencesStateChange(({ selectedAddress, useTokenDetection }) => {\n const {\n selectedAddress: currentSelectedAddress,\n isDetectionEnabledFromPreferences,\n } = this.config;\n const isSelectedAddressChanged =\n selectedAddress !== currentSelectedAddress;\n const isDetectionChangedFromPreferences =\n isDetectionEnabledFromPreferences !== useTokenDetection;\n\n this.configure({\n isDetectionEnabledFromPreferences: useTokenDetection,\n selectedAddress,\n });\n\n if (\n useTokenDetection &&\n (isSelectedAddressChanged || isDetectionChangedFromPreferences)\n ) {\n this.detectTokens();\n }\n });\n\n onNetworkStateChange(({ provider: { chainId } }) => {\n const { chainId: currentChainId } = this.config;\n const isDetectionEnabledForNetwork =\n isTokenDetectionSupportedForNetwork(chainId);\n const isChainIdChanged = currentChainId !== chainId;\n\n this.configure({\n chainId,\n isDetectionEnabledForNetwork,\n });\n\n if (isDetectionEnabledForNetwork && isChainIdChanged) {\n this.detectTokens();\n }\n });\n }\n\n /**\n * Start polling for detected tokens.\n */\n async start() {\n this.configure({ disabled: false });\n await this.startPolling();\n }\n\n /**\n * Stop polling for detected tokens.\n */\n stop() {\n this.configure({ disabled: true });\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectTokens();\n this.intervalId = setInterval(async () => {\n await this.detectTokens();\n }, this.config.interval);\n }\n\n /**\n * Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.\n */\n async detectTokens() {\n const {\n disabled,\n isDetectionEnabledForNetwork,\n isDetectionEnabledFromPreferences,\n } = this.config;\n if (\n disabled ||\n !isDetectionEnabledForNetwork ||\n !isDetectionEnabledFromPreferences\n ) {\n return;\n }\n const { tokens } = this.getTokensState();\n const { selectedAddress } = this.config;\n\n const tokensAddresses = tokens.map(\n /* istanbul ignore next*/ (token) => token.address.toLowerCase(),\n );\n const { tokenList } = this.getTokenListState();\n const tokensToDetect: string[] = [];\n for (const address in tokenList) {\n if (!tokensAddresses.includes(address)) {\n tokensToDetect.push(address);\n }\n }\n const sliceOfTokensToDetect = [];\n sliceOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);\n sliceOfTokensToDetect[1] = tokensToDetect.slice(\n 1000,\n tokensToDetect.length - 1,\n );\n\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n for (const tokensSlice of sliceOfTokensToDetect) {\n if (tokensSlice.length === 0) {\n break;\n }\n\n await safelyExecute(async () => {\n const balances = await this.getBalancesInSingleCall(\n selectedAddress,\n tokensSlice,\n );\n const tokensToAdd: Token[] = [];\n for (const tokenAddress in balances) {\n let ignored;\n /* istanbul ignore else */\n const { ignoredTokens } = this.getTokensState();\n if (ignoredTokens.length) {\n ignored = ignoredTokens.find(\n (ignoredTokenAddress) =>\n ignoredTokenAddress === toChecksumHexAddress(tokenAddress),\n );\n }\n const caseInsensitiveTokenKey =\n Object.keys(tokenList).find(\n (i) => i.toLowerCase() === tokenAddress.toLowerCase(),\n ) || '';\n\n if (ignored === undefined) {\n const { decimals, symbol, aggregators, iconUrl } =\n tokenList[caseInsensitiveTokenKey];\n tokensToAdd.push({\n address: tokenAddress,\n decimals,\n symbol,\n aggregators,\n image: iconUrl,\n isERC721: false,\n });\n }\n }\n\n if (tokensToAdd.length) {\n await this.addDetectedTokens(tokensToAdd);\n }\n });\n }\n }\n}\n\nexport default TokenDetectionController;\n"]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Patch } from 'immer';
|
|
2
|
+
import { BaseControllerV2, RestrictedControllerMessenger } from '@metamask/base-controller';
|
|
3
|
+
import { NetworkControllerProviderChangeEvent, NetworkState, ProviderConfig } from '@metamask/network-controller';
|
|
4
|
+
declare const name = "TokenListController";
|
|
5
|
+
export declare type TokenListToken = {
|
|
6
|
+
name: string;
|
|
7
|
+
symbol: string;
|
|
8
|
+
decimals: number;
|
|
9
|
+
address: string;
|
|
10
|
+
occurrences: number;
|
|
11
|
+
aggregators: string[];
|
|
12
|
+
iconUrl: string;
|
|
13
|
+
};
|
|
14
|
+
export declare type TokenListMap = Record<string, TokenListToken>;
|
|
15
|
+
declare type DataCache = {
|
|
16
|
+
timestamp: number;
|
|
17
|
+
data: TokenListMap;
|
|
18
|
+
};
|
|
19
|
+
declare type TokensChainsCache = {
|
|
20
|
+
[chainSlug: string]: DataCache;
|
|
21
|
+
};
|
|
22
|
+
export declare type TokenListState = {
|
|
23
|
+
tokenList: TokenListMap;
|
|
24
|
+
tokensChainsCache: TokensChainsCache;
|
|
25
|
+
preventPollingOnNetworkRestart: boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare type TokenListStateChange = {
|
|
28
|
+
type: `${typeof name}:stateChange`;
|
|
29
|
+
payload: [TokenListState, Patch[]];
|
|
30
|
+
};
|
|
31
|
+
export declare type GetTokenListState = {
|
|
32
|
+
type: `${typeof name}:getState`;
|
|
33
|
+
handler: () => TokenListState;
|
|
34
|
+
};
|
|
35
|
+
declare type TokenListMessenger = RestrictedControllerMessenger<typeof name, GetTokenListState, TokenListStateChange | NetworkControllerProviderChangeEvent, never, TokenListStateChange['type'] | NetworkControllerProviderChangeEvent['type']>;
|
|
36
|
+
/**
|
|
37
|
+
* Controller that passively polls on a set interval for the list of tokens from metaswaps api
|
|
38
|
+
*/
|
|
39
|
+
export declare class TokenListController extends BaseControllerV2<typeof name, TokenListState, TokenListMessenger> {
|
|
40
|
+
#private;
|
|
41
|
+
private mutex;
|
|
42
|
+
private intervalId?;
|
|
43
|
+
private intervalDelay;
|
|
44
|
+
private cacheRefreshThreshold;
|
|
45
|
+
private chainId;
|
|
46
|
+
private abortController;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a TokenListController instance.
|
|
49
|
+
*
|
|
50
|
+
* @param options - The controller options.
|
|
51
|
+
* @param options.chainId - The chain ID of the current network.
|
|
52
|
+
* @param options.onNetworkStateChange - A function for registering an event handler for network state changes.
|
|
53
|
+
* @param options.interval - The polling interval, in milliseconds.
|
|
54
|
+
* @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.
|
|
55
|
+
* @param options.messenger - A restricted controller messenger.
|
|
56
|
+
* @param options.state - Initial state to set on this controller.
|
|
57
|
+
* @param options.preventPollingOnNetworkRestart - Determines whether to prevent poilling on network restart in extension.
|
|
58
|
+
*/
|
|
59
|
+
constructor({ chainId, preventPollingOnNetworkRestart, onNetworkStateChange, interval, cacheRefreshThreshold, messenger, state, }: {
|
|
60
|
+
chainId: string;
|
|
61
|
+
preventPollingOnNetworkRestart?: boolean;
|
|
62
|
+
onNetworkStateChange?: (listener: (networkState: NetworkState | ProviderConfig) => void) => void;
|
|
63
|
+
interval?: number;
|
|
64
|
+
cacheRefreshThreshold?: number;
|
|
65
|
+
messenger: TokenListMessenger;
|
|
66
|
+
state?: Partial<TokenListState>;
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* Start polling for the token list.
|
|
70
|
+
*/
|
|
71
|
+
start(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Restart polling for the token list.
|
|
74
|
+
*/
|
|
75
|
+
restart(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Stop polling for the token list.
|
|
78
|
+
*/
|
|
79
|
+
stop(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Prepare to discard this controller.
|
|
82
|
+
*
|
|
83
|
+
* This stops any active polling.
|
|
84
|
+
*/
|
|
85
|
+
destroy(): void;
|
|
86
|
+
private stopPolling;
|
|
87
|
+
/**
|
|
88
|
+
* Starts a new polling interval.
|
|
89
|
+
*/
|
|
90
|
+
private startPolling;
|
|
91
|
+
/**
|
|
92
|
+
* Fetching token list from the Token Service API.
|
|
93
|
+
*/
|
|
94
|
+
fetchTokenList(): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Checks if the Cache timestamp is valid,
|
|
97
|
+
* if yes data in cache will be returned
|
|
98
|
+
* otherwise null will be returned.
|
|
99
|
+
*
|
|
100
|
+
* @returns The cached data, or `null` if the cache was expired.
|
|
101
|
+
*/
|
|
102
|
+
fetchFromCache(): Promise<TokenListMap | null>;
|
|
103
|
+
/**
|
|
104
|
+
* Clearing tokenList and tokensChainsCache explicitly.
|
|
105
|
+
*/
|
|
106
|
+
clearingTokenListData(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Updates preventPollingOnNetworkRestart from extension.
|
|
109
|
+
*
|
|
110
|
+
* @param shouldPreventPolling - Determine whether to prevent polling on network change
|
|
111
|
+
*/
|
|
112
|
+
updatePreventPollingOnNetworkRestart(shouldPreventPolling: boolean): void;
|
|
113
|
+
}
|
|
114
|
+
export default TokenListController;
|