Skip to content

Commit

Permalink
**BREAKING**: Fetch and return token image as part of getDetails ca…
Browse files Browse the repository at this point in the history
…lls on ERC721Standard and ERC1155Standard (#702)

* Fetch and return token image as part of getDetails call

* adapt tests
  • Loading branch information
adonesky1 committed Mar 3, 2022
1 parent e8598c3 commit 613c5b9
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 24 deletions.
10 changes: 8 additions & 2 deletions src/ComposableController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ describe('ComposableController', () => {
it('should compose controller state', () => {
const preferencesController = new PreferencesController();
const networkController = new NetworkController();
const assetContractController = new AssetsContractController();
const assetContractController = new AssetsContractController({
onPreferencesStateChange: (listener) =>
preferencesController.subscribe(listener),
});
const collectiblesController = new CollectiblesController({
onPreferencesStateChange: (listener) =>
preferencesController.subscribe(listener),
Expand Down Expand Up @@ -172,7 +175,10 @@ describe('ComposableController', () => {
it('should compose flat controller state', () => {
const preferencesController = new PreferencesController();
const networkController = new NetworkController();
const assetContractController = new AssetsContractController();
const assetContractController = new AssetsContractController({
onPreferencesStateChange: (listener) =>
preferencesController.subscribe(listener),
});
const collectiblesController = new CollectiblesController({
onPreferencesStateChange: (listener) =>
preferencesController.subscribe(listener),
Expand Down
22 changes: 21 additions & 1 deletion src/assets/AssetsContractController.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import HttpProvider from 'ethjs-provider-http';
import { IPFS_DEFAULT_GATEWAY_URL } from '../constants';
import { PreferencesController } from '../user/PreferencesController';
import { AssetsContractController } from './AssetsContractController';

const MAINNET_PROVIDER = new HttpProvider(
Expand All @@ -16,12 +18,30 @@ const TEST_ACCOUNT_PUBLIC_ADDRESS =
'0x5a3CA5cD63807Ce5e4d7841AB32Ce6B6d9BbBa2D';
describe('AssetsContractController', () => {
let assetsContract: AssetsContractController;
let preferences: PreferencesController;
beforeEach(() => {
assetsContract = new AssetsContractController();
preferences = new PreferencesController();
assetsContract = new AssetsContractController({
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
});
});

it('should set default config', () => {
expect(assetsContract.config).toStrictEqual({
ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,
provider: undefined,
});
});

it('should update the ipfsGateWay config value when this value is changed in the preferences controller', () => {
expect(assetsContract.config).toStrictEqual({
ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,
provider: undefined,
});

preferences.setIpfsGateway('newIPFSGateWay');
expect(assetsContract.config).toStrictEqual({
ipfsGateway: 'newIPFSGateWay',
provider: undefined,
});
});
Expand Down
30 changes: 28 additions & 2 deletions src/assets/AssetsContractController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BN } from 'ethereumjs-util';
import Web3 from 'web3';
import abiSingleCallBalancesContract from 'single-call-balance-checker-abi';
import { BaseController, BaseConfig, BaseState } from '../BaseController';
import type { PreferencesState } from '../user/PreferencesController';
import { IPFS_DEFAULT_GATEWAY_URL } from '../constants';
import { ERC721Standard } from './Standards/CollectibleStandards/ERC721/ERC721Standard';
import { ERC1155Standard } from './Standards/CollectibleStandards/ERC1155/ERC1155Standard';
import { ERC20Standard } from './Standards/ERC20Standard';
Expand All @@ -20,6 +22,7 @@ const MISSING_PROVIDER_ERROR =
*/
export interface AssetsContractConfig extends BaseConfig {
provider: any;
ipfsGateway: string;
}

/**
Expand Down Expand Up @@ -55,18 +58,31 @@ export class AssetsContractController extends BaseController<
/**
* Creates a AssetsContractController instance.
*
* @param options - The controller options.
* @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.
* @param config - Initial options used to configure this controller.
* @param state - Initial state to set on this controller.
*/
constructor(
{
onPreferencesStateChange,
}: {
onPreferencesStateChange: (
listener: (preferencesState: PreferencesState) => void,
) => void;
},
config?: Partial<AssetsContractConfig>,
state?: Partial<BaseState>,
) {
super(config, state);
this.defaultConfig = {
provider: undefined,
ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,
};
this.initialize();
onPreferencesStateChange(({ ipfsGateway }) => {
this.configure({ ipfsGateway });
});
}

/**
Expand Down Expand Up @@ -168,10 +184,16 @@ export class AssetsContractController extends BaseController<
throw new Error(MISSING_PROVIDER_ERROR);
}

const { ipfsGateway } = this.config;

// ERC721
try {
return {
...(await this.erc721Standard.getDetails(tokenAddress, tokenId)),
...(await this.erc721Standard.getDetails(
tokenAddress,
ipfsGateway,
tokenId,
)),
};
} catch {
// Ignore
Expand All @@ -180,7 +202,11 @@ export class AssetsContractController extends BaseController<
// ERC1155
try {
return {
...(await this.erc1155Standard.getDetails(tokenAddress, tokenId)),
...(await this.erc1155Standard.getDetails(
tokenAddress,
ipfsGateway,
tokenId,
)),
};
} catch {
// Ignore
Expand Down
5 changes: 4 additions & 1 deletion src/assets/CollectibleDetectionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ describe('CollectibleDetectionController', () => {
beforeEach(async () => {
preferences = new PreferencesController();
network = new NetworkController();
assetsContract = new AssetsContractController();
assetsContract = new AssetsContractController({
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
});

collectiblesController = new CollectiblesController({
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
onNetworkStateChange: (listener) => network.subscribe(listener),
Expand Down
5 changes: 4 additions & 1 deletion src/assets/CollectiblesController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ describe('CollectiblesController', () => {
beforeEach(() => {
preferences = new PreferencesController();
network = new NetworkController();
assetsContract = new AssetsContractController();
assetsContract = new AssetsContractController({
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
});

collectiblesController = new CollectiblesController({
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
onNetworkStateChange: (listener) => network.subscribe(listener),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ERC1155_METADATA_URI_INTERFACE_ID,
ERC1155_TOKEN_RECEIVER_INTERFACE_ID,
} from '../../../../constants';
import { getFormattedIpfsUrl, timeoutFetch } from '../../../../util';
import { Web3 } from '../../standards-types';

export class ERC1155Standard {
Expand Down Expand Up @@ -173,27 +174,46 @@ export class ERC1155Standard {
* Query if a contract implements an interface.
*
* @param address - Asset contract address.
* @param ipfsGateway - The user's preferred IPFS gateway.
* @param tokenId - tokenId of a given token in the contract.
* @returns Promise resolving an object containing the standard, tokenURI, symbol and name of the given contract/tokenId pair.
*/
getDetails = async (
address: string,
ipfsGateway: string,
tokenId?: string,
): Promise<{
standard: string;
tokenURI: string | undefined;
image: string | undefined;
}> => {
const isERC1155 = await this.contractSupportsBase1155Interface(address);
let tokenURI;
let tokenURI, image;

if (tokenId) {
tokenURI = await this.getTokenURI(address, tokenId);
if (tokenURI.startsWith('ipfs://')) {

This comment has been minimized.

Copy link
@returnportal

returnportal May 8, 2022

Sorry where's the test for ipfs://?

tokenURI = getFormattedIpfsUrl(ipfsGateway, tokenURI, true);
}

try {
const response = await timeoutFetch(tokenURI);
const object = await response.json();
image = object?.image;
if (image?.startsWith('ipfs://')) {
image = getFormattedIpfsUrl(ipfsGateway, image, true);
}
} catch {
// ignore
}
}

// TODO consider querying to the metadata to get name.
if (isERC1155) {
return {
standard: ERC1155,
tokenURI,
image,
};
}

Expand Down
Loading

0 comments on commit 613c5b9

Please sign in to comment.