@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,284 @@
|
|
|
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.TokenRatesController = void 0;
|
|
13
|
+
const base_controller_1 = require("@metamask/base-controller");
|
|
14
|
+
const controller_utils_1 = require("@metamask/controller-utils");
|
|
15
|
+
const crypto_compare_1 = require("./crypto-compare");
|
|
16
|
+
const CoinGeckoApi = {
|
|
17
|
+
BASE_URL: 'https://api.coingecko.com/api/v3',
|
|
18
|
+
getTokenPriceURL(chainSlug, query) {
|
|
19
|
+
return `${this.BASE_URL}/simple/token_price/${chainSlug}?${query}`;
|
|
20
|
+
},
|
|
21
|
+
getPlatformsURL() {
|
|
22
|
+
return `${this.BASE_URL}/asset_platforms`;
|
|
23
|
+
},
|
|
24
|
+
getSupportedVsCurrencies() {
|
|
25
|
+
return `${this.BASE_URL}/simple/supported_vs_currencies`;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Finds the chain slug in the data array given a chainId.
|
|
30
|
+
*
|
|
31
|
+
* @param chainId - The current chain ID.
|
|
32
|
+
* @param data - A list platforms supported by the CoinGecko API.
|
|
33
|
+
* @returns The CoinGecko slug for the given chain ID, or `null` if the slug was not found.
|
|
34
|
+
*/
|
|
35
|
+
function findChainSlug(chainId, data) {
|
|
36
|
+
var _a;
|
|
37
|
+
if (!data) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const chain = (_a = data.find(({ chain_identifier }) => chain_identifier !== null && String(chain_identifier) === chainId)) !== null && _a !== void 0 ? _a : null;
|
|
41
|
+
return (chain === null || chain === void 0 ? void 0 : chain.id) || null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Controller that passively polls on a set interval for token-to-fiat exchange rates
|
|
45
|
+
* for tokens stored in the TokensController
|
|
46
|
+
*/
|
|
47
|
+
class TokenRatesController extends base_controller_1.BaseController {
|
|
48
|
+
/**
|
|
49
|
+
* Creates a TokenRatesController instance.
|
|
50
|
+
*
|
|
51
|
+
* @param options - The controller options.
|
|
52
|
+
* @param options.onTokensStateChange - Allows subscribing to token controller state changes.
|
|
53
|
+
* @param options.onCurrencyRateStateChange - Allows subscribing to currency rate controller state changes.
|
|
54
|
+
* @param options.onNetworkStateChange - Allows subscribing to network state changes.
|
|
55
|
+
* @param config - Initial options used to configure this controller.
|
|
56
|
+
* @param state - Initial state to set on this controller.
|
|
57
|
+
*/
|
|
58
|
+
constructor({ onTokensStateChange, onCurrencyRateStateChange, onNetworkStateChange, }, config, state) {
|
|
59
|
+
super(config, state);
|
|
60
|
+
this.tokenList = [];
|
|
61
|
+
this.supportedChains = {
|
|
62
|
+
timestamp: 0,
|
|
63
|
+
data: null,
|
|
64
|
+
};
|
|
65
|
+
this.supportedVsCurrencies = {
|
|
66
|
+
timestamp: 0,
|
|
67
|
+
data: [],
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Name of this controller used during composition
|
|
71
|
+
*/
|
|
72
|
+
this.name = 'TokenRatesController';
|
|
73
|
+
this.defaultConfig = {
|
|
74
|
+
disabled: true,
|
|
75
|
+
interval: 3 * 60 * 1000,
|
|
76
|
+
nativeCurrency: 'eth',
|
|
77
|
+
chainId: '',
|
|
78
|
+
tokens: [],
|
|
79
|
+
threshold: 6 * 60 * 60 * 1000,
|
|
80
|
+
};
|
|
81
|
+
this.defaultState = {
|
|
82
|
+
contractExchangeRates: {},
|
|
83
|
+
};
|
|
84
|
+
this.initialize();
|
|
85
|
+
this.configure({ disabled: false }, false, false);
|
|
86
|
+
onTokensStateChange(({ tokens, detectedTokens }) => {
|
|
87
|
+
this.configure({ tokens: [...tokens, ...detectedTokens] });
|
|
88
|
+
});
|
|
89
|
+
onCurrencyRateStateChange((currencyRateState) => {
|
|
90
|
+
this.configure({ nativeCurrency: currencyRateState.nativeCurrency });
|
|
91
|
+
});
|
|
92
|
+
onNetworkStateChange(({ provider }) => {
|
|
93
|
+
const { chainId } = provider;
|
|
94
|
+
this.update({ contractExchangeRates: {} });
|
|
95
|
+
this.configure({ chainId });
|
|
96
|
+
});
|
|
97
|
+
this.poll();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Sets a new polling interval.
|
|
101
|
+
*
|
|
102
|
+
* @param interval - Polling interval used to fetch new token rates.
|
|
103
|
+
*/
|
|
104
|
+
poll(interval) {
|
|
105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
+
interval && this.configure({ interval }, false, false);
|
|
107
|
+
this.handle && clearTimeout(this.handle);
|
|
108
|
+
yield (0, controller_utils_1.safelyExecute)(() => this.updateExchangeRates());
|
|
109
|
+
this.handle = setTimeout(() => {
|
|
110
|
+
this.poll(this.config.interval);
|
|
111
|
+
}, this.config.interval);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Sets a new chainId.
|
|
116
|
+
*
|
|
117
|
+
* TODO: Replace this with a method.
|
|
118
|
+
*
|
|
119
|
+
* @param _chainId - The current chain ID.
|
|
120
|
+
*/
|
|
121
|
+
set chainId(_chainId) {
|
|
122
|
+
!this.disabled && (0, controller_utils_1.safelyExecute)(() => this.updateExchangeRates());
|
|
123
|
+
}
|
|
124
|
+
get chainId() {
|
|
125
|
+
throw new Error('Property only used for setting');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Sets a new token list to track prices.
|
|
129
|
+
*
|
|
130
|
+
* TODO: Replace this with a method.
|
|
131
|
+
*
|
|
132
|
+
* @param tokens - List of tokens to track exchange rates for.
|
|
133
|
+
*/
|
|
134
|
+
set tokens(tokens) {
|
|
135
|
+
this.tokenList = tokens;
|
|
136
|
+
!this.disabled && (0, controller_utils_1.safelyExecute)(() => this.updateExchangeRates());
|
|
137
|
+
}
|
|
138
|
+
get tokens() {
|
|
139
|
+
throw new Error('Property only used for setting');
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Fetches a pairs of token address and native currency.
|
|
143
|
+
*
|
|
144
|
+
* @param chainSlug - Chain string identifier.
|
|
145
|
+
* @param vsCurrency - Query according to tokens in tokenList and native currency.
|
|
146
|
+
* @returns The exchange rates for the given pairs.
|
|
147
|
+
*/
|
|
148
|
+
fetchExchangeRate(chainSlug, vsCurrency) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const tokenPairs = this.tokenList.map((token) => token.address).join(',');
|
|
151
|
+
const query = `contract_addresses=${tokenPairs}&vs_currencies=${vsCurrency.toLowerCase()}`;
|
|
152
|
+
return (0, controller_utils_1.handleFetch)(CoinGeckoApi.getTokenPriceURL(chainSlug, query));
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Checks if the current native currency is a supported vs currency to use
|
|
157
|
+
* to query for token exchange rates.
|
|
158
|
+
*
|
|
159
|
+
* @param nativeCurrency - The native currency of the currently active network.
|
|
160
|
+
* @returns A boolean indicating whether it's a supported vsCurrency.
|
|
161
|
+
*/
|
|
162
|
+
checkIsSupportedVsCurrency(nativeCurrency) {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
const { threshold } = this.config;
|
|
165
|
+
const { timestamp, data } = this.supportedVsCurrencies;
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
if (now - timestamp > threshold) {
|
|
168
|
+
const currencies = yield (0, controller_utils_1.handleFetch)(CoinGeckoApi.getSupportedVsCurrencies());
|
|
169
|
+
this.supportedVsCurrencies = {
|
|
170
|
+
data: currencies,
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
};
|
|
173
|
+
return currencies.includes(nativeCurrency.toLowerCase());
|
|
174
|
+
}
|
|
175
|
+
return data.includes(nativeCurrency.toLowerCase());
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Gets current chain ID slug from cached supported platforms CoinGecko API response.
|
|
180
|
+
* If cached supported platforms response is stale, fetches and updates it.
|
|
181
|
+
*
|
|
182
|
+
* @returns The CoinGecko slug for the current chain ID.
|
|
183
|
+
*/
|
|
184
|
+
getChainSlug() {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
const { threshold, chainId } = this.config;
|
|
187
|
+
const { data, timestamp } = this.supportedChains;
|
|
188
|
+
const now = Date.now();
|
|
189
|
+
if (now - timestamp > threshold) {
|
|
190
|
+
const platforms = yield (0, controller_utils_1.handleFetch)(CoinGeckoApi.getPlatformsURL());
|
|
191
|
+
this.supportedChains = {
|
|
192
|
+
data: platforms,
|
|
193
|
+
timestamp: Date.now(),
|
|
194
|
+
};
|
|
195
|
+
return findChainSlug(chainId, platforms);
|
|
196
|
+
}
|
|
197
|
+
return findChainSlug(chainId, data);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Updates exchange rates for all tokens.
|
|
202
|
+
*/
|
|
203
|
+
updateExchangeRates() {
|
|
204
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
205
|
+
if (this.tokenList.length === 0 || this.disabled) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const slug = yield this.getChainSlug();
|
|
209
|
+
let newContractExchangeRates = {};
|
|
210
|
+
if (!slug) {
|
|
211
|
+
this.tokenList.forEach((token) => {
|
|
212
|
+
const address = (0, controller_utils_1.toChecksumHexAddress)(token.address);
|
|
213
|
+
newContractExchangeRates[address] = undefined;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const { nativeCurrency } = this.config;
|
|
218
|
+
newContractExchangeRates = yield this.fetchAndMapExchangeRates(nativeCurrency, slug);
|
|
219
|
+
}
|
|
220
|
+
this.update({ contractExchangeRates: newContractExchangeRates });
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Checks if the active network's native currency is supported by the coingecko API.
|
|
225
|
+
* If supported, it fetches and maps contractExchange rates to a format to be consumed by the UI.
|
|
226
|
+
* If not supported, it fetches contractExchange rates and maps them from token/fallback-currency
|
|
227
|
+
* to token/nativeCurrency.
|
|
228
|
+
*
|
|
229
|
+
* @param nativeCurrency - The native currency of the currently active network.
|
|
230
|
+
* @param slug - The unique slug used to id the chain by the coingecko api
|
|
231
|
+
* should be used to query token exchange rates.
|
|
232
|
+
* @returns An object with conversion rates for each token
|
|
233
|
+
* related to the network's native currency.
|
|
234
|
+
*/
|
|
235
|
+
fetchAndMapExchangeRates(nativeCurrency, slug) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
const contractExchangeRates = {};
|
|
238
|
+
// check if native currency is supported as a vs_currency by the API
|
|
239
|
+
const nativeCurrencySupported = yield this.checkIsSupportedVsCurrency(nativeCurrency);
|
|
240
|
+
if (nativeCurrencySupported) {
|
|
241
|
+
// If it is we can do a simple fetch against the CoinGecko API
|
|
242
|
+
const prices = yield this.fetchExchangeRate(slug, nativeCurrency);
|
|
243
|
+
this.tokenList.forEach((token) => {
|
|
244
|
+
const price = prices[token.address.toLowerCase()];
|
|
245
|
+
contractExchangeRates[(0, controller_utils_1.toChecksumHexAddress)(token.address)] = price
|
|
246
|
+
? price[nativeCurrency.toLowerCase()]
|
|
247
|
+
: 0;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// if native currency is not supported we need to use a fallback vsCurrency, get the exchange rates
|
|
252
|
+
// in token/fallback-currency format and convert them to expected token/nativeCurrency format.
|
|
253
|
+
let tokenExchangeRates;
|
|
254
|
+
let vsCurrencyToNativeCurrencyConversionRate = 0;
|
|
255
|
+
try {
|
|
256
|
+
[
|
|
257
|
+
tokenExchangeRates,
|
|
258
|
+
{ conversionRate: vsCurrencyToNativeCurrencyConversionRate },
|
|
259
|
+
] = yield Promise.all([
|
|
260
|
+
this.fetchExchangeRate(slug, controller_utils_1.FALL_BACK_VS_CURRENCY),
|
|
261
|
+
(0, crypto_compare_1.fetchExchangeRate)(nativeCurrency, controller_utils_1.FALL_BACK_VS_CURRENCY, false),
|
|
262
|
+
]);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
if (error instanceof Error &&
|
|
266
|
+
error.message.includes('market does not exist for this coin pair')) {
|
|
267
|
+
return {};
|
|
268
|
+
}
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
for (const [tokenAddress, conversion] of Object.entries(tokenExchangeRates)) {
|
|
272
|
+
const tokenToVsCurrencyConversionRate = conversion[controller_utils_1.FALL_BACK_VS_CURRENCY.toLowerCase()];
|
|
273
|
+
contractExchangeRates[(0, controller_utils_1.toChecksumHexAddress)(tokenAddress)] =
|
|
274
|
+
tokenToVsCurrencyConversionRate *
|
|
275
|
+
vsCurrencyToNativeCurrencyConversionRate;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return contractExchangeRates;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
exports.TokenRatesController = TokenRatesController;
|
|
283
|
+
exports.default = TokenRatesController;
|
|
284
|
+
//# sourceMappingURL=TokenRatesController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenRatesController.js","sourceRoot":"","sources":["../src/TokenRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseController,\n BaseConfig,\n BaseState,\n} from '@metamask/base-controller';\nimport {\n safelyExecute,\n handleFetch,\n toChecksumHexAddress,\n FALL_BACK_VS_CURRENCY,\n} from '@metamask/controller-utils';\nimport type { NetworkState } from '@metamask/network-controller';\nimport { fetchExchangeRate as fetchNativeExchangeRate } from './crypto-compare';\nimport type { TokensState } from './TokensController';\nimport type { CurrencyRateState } from './CurrencyRateController';\n\n/**\n * @type CoinGeckoResponse\n *\n * CoinGecko API response representation\n */\nexport interface CoinGeckoResponse {\n [address: string]: {\n [currency: string]: number;\n };\n}\n/**\n * @type CoinGeckoPlatform\n *\n * CoinGecko supported platform API representation\n */\nexport interface CoinGeckoPlatform {\n id: string;\n chain_identifier: null | number;\n name: string;\n shortname: string;\n}\n\n/**\n * @type Token\n *\n * Token representation\n * @property address - Hex address of the token contract\n * @property decimals - Number of decimals the token uses\n * @property symbol - Symbol of the token\n * @property image - Image of the token, url or bit32 image\n */\nexport interface Token {\n address: string;\n decimals: number;\n symbol: string;\n aggregators?: string[];\n image?: string;\n balanceError?: unknown;\n isERC721?: boolean;\n}\n\n/**\n * @type TokenRatesConfig\n *\n * Token rates controller configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property nativeCurrency - Current native currency selected to use base of rates\n * @property chainId - Current network chainId\n * @property tokens - List of tokens to track exchange rates for\n * @property threshold - Threshold to invalidate the supportedChains\n */\nexport interface TokenRatesConfig extends BaseConfig {\n interval: number;\n nativeCurrency: string;\n chainId: string;\n tokens: Token[];\n threshold: number;\n}\n\ninterface ContractExchangeRates {\n [address: string]: number | undefined;\n}\n\ninterface SupportedChainsCache {\n timestamp: number;\n data: CoinGeckoPlatform[] | null;\n}\n\ninterface SupportedVsCurrenciesCache {\n timestamp: number;\n data: string[];\n}\n\n/**\n * @type TokenRatesState\n *\n * Token rates controller state\n * @property contractExchangeRates - Hash of token contract addresses to exchange rates\n * @property supportedChains - Cached chain data\n */\nexport interface TokenRatesState extends BaseState {\n contractExchangeRates: ContractExchangeRates;\n}\n\nconst CoinGeckoApi = {\n BASE_URL: 'https://api.coingecko.com/api/v3',\n getTokenPriceURL(chainSlug: string, query: string) {\n return `${this.BASE_URL}/simple/token_price/${chainSlug}?${query}`;\n },\n getPlatformsURL() {\n return `${this.BASE_URL}/asset_platforms`;\n },\n getSupportedVsCurrencies() {\n return `${this.BASE_URL}/simple/supported_vs_currencies`;\n },\n};\n\n/**\n * Finds the chain slug in the data array given a chainId.\n *\n * @param chainId - The current chain ID.\n * @param data - A list platforms supported by the CoinGecko API.\n * @returns The CoinGecko slug for the given chain ID, or `null` if the slug was not found.\n */\nfunction findChainSlug(\n chainId: string,\n data: CoinGeckoPlatform[] | null,\n): string | null {\n if (!data) {\n return null;\n }\n const chain =\n data.find(\n ({ chain_identifier }) =>\n chain_identifier !== null && String(chain_identifier) === chainId,\n ) ?? null;\n return chain?.id || null;\n}\n\n/**\n * Controller that passively polls on a set interval for token-to-fiat exchange rates\n * for tokens stored in the TokensController\n */\nexport class TokenRatesController extends BaseController<\n TokenRatesConfig,\n TokenRatesState\n> {\n private handle?: ReturnType<typeof setTimeout>;\n\n private tokenList: Token[] = [];\n\n private supportedChains: SupportedChainsCache = {\n timestamp: 0,\n data: null,\n };\n\n private supportedVsCurrencies: SupportedVsCurrenciesCache = {\n timestamp: 0,\n data: [],\n };\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenRatesController';\n\n /**\n * Creates a TokenRatesController instance.\n *\n * @param options - The controller options.\n * @param options.onTokensStateChange - Allows subscribing to token controller state changes.\n * @param options.onCurrencyRateStateChange - Allows subscribing to currency rate controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network state changes.\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 onCurrencyRateStateChange,\n onNetworkStateChange,\n }: {\n onTokensStateChange: (\n listener: (tokensState: TokensState) => void,\n ) => void;\n onCurrencyRateStateChange: (\n listener: (currencyRateState: CurrencyRateState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n },\n config?: Partial<TokenRatesConfig>,\n state?: Partial<TokenRatesState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n disabled: true,\n interval: 3 * 60 * 1000,\n nativeCurrency: 'eth',\n chainId: '',\n tokens: [],\n threshold: 6 * 60 * 60 * 1000,\n };\n\n this.defaultState = {\n contractExchangeRates: {},\n };\n this.initialize();\n this.configure({ disabled: false }, false, false);\n onTokensStateChange(({ tokens, detectedTokens }) => {\n this.configure({ tokens: [...tokens, ...detectedTokens] });\n });\n\n onCurrencyRateStateChange((currencyRateState) => {\n this.configure({ nativeCurrency: currencyRateState.nativeCurrency });\n });\n\n onNetworkStateChange(({ provider }) => {\n const { chainId } = provider;\n this.update({ contractExchangeRates: {} });\n this.configure({ chainId });\n });\n this.poll();\n }\n\n /**\n * Sets a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token rates.\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.updateExchangeRates());\n this.handle = setTimeout(() => {\n this.poll(this.config.interval);\n }, this.config.interval);\n }\n\n /**\n * Sets a new chainId.\n *\n * TODO: Replace this with a method.\n *\n * @param _chainId - The current chain ID.\n */\n set chainId(_chainId: string) {\n !this.disabled && safelyExecute(() => this.updateExchangeRates());\n }\n\n get chainId() {\n throw new Error('Property only used for setting');\n }\n\n /**\n * Sets a new token list to track prices.\n *\n * TODO: Replace this with a method.\n *\n * @param tokens - List of tokens to track exchange rates for.\n */\n set tokens(tokens: Token[]) {\n this.tokenList = tokens;\n !this.disabled && safelyExecute(() => this.updateExchangeRates());\n }\n\n get tokens() {\n throw new Error('Property only used for setting');\n }\n\n /**\n * Fetches a pairs of token address and native currency.\n *\n * @param chainSlug - Chain string identifier.\n * @param vsCurrency - Query according to tokens in tokenList and native currency.\n * @returns The exchange rates for the given pairs.\n */\n async fetchExchangeRate(\n chainSlug: string,\n vsCurrency: string,\n ): Promise<CoinGeckoResponse> {\n const tokenPairs = this.tokenList.map((token) => token.address).join(',');\n const query = `contract_addresses=${tokenPairs}&vs_currencies=${vsCurrency.toLowerCase()}`;\n return handleFetch(CoinGeckoApi.getTokenPriceURL(chainSlug, query));\n }\n\n /**\n * Checks if the current native currency is a supported vs currency to use\n * to query for token exchange rates.\n *\n * @param nativeCurrency - The native currency of the currently active network.\n * @returns A boolean indicating whether it's a supported vsCurrency.\n */\n private async checkIsSupportedVsCurrency(nativeCurrency: string) {\n const { threshold } = this.config;\n const { timestamp, data } = this.supportedVsCurrencies;\n\n const now = Date.now();\n\n if (now - timestamp > threshold) {\n const currencies = await handleFetch(\n CoinGeckoApi.getSupportedVsCurrencies(),\n );\n this.supportedVsCurrencies = {\n data: currencies,\n timestamp: Date.now(),\n };\n return currencies.includes(nativeCurrency.toLowerCase());\n }\n\n return data.includes(nativeCurrency.toLowerCase());\n }\n\n /**\n * Gets current chain ID slug from cached supported platforms CoinGecko API response.\n * If cached supported platforms response is stale, fetches and updates it.\n *\n * @returns The CoinGecko slug for the current chain ID.\n */\n async getChainSlug(): Promise<string | null> {\n const { threshold, chainId } = this.config;\n const { data, timestamp } = this.supportedChains;\n\n const now = Date.now();\n\n if (now - timestamp > threshold) {\n const platforms = await handleFetch(CoinGeckoApi.getPlatformsURL());\n this.supportedChains = {\n data: platforms,\n timestamp: Date.now(),\n };\n return findChainSlug(chainId, platforms);\n }\n\n return findChainSlug(chainId, data);\n }\n\n /**\n * Updates exchange rates for all tokens.\n */\n async updateExchangeRates() {\n if (this.tokenList.length === 0 || this.disabled) {\n return;\n }\n const slug = await this.getChainSlug();\n\n let newContractExchangeRates: ContractExchangeRates = {};\n if (!slug) {\n this.tokenList.forEach((token) => {\n const address = toChecksumHexAddress(token.address);\n newContractExchangeRates[address] = undefined;\n });\n } else {\n const { nativeCurrency } = this.config;\n newContractExchangeRates = await this.fetchAndMapExchangeRates(\n nativeCurrency,\n slug,\n );\n }\n this.update({ contractExchangeRates: newContractExchangeRates });\n }\n\n /**\n * Checks if the active network's native currency is supported by the coingecko API.\n * If supported, it fetches and maps contractExchange rates to a format to be consumed by the UI.\n * If not supported, it fetches contractExchange rates and maps them from token/fallback-currency\n * to token/nativeCurrency.\n *\n * @param nativeCurrency - The native currency of the currently active network.\n * @param slug - The unique slug used to id the chain by the coingecko api\n * should be used to query token exchange rates.\n * @returns An object with conversion rates for each token\n * related to the network's native currency.\n */\n async fetchAndMapExchangeRates(\n nativeCurrency: string,\n slug: string,\n ): Promise<ContractExchangeRates> {\n const contractExchangeRates: ContractExchangeRates = {};\n\n // check if native currency is supported as a vs_currency by the API\n const nativeCurrencySupported = await this.checkIsSupportedVsCurrency(\n nativeCurrency,\n );\n\n if (nativeCurrencySupported) {\n // If it is we can do a simple fetch against the CoinGecko API\n const prices = await this.fetchExchangeRate(slug, nativeCurrency);\n this.tokenList.forEach((token) => {\n const price = prices[token.address.toLowerCase()];\n contractExchangeRates[toChecksumHexAddress(token.address)] = price\n ? price[nativeCurrency.toLowerCase()]\n : 0;\n });\n } else {\n // if native currency is not supported we need to use a fallback vsCurrency, get the exchange rates\n // in token/fallback-currency format and convert them to expected token/nativeCurrency format.\n let tokenExchangeRates;\n let vsCurrencyToNativeCurrencyConversionRate = 0;\n try {\n [\n tokenExchangeRates,\n { conversionRate: vsCurrencyToNativeCurrencyConversionRate },\n ] = await Promise.all([\n this.fetchExchangeRate(slug, FALL_BACK_VS_CURRENCY),\n fetchNativeExchangeRate(nativeCurrency, FALL_BACK_VS_CURRENCY, false),\n ]);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('market does not exist for this coin pair')\n ) {\n return {};\n }\n throw error;\n }\n\n for (const [tokenAddress, conversion] of Object.entries(\n tokenExchangeRates,\n )) {\n const tokenToVsCurrencyConversionRate =\n conversion[FALL_BACK_VS_CURRENCY.toLowerCase()];\n contractExchangeRates[toChecksumHexAddress(tokenAddress)] =\n tokenToVsCurrencyConversionRate *\n vsCurrencyToNativeCurrencyConversionRate;\n }\n }\n\n return contractExchangeRates;\n }\n}\n\nexport default TokenRatesController;\n"]}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { Contract } from '@ethersproject/contracts';
|
|
4
|
+
import { BaseController, BaseConfig, BaseState } from '@metamask/base-controller';
|
|
5
|
+
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
6
|
+
import type { NetworkState } from '@metamask/network-controller';
|
|
7
|
+
import { NetworkType } from '@metamask/controller-utils';
|
|
8
|
+
import type { Token } from './TokenRatesController';
|
|
9
|
+
/**
|
|
10
|
+
* @type TokensConfig
|
|
11
|
+
*
|
|
12
|
+
* Tokens controller configuration
|
|
13
|
+
* @property networkType - Network ID as per net_version
|
|
14
|
+
* @property selectedAddress - Vault selected address
|
|
15
|
+
*/
|
|
16
|
+
export interface TokensConfig extends BaseConfig {
|
|
17
|
+
networkType: NetworkType;
|
|
18
|
+
selectedAddress: string;
|
|
19
|
+
chainId: string;
|
|
20
|
+
provider: any;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @type AssetSuggestionResult
|
|
24
|
+
* @property result - Promise resolving to a new suggested asset address
|
|
25
|
+
* @property suggestedAssetMeta - Meta information about this new suggested asset
|
|
26
|
+
*/
|
|
27
|
+
interface AssetSuggestionResult {
|
|
28
|
+
result: Promise<string>;
|
|
29
|
+
suggestedAssetMeta: SuggestedAssetMeta;
|
|
30
|
+
}
|
|
31
|
+
declare enum SuggestedAssetStatus {
|
|
32
|
+
accepted = "accepted",
|
|
33
|
+
failed = "failed",
|
|
34
|
+
pending = "pending",
|
|
35
|
+
rejected = "rejected"
|
|
36
|
+
}
|
|
37
|
+
export declare type SuggestedAssetMetaBase = {
|
|
38
|
+
id: string;
|
|
39
|
+
time: number;
|
|
40
|
+
type: string;
|
|
41
|
+
asset: Token;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* @type SuggestedAssetMeta
|
|
45
|
+
*
|
|
46
|
+
* Suggested asset by EIP747 meta data
|
|
47
|
+
* @property error - Synthesized error information for failed asset suggestions
|
|
48
|
+
* @property id - Generated UUID associated with this suggested asset
|
|
49
|
+
* @property status - String status of this this suggested asset
|
|
50
|
+
* @property time - Timestamp associated with this this suggested asset
|
|
51
|
+
* @property type - Type type this suggested asset
|
|
52
|
+
* @property asset - Asset suggested object
|
|
53
|
+
*/
|
|
54
|
+
export declare type SuggestedAssetMeta = (SuggestedAssetMetaBase & {
|
|
55
|
+
status: SuggestedAssetStatus.failed;
|
|
56
|
+
error: Error;
|
|
57
|
+
}) | (SuggestedAssetMetaBase & {
|
|
58
|
+
status: SuggestedAssetStatus.accepted | SuggestedAssetStatus.rejected | SuggestedAssetStatus.pending;
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* @type TokensState
|
|
62
|
+
*
|
|
63
|
+
* Assets controller state
|
|
64
|
+
* @property tokens - List of tokens associated with the active network and address pair
|
|
65
|
+
* @property ignoredTokens - List of ignoredTokens associated with the active network and address pair
|
|
66
|
+
* @property detectedTokens - List of detected tokens associated with the active network and address pair
|
|
67
|
+
* @property allTokens - Object containing tokens by network and account
|
|
68
|
+
* @property allIgnoredTokens - Object containing hidden/ignored tokens by network and account
|
|
69
|
+
* @property allDetectedTokens - Object containing tokens detected with non-zero balances
|
|
70
|
+
* @property suggestedAssets - List of pending suggested assets to be added or canceled
|
|
71
|
+
*/
|
|
72
|
+
export interface TokensState extends BaseState {
|
|
73
|
+
tokens: Token[];
|
|
74
|
+
ignoredTokens: string[];
|
|
75
|
+
detectedTokens: Token[];
|
|
76
|
+
allTokens: {
|
|
77
|
+
[key: string]: {
|
|
78
|
+
[key: string]: Token[];
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
allIgnoredTokens: {
|
|
82
|
+
[key: string]: {
|
|
83
|
+
[key: string]: string[];
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
allDetectedTokens: {
|
|
87
|
+
[key: string]: {
|
|
88
|
+
[key: string]: Token[];
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
suggestedAssets: SuggestedAssetMeta[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Controller that stores assets and exposes convenience methods
|
|
95
|
+
*/
|
|
96
|
+
export declare class TokensController extends BaseController<TokensConfig, TokensState> {
|
|
97
|
+
private mutex;
|
|
98
|
+
private ethersProvider;
|
|
99
|
+
private abortController;
|
|
100
|
+
private failSuggestedAsset;
|
|
101
|
+
/**
|
|
102
|
+
* Fetch metadata for a token.
|
|
103
|
+
*
|
|
104
|
+
* @param tokenAddress - The address of the token.
|
|
105
|
+
* @returns The token metadata.
|
|
106
|
+
*/
|
|
107
|
+
private fetchTokenMetadata;
|
|
108
|
+
/**
|
|
109
|
+
* EventEmitter instance used to listen to specific EIP747 events
|
|
110
|
+
*/
|
|
111
|
+
hub: EventEmitter;
|
|
112
|
+
/**
|
|
113
|
+
* Name of this controller used during composition
|
|
114
|
+
*/
|
|
115
|
+
name: string;
|
|
116
|
+
/**
|
|
117
|
+
* Creates a TokensController instance.
|
|
118
|
+
*
|
|
119
|
+
* @param options - The controller options.
|
|
120
|
+
* @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.
|
|
121
|
+
* @param options.onNetworkStateChange - Allows subscribing to network controller state changes.
|
|
122
|
+
* @param options.config - Initial options used to configure this controller.
|
|
123
|
+
* @param options.state - Initial state to set on this controller.
|
|
124
|
+
*/
|
|
125
|
+
constructor({ onPreferencesStateChange, onNetworkStateChange, config, state, }: {
|
|
126
|
+
onPreferencesStateChange: (listener: (preferencesState: PreferencesState) => void) => void;
|
|
127
|
+
onNetworkStateChange: (listener: (networkState: NetworkState) => void) => void;
|
|
128
|
+
config?: Partial<TokensConfig>;
|
|
129
|
+
state?: Partial<TokensState>;
|
|
130
|
+
});
|
|
131
|
+
_instantiateNewEthersProvider(): any;
|
|
132
|
+
/**
|
|
133
|
+
* Adds a token to the stored token list.
|
|
134
|
+
*
|
|
135
|
+
* @param address - Hex address of the token contract.
|
|
136
|
+
* @param symbol - Symbol of the token.
|
|
137
|
+
* @param decimals - Number of decimals the token uses.
|
|
138
|
+
* @param image - Image of the token.
|
|
139
|
+
* @returns Current token list.
|
|
140
|
+
*/
|
|
141
|
+
addToken(address: string, symbol: string, decimals: number, image?: string): Promise<Token[]>;
|
|
142
|
+
/**
|
|
143
|
+
* Add a batch of tokens.
|
|
144
|
+
*
|
|
145
|
+
* @param tokensToImport - Array of tokens to import.
|
|
146
|
+
*/
|
|
147
|
+
addTokens(tokensToImport: Token[]): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Ignore a batch of tokens.
|
|
150
|
+
*
|
|
151
|
+
* @param tokenAddressesToIgnore - Array of token addresses to ignore.
|
|
152
|
+
*/
|
|
153
|
+
ignoreTokens(tokenAddressesToIgnore: string[]): void;
|
|
154
|
+
/**
|
|
155
|
+
* Adds a batch of detected tokens to the stored token list.
|
|
156
|
+
*
|
|
157
|
+
* @param incomingDetectedTokens - Array of detected tokens to be added or updated.
|
|
158
|
+
*/
|
|
159
|
+
addDetectedTokens(incomingDetectedTokens: Token[]): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Adds isERC721 field to token object. This is called when a user attempts to add tokens that
|
|
162
|
+
* were previously added which do not yet had isERC721 field.
|
|
163
|
+
*
|
|
164
|
+
* @param tokenAddress - The contract address of the token requiring the isERC721 field added.
|
|
165
|
+
* @returns The new token object with the added isERC721 field.
|
|
166
|
+
*/
|
|
167
|
+
updateTokenType(tokenAddress: string): Promise<Token>;
|
|
168
|
+
/**
|
|
169
|
+
* Detects whether or not a token is ERC-721 compatible.
|
|
170
|
+
*
|
|
171
|
+
* @param tokenAddress - The token contract address.
|
|
172
|
+
* @returns A boolean indicating whether the token address passed in supports the EIP-721
|
|
173
|
+
* interface.
|
|
174
|
+
*/
|
|
175
|
+
_detectIsERC721(tokenAddress: string): Promise<any>;
|
|
176
|
+
_createEthersContract(tokenAddress: string, abi: string, ethersProvider: any): Contract;
|
|
177
|
+
_generateRandomId(): string;
|
|
178
|
+
/**
|
|
179
|
+
* Adds a new suggestedAsset to state. Parameters will be validated according to
|
|
180
|
+
* asset type being watched. A `<suggestedAssetMeta.id>:pending` hub event will be emitted once added.
|
|
181
|
+
*
|
|
182
|
+
* @param asset - The asset to be watched. For now only ERC20 tokens are accepted.
|
|
183
|
+
* @param type - The asset type.
|
|
184
|
+
* @returns Object containing a Promise resolving to the suggestedAsset address if accepted.
|
|
185
|
+
*/
|
|
186
|
+
watchAsset(asset: Token, type: string): Promise<AssetSuggestionResult>;
|
|
187
|
+
/**
|
|
188
|
+
* Accepts to watch an asset and updates it's status and deletes the suggestedAsset from state,
|
|
189
|
+
* adding the asset to corresponding asset state. In this case ERC20 tokens.
|
|
190
|
+
* A `<suggestedAssetMeta.id>:finished` hub event is fired after accepted or failure.
|
|
191
|
+
*
|
|
192
|
+
* @param suggestedAssetID - The ID of the suggestedAsset to accept.
|
|
193
|
+
*/
|
|
194
|
+
acceptWatchAsset(suggestedAssetID: string): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Rejects a watchAsset request based on its ID by setting its status to "rejected"
|
|
197
|
+
* and emitting a `<suggestedAssetMeta.id>:finished` hub event.
|
|
198
|
+
*
|
|
199
|
+
* @param suggestedAssetID - The ID of the suggestedAsset to accept.
|
|
200
|
+
*/
|
|
201
|
+
rejectWatchAsset(suggestedAssetID: string): void;
|
|
202
|
+
/**
|
|
203
|
+
* Takes a new tokens and ignoredTokens array for the current network/account combination
|
|
204
|
+
* and returns new allTokens and allIgnoredTokens state to update to.
|
|
205
|
+
*
|
|
206
|
+
* @param params - Object that holds token params.
|
|
207
|
+
* @param params.newTokens - The new tokens to set for the current network and selected account.
|
|
208
|
+
* @param params.newIgnoredTokens - The new ignored tokens to set for the current network and selected account.
|
|
209
|
+
* @param params.newDetectedTokens - The new detected tokens to set for the current network and selected account.
|
|
210
|
+
* @returns The updated `allTokens` and `allIgnoredTokens` state.
|
|
211
|
+
*/
|
|
212
|
+
_getNewAllTokensState(params: {
|
|
213
|
+
newTokens?: Token[];
|
|
214
|
+
newIgnoredTokens?: string[];
|
|
215
|
+
newDetectedTokens?: Token[];
|
|
216
|
+
}): {
|
|
217
|
+
newAllTokens: {
|
|
218
|
+
[key: string]: {
|
|
219
|
+
[key: string]: Token[];
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
newAllIgnoredTokens: {
|
|
223
|
+
[key: string]: {
|
|
224
|
+
[key: string]: string[];
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
newAllDetectedTokens: {
|
|
228
|
+
[key: string]: {
|
|
229
|
+
[key: string]: Token[];
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Removes all tokens from the ignored list.
|
|
235
|
+
*/
|
|
236
|
+
clearIgnoredTokens(): void;
|
|
237
|
+
}
|
|
238
|
+
export default TokensController;
|