Skip to content

Commit

Permalink
Merge branch 'dynamic-speed-up' of github.com:MetaMask/controllers in…
Browse files Browse the repository at this point in the history
…to dynamic-speed-up

* 'dynamic-speed-up' of github.com:MetaMask/controllers:
  Bump @metamask/auto-changelog from 2.4.0 to 2.5.0 (#549)
  Update Changelog (#548)
  14.0.2 (#547)
  Fix `resetPolling` functionality (#546)
  TokenService improvements (#541)
  Release/14.0.1 (#545)
  Make gweiDecToWEIBN util resilient against params with too many decimals (#544)
  Bump @metamask/contract-metadata from 1.27.0 to 1.28.0 (#540)
  14.0.0 (#539)
  Bug: Mainnet NFT Autodetect API  (#536)
  • Loading branch information
rickycodes committed Aug 3, 2021
2 parents f0ba965 + 00ce669 commit 4fff419
Show file tree
Hide file tree
Showing 15 changed files with 400 additions and 81 deletions.
28 changes: 27 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [14.0.2] - 2021-07-28
### Changed
- Fix `resetPolling` functionality ([#546](https://github.com/MetaMask/controllers/pull/546))
- This fix addresses a bug that was discovered in `resetPolling` in `GasFeeController` functionality being called too frequently.
- Improve token list API error handling ([#541](https://github.com/MetaMask/controllers/pull/541))

## [14.0.1] - 2021-07-28 [DEPRECATED]
### Changed
- Ensure gas estimate fetching in gasFeeController correctly handles responses with invalid number of decimals ([#544](https://github.com/MetaMask/controllers/pull/544))
- Bump @metamask/contract-metadata from 1.27.0 to 1.28.0 ([#540](https://github.com/MetaMask/controllers/pull/540))

## [14.0.0] - 2021-07-27 [DEPRECATED]
### Added
- **BREAKING** Add EIP1559 support including `speedUpTransaction` and `stopTransaction` ([#521](https://github.com/MetaMask/controllers/pull/521))
- The breaking change here is that consumers of this repo now have to check if the transaction object includes a gas price and fetch and add it themselves (if need be).

### Changed
- Make equality comparisons for token and collectible addresses in TokensController and CollectiblesController case insensitive ([#537](https://github.com/MetaMask/controllers/pull/537))
- Reset gas fee estimate polling onNetworkStateChange in the gasFeeController ([#534](https://github.com/MetaMask/controllers/pull/534))

### Fixed
- Update AssetDetectionController to handle new fetch limits for the OpenSea collectibles api ([#536](https://github.com/MetaMask/controllers/pull/536))

## [13.2.0]
### Added
- Add options to GasFeeController fetchGasFeeEstimates ([#526](https://github.com/MetaMask/controllers/pull/526))
Expand Down Expand Up @@ -298,7 +321,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- Remove shapeshift controller (#209)

[Unreleased]: https://github.com/MetaMask/controllers/compare/v13.2.0...HEAD
[Unreleased]: https://github.com/MetaMask/controllers/compare/v14.0.2...HEAD
[14.0.2]: https://github.com/MetaMask/controllers/compare/v14.0.1...v14.0.2
[14.0.1]: https://github.com/MetaMask/controllers/compare/v14.0.0...v14.0.1
[14.0.0]: https://github.com/MetaMask/controllers/compare/v13.2.0...v14.0.0
[13.2.0]: https://github.com/MetaMask/controllers/compare/v13.1.0...v13.2.0
[13.1.0]: https://github.com/MetaMask/controllers/compare/v13.0.0...v13.1.0
[13.0.0]: https://github.com/MetaMask/controllers/compare/v12.1.0...v13.0.0
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metamask/controllers",
"version": "13.2.0",
"version": "14.0.2",
"description": "Collection of platform-agnostic modules for creating secure data models for cryptocurrency wallets",
"keywords": [
"MetaMask",
Expand Down Expand Up @@ -37,7 +37,7 @@
"dependencies": {
"@ethereumjs/common": "^2.3.1",
"@ethereumjs/tx": "^3.2.1",
"@metamask/contract-metadata": "^1.27.0",
"@metamask/contract-metadata": "^1.28.0",
"@types/uuid": "^8.3.0",
"async-mutex": "^0.2.6",
"babel-runtime": "^6.26.0",
Expand Down Expand Up @@ -68,7 +68,7 @@
},
"devDependencies": {
"@lavamoat/allow-scripts": "^1.0.6",
"@metamask/auto-changelog": "^2.4.0",
"@metamask/auto-changelog": "^2.5.0",
"@metamask/eslint-config": "^7.0.1",
"@metamask/eslint-config-jest": "^7.0.0",
"@metamask/eslint-config-nodejs": "^7.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/apis/token-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ describe('FetchtokenList', () => {
it('should call the api to return the token metadata for eth address provided', async () => {
nock(TOKEN_END_POINT_API)
.get(
`/tokens/${NetworksChainId.mainnet}?address=0x514910771af9ca656af840dff83e8264ecf986ca`,
`/token/${NetworksChainId.mainnet}?address=0x514910771af9ca656af840dff83e8264ecf986ca`,
)
.reply(200, sampleToken)
.persist();
Expand Down
71 changes: 39 additions & 32 deletions src/apis/token-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,32 @@ function getTokensURL(chainId: string) {
return `${END_POINT}/tokens/${chainId}`;
}
function getTokenMetadataURL(chainId: string, tokenAddress: string) {
return `${END_POINT}/tokens/${chainId}?address=${tokenAddress}`;
return `${END_POINT}/token/${chainId}?address=${tokenAddress}`;
}

/**
* Fetches the list of token metadata for a given network chainId
*
* @returns - Promise resolving token List
* @returns - Promise resolving token List
*/
export async function fetchTokenList(chainId: string): Promise<Response> {
export async function fetchTokenList(chainId: string): Promise<unknown> {
const tokenURL = getTokensURL(chainId);
const fetchOptions: RequestInit = {
referrer: tokenURL,
referrerPolicy: 'no-referrer-when-downgrade',
method: 'GET',
mode: 'cors',
};
fetchOptions.headers = new window.Headers();
fetchOptions.headers.set('Content-Type', 'application/json');
const tokenResponse = await timeoutFetch(tokenURL, fetchOptions);
return await tokenResponse.json();
const response = await queryApi(tokenURL);
return parseJsonResponse(response);
}

/**
* Fetch metadata for the token address provided for a given network chainId
*
* @return Promise resolving token metadata for the tokenAddress provided
*/
export async function fetchTokenMetadata(
chainId: string,
tokenAddress: string,
): Promise<unknown> {
const tokenMetadataURL = getTokenMetadataURL(chainId, tokenAddress);
const response = await queryApi(tokenMetadataURL);
return parseJsonResponse(response);
}

/**
Expand All @@ -38,35 +44,36 @@ export async function fetchTokenList(chainId: string): Promise<Response> {
*/
export async function syncTokens(chainId: string): Promise<void> {
const syncURL = syncTokensURL(chainId);
const fetchOptions: RequestInit = {
referrer: syncURL,
referrerPolicy: 'no-referrer-when-downgrade',
method: 'GET',
mode: 'cors',
};
fetchOptions.headers = new window.Headers();
fetchOptions.headers.set('Content-Type', 'application/json');
await timeoutFetch(syncURL, fetchOptions);
queryApi(syncURL);
}

/**
* Fetch metadata for the token address provided for a given network chainId
* Perform fetch request against the api
*
* @return Promise resolving token metadata for the tokenAddress provided
* @return Promise resolving request response
*/
export async function fetchTokenMetadata(
chainId: string,
tokenAddress: string,
): Promise<Response> {
const tokenMetadataURL = getTokenMetadataURL(chainId, tokenAddress);
async function queryApi(apiURL: string): Promise<Response> {
const fetchOptions: RequestInit = {
referrer: tokenMetadataURL,
referrer: apiURL,
referrerPolicy: 'no-referrer-when-downgrade',
method: 'GET',
mode: 'cors',
};
fetchOptions.headers = new window.Headers();
fetchOptions.headers.set('Content-Type', 'application/json');
const tokenResponse = await timeoutFetch(tokenMetadataURL, fetchOptions);
return await tokenResponse.json();
return await timeoutFetch(apiURL, fetchOptions);
}

/**
* Parse response
*
* @return Promise resolving request response json value
*/
async function parseJsonResponse(apiResponse: Response): Promise<unknown> {
const responseObj = await apiResponse.json();
// api may return errors as json without setting an error http status code
if (responseObj?.error) {
throw new Error(`TokenService Error: ${responseObj.error}`);
}
return responseObj;
}
24 changes: 20 additions & 4 deletions src/assets/AssetsDetectionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ describe('AssetsDetectionController', () => {
});

nock(OPEN_SEA_HOST)
.get(`${OPEN_SEA_PATH}/assets?owner=0x2&limit=300`)
.get(`${OPEN_SEA_PATH}/assets?owner=0x2&offset=0&limit=50`)
.reply(200, {
assets: [
{
Expand All @@ -177,6 +177,10 @@ describe('AssetsDetectionController', () => {
},
],
})
.get(`${OPEN_SEA_PATH}/assets?owner=0x2&offset=50&limit=50`)
.reply(200, {
assets: [],
})
.persist();

nock(OPEN_SEA_HOST)
Expand Down Expand Up @@ -208,7 +212,7 @@ describe('AssetsDetectionController', () => {
`${OPEN_SEA_PATH}/asset_contract/0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d`,
)
.replyWithError(new TypeError('Failed to fetch'))
.get(`${OPEN_SEA_PATH}/assets?owner=0x1&limit=300`)
.get(`${OPEN_SEA_PATH}/assets?owner=0x1&offset=0&limit=50`)
.reply(200, {
assets: [
{
Expand Down Expand Up @@ -240,7 +244,11 @@ describe('AssetsDetectionController', () => {
},
],
})
.get(`${OPEN_SEA_PATH}/assets?owner=0x9&limit=300`)
.get(`${OPEN_SEA_PATH}/assets?owner=0x1&offset=50&limit=50`)
.reply(200, {
assets: [],
})
.get(`${OPEN_SEA_PATH}/assets?owner=0x9&offset=50&limit=50`)
.delay(800)
.reply(200, {
assets: [
Expand All @@ -254,6 +262,10 @@ describe('AssetsDetectionController', () => {
token_id: '2574',
},
],
})
.get(`${OPEN_SEA_PATH}/assets?owner=0x9&offset=50&limit=50`)
.reply(200, {
assets: [],
});
stub(tokensController, '_detectIsERC721').callsFake(() =>
Promise.resolve(false),
Expand Down Expand Up @@ -524,7 +536,7 @@ describe('AssetsDetectionController', () => {
symbol: 'II',
total_supply: 10,
})
.get(`${OPEN_SEA_PATH}/assets?owner=0x1&limit=300`)
.get(`${OPEN_SEA_PATH}/assets?owner=0x1&offset=0&limit=50`)
.reply(200, {
assets: [
{
Expand Down Expand Up @@ -555,6 +567,10 @@ describe('AssetsDetectionController', () => {
token_id: '2574',
},
],
})
.get(`${OPEN_SEA_PATH}/assets?owner=0x1&offset=50&limit=50`)
.reply(200, {
assets: [],
});

// Now user should have respective collectibles
Expand Down
26 changes: 15 additions & 11 deletions src/assets/AssetsDetectionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,32 +139,36 @@ export class AssetsDetectionController extends BaseController<
> {
private handle?: NodeJS.Timer;

private getOwnerCollectiblesApi(address: string) {
return `https://api.opensea.io/api/v1/assets?owner=${address}&limit=300`;
private getOwnerCollectiblesApi(address: string, offset: number) {
return `https://api.opensea.io/api/v1/assets?owner=${address}&offset=${offset}&limit=50`;
}

private async getOwnerCollectibles() {
const { selectedAddress } = this.config;
const api = this.getOwnerCollectiblesApi(selectedAddress);
let response: Response;
let collectibles: any = [];
const openSeaApiKey = this.getOpenSeaApiKey();
try {
const openSeaApiKey = this.getOpenSeaApiKey();
let offset = 0;
let pagingFinish = false;
/* istanbul ignore if */
if (openSeaApiKey) {
do {
const api = this.getOwnerCollectiblesApi(selectedAddress, offset);
response = await timeoutFetch(
api,
{ headers: { 'X-API-KEY': openSeaApiKey } },
openSeaApiKey ? { headers: { 'X-API-KEY': openSeaApiKey } } : {},
15000,
);
} else {
response = await timeoutFetch(api, {}, 15000);
}
const collectiblesArray = await response.json();
collectiblesArray.assets?.length !== 0
? (collectibles = [...collectibles, ...collectiblesArray.assets])
: (pagingFinish = true);
offset += 50;
} while (!pagingFinish);
} catch (e) {
/* istanbul ignore next */
return [];
}
const collectiblesArray = await response.json();
const collectibles = collectiblesArray.assets;
return collectibles;
}

Expand Down
2 changes: 1 addition & 1 deletion src/assets/TokenListController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,7 @@ describe('TokenListController', () => {

it('should return the metadata for a tokenAddress provided', async () => {
nock(TOKEN_END_POINT_API)
.get(`/tokens/${NetworksChainId.mainnet}`)
.get(`/token/${NetworksChainId.mainnet}`)
.query({ address: '0x514910771af9ca656af840dff83e8264ecf986ca' })
.reply(200, sampleTokenMetaData)
.persist();
Expand Down
14 changes: 7 additions & 7 deletions src/assets/TokenListController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,8 @@ export class TokenListController extends BaseController<
async fetchFromDynamicTokenList(): Promise<void> {
const releaseLock = await this.mutex.acquire();
try {
const tokensFromAPI: Token[] = await safelyExecute(() =>
this.fetchFromCache(),
);
const tokensFromAPI: Token[] =
(await safelyExecute(() => this.fetchFromCache())) || [];
const { tokensChainsCache } = this.state;
const tokenList: TokenMap = {};

Expand Down Expand Up @@ -316,14 +315,15 @@ export class TokenListController extends BaseController<
/**
* Fetch metadata for a token whose address is send to the API
* @param tokenAddress
* @returns Promise that resolvesto Token Metadata
* @returns Promise that resolves to Token Metadata
*/
async fetchTokenMetadata(tokenAddress: string): Promise<Token> {
const releaseLock = await this.mutex.acquire();
try {
const token = await safelyExecute(() =>
fetchTokenMetadata(this.chainId, tokenAddress),
);
const token = (await fetchTokenMetadata(
this.chainId,
tokenAddress,
)) as Token;
return token;
} finally {
releaseLock();
Expand Down
14 changes: 9 additions & 5 deletions src/gas/GasFeeController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {

private getChainId;

private currentChainId;

private ethQuery: any;

/**
Expand Down Expand Up @@ -283,23 +285,25 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {
this.EIP1559APIEndpoint = EIP1559APIEndpoint;
this.legacyAPIEndpoint = legacyAPIEndpoint;
this.getChainId = getChainId;

this.currentChainId = this.getChainId();
const provider = getProvider();
this.ethQuery = new EthQuery(provider);
onNetworkStateChange(async () => {
const newProvider = getProvider();
const newChainId = this.getChainId();
this.ethQuery = new EthQuery(newProvider);
await this.resetPolling();
if (this.currentChainId !== newChainId) {
this.currentChainId = newChainId;
await this.resetPolling();
}
});
}

async resetPolling() {
if (this.pollTokens.size !== 0) {
// restart polling
const { getGasFeeEstimatesAndStartPolling } = this;
const tokens = Array.from(this.pollTokens);
this.stopPolling();
await getGasFeeEstimatesAndStartPolling(tokens[0]);
await this.getGasFeeEstimatesAndStartPolling(tokens[0]);
tokens.slice(1).forEach((token) => {
this.pollTokens.add(token);
});
Expand Down
Loading

0 comments on commit 4fff419

Please sign in to comment.