feat: implement available token fetching
This commit is contained in:
parent
7fe27e903b
commit
f1354632a1
@ -20,6 +20,8 @@
|
|||||||
"author": "Fabio Berger",
|
"author": "Fabio Berger",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@0x/asset-buyer": "^3.0.2",
|
||||||
|
"@0x/contract-addresses": "^2.0.0",
|
||||||
"@0x/contract-wrappers": "^4.1.1",
|
"@0x/contract-wrappers": "^4.1.1",
|
||||||
"@0x/json-schemas": "^2.1.2",
|
"@0x/json-schemas": "^2.1.2",
|
||||||
"@0x/order-utils": "^3.0.4",
|
"@0x/order-utils": "^3.0.4",
|
||||||
|
@ -10,7 +10,7 @@ import { Text } from './text';
|
|||||||
|
|
||||||
export interface MultiSelectItemConfig {
|
export interface MultiSelectItemConfig {
|
||||||
value: string;
|
value: string;
|
||||||
displayText: string;
|
displayText: React.ReactNode;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,11 +27,16 @@ export class MultiSelect extends React.Component<MultiSelectProps> {
|
|||||||
textColor: colors.darkGrey,
|
textColor: colors.darkGrey,
|
||||||
};
|
};
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { items, backgroundColor } = this.props;
|
const { items, backgroundColor, selectedValues } = this.props;
|
||||||
return (
|
return (
|
||||||
<Container backgroundColor={backgroundColor} borderRadius="4px">
|
<Container backgroundColor={backgroundColor} borderRadius="4px">
|
||||||
{_.map(items, item => (
|
{_.map(items, item => (
|
||||||
<MultiSelectItem key={item.value} displayText={item.displayText} onClick={item.onClick} />
|
<MultiSelectItem
|
||||||
|
key={item.value}
|
||||||
|
displayText={item.displayText}
|
||||||
|
onClick={item.onClick}
|
||||||
|
isSelected={_.includes(selectedValues, item.value)}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
@ -39,7 +44,7 @@ export class MultiSelect extends React.Component<MultiSelectProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MultiSelectItemProps {
|
export interface MultiSelectItemProps {
|
||||||
displayText: string;
|
displayText: React.ReactNode;
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
|
import { StandardRelayerAPIOrderProvider } from '@0x/asset-buyer';
|
||||||
|
import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses';
|
||||||
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
|
import { ObjectMap } from '@0x/types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Container } from 'ts/components/ui/container';
|
import { Container } from 'ts/components/ui/container';
|
||||||
|
import { MultiSelect } from 'ts/components/ui/multi_select';
|
||||||
import { Select, SelectItemConfig } from 'ts/components/ui/select';
|
import { Select, SelectItemConfig } from 'ts/components/ui/select';
|
||||||
|
import { Spinner } from 'ts/components/ui/spinner';
|
||||||
import { Text } from 'ts/components/ui/text';
|
import { Text } from 'ts/components/ui/text';
|
||||||
import { colors } from 'ts/style/colors';
|
import { colors } from 'ts/style/colors';
|
||||||
|
import { WebsiteBackendTokenInfo } from 'ts/types';
|
||||||
|
import { backendClient } from 'ts/utils/backend_client';
|
||||||
|
import { constants } from 'ts/utils/constants';
|
||||||
|
|
||||||
import { ZeroExInstantBaseConfig } from '../../../../instant/src/types';
|
import { ZeroExInstantBaseConfig } from '../../../../instant/src/types';
|
||||||
|
|
||||||
@ -13,17 +22,41 @@ export interface ConfigGeneratorProps {
|
|||||||
onConfigChange: (config: ZeroExInstantBaseConfig) => void;
|
onConfigChange: (config: ZeroExInstantBaseConfig) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConfigGeneratorState {
|
||||||
|
isLoadingAvailableTokens: boolean;
|
||||||
|
// Address to token info
|
||||||
|
allKnownTokens: ObjectMap<WebsiteBackendTokenInfo>;
|
||||||
|
availableTokens?: WebsiteBackendTokenInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/'];
|
const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/'];
|
||||||
|
|
||||||
export class ConfigGenerator extends React.Component<ConfigGeneratorProps> {
|
export class ConfigGenerator extends React.Component<ConfigGeneratorProps> {
|
||||||
|
public state: ConfigGeneratorState = {
|
||||||
|
isLoadingAvailableTokens: true,
|
||||||
|
allKnownTokens: {},
|
||||||
|
};
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this._setAllKnownTokens(this._setAvailableAssetsFromOrderProvider);
|
||||||
|
}
|
||||||
|
public componentDidUpdate(prevProps: ConfigGeneratorProps): void {
|
||||||
|
if (prevProps.value.orderSource !== this.props.value.orderSource) {
|
||||||
|
this._setAvailableAssetsFromOrderProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { value } = this.props;
|
const { value } = this.props;
|
||||||
|
if (!_.isString(value.orderSource)) {
|
||||||
|
throw new Error('ConfigGenerator component only supports string values as an orderSource.');
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<ConfigGeneratorSection title="Standard Relayer API Endpoint">
|
<ConfigGeneratorSection title="Standard Relayer API Endpoint">
|
||||||
<Select value={value.orderSource as string} items={this._generateItems()} />
|
<Select value={value.orderSource} items={this._generateItems()} />
|
||||||
|
</ConfigGeneratorSection>
|
||||||
|
<ConfigGeneratorSection title="What tokens can users buy?">
|
||||||
|
{this._renderTokenMultiSelectOrSpinner()}
|
||||||
</ConfigGeneratorSection>
|
</ConfigGeneratorSection>
|
||||||
<ConfigGeneratorSection title="What tokens can users buy?">BLAH</ConfigGeneratorSection>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -40,6 +73,76 @@ export class ConfigGenerator extends React.Component<ConfigGeneratorProps> {
|
|||||||
};
|
};
|
||||||
this.props.onConfigChange(newConfig);
|
this.props.onConfigChange(newConfig);
|
||||||
};
|
};
|
||||||
|
private readonly _handleTokenClick = (assetData: string) => {
|
||||||
|
const { value } = this.props;
|
||||||
|
let newAvailableAssetDatas = [];
|
||||||
|
if (_.includes(value.availableAssetDatas, assetData)) {
|
||||||
|
// Add it
|
||||||
|
newAvailableAssetDatas = [...value.availableAssetDatas, assetData];
|
||||||
|
} else {
|
||||||
|
// Remove it
|
||||||
|
newAvailableAssetDatas = _.remove(value.availableAssetDatas, assetData);
|
||||||
|
}
|
||||||
|
const newConfig = {
|
||||||
|
...this.props.value,
|
||||||
|
availableAssetDatas: newAvailableAssetDatas,
|
||||||
|
};
|
||||||
|
this.props.onConfigChange(newConfig);
|
||||||
|
};
|
||||||
|
private _setAllKnownTokens = async (callback: () => void): Promise<void> => {
|
||||||
|
const tokenInfos = await backendClient.getTokenInfosAsync();
|
||||||
|
const allKnownTokens = _.reduce(
|
||||||
|
tokenInfos,
|
||||||
|
(acc, tokenInfo) => {
|
||||||
|
acc[tokenInfo.address] = tokenInfo;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as ObjectMap<WebsiteBackendTokenInfo>,
|
||||||
|
);
|
||||||
|
this.setState({ allKnownTokens }, callback);
|
||||||
|
};
|
||||||
|
private _setAvailableAssetsFromOrderProvider = async (): Promise<void> => {
|
||||||
|
const { value } = this.props;
|
||||||
|
if (!_.isUndefined(value.orderSource) && _.isString(value.orderSource)) {
|
||||||
|
this.setState({ isLoadingAvailableTokens: true });
|
||||||
|
const networkId = constants.NETWORK_ID_MAINNET;
|
||||||
|
const sraOrderProvider = new StandardRelayerAPIOrderProvider(value.orderSource, networkId);
|
||||||
|
const etherTokenAddress = getContractAddressesForNetworkOrThrow(networkId).etherToken;
|
||||||
|
const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddress);
|
||||||
|
const assetDatas = await sraOrderProvider.getAvailableMakerAssetDatasAsync(etherTokenAssetData);
|
||||||
|
const availableTokens = _.compact(
|
||||||
|
_.map(assetDatas, assetData => {
|
||||||
|
const address = assetDataUtils.decodeAssetDataOrThrow(assetData).tokenAddress;
|
||||||
|
return this.state.allKnownTokens[address];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.setState({ availableTokens, isLoadingAvailableTokens: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private _renderTokenMultiSelectOrSpinner = (): React.ReactNode => {
|
||||||
|
const { value } = this.props;
|
||||||
|
const { availableTokens, isLoadingAvailableTokens } = this.state;
|
||||||
|
if (isLoadingAvailableTokens) {
|
||||||
|
return (
|
||||||
|
<Container className="flex items-center">
|
||||||
|
<Spinner />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const items = _.map(availableTokens, token => {
|
||||||
|
const assetData = assetDataUtils.encodeERC20AssetData(token.address);
|
||||||
|
return {
|
||||||
|
value: assetDataUtils.encodeERC20AssetData(token.address),
|
||||||
|
displayText: (
|
||||||
|
<Text>
|
||||||
|
<b>{token.symbol}</b> - {token.name}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
onClick: this._handleTokenClick.bind(this, assetData),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return <MultiSelect items={items} selectedValues={value.availableAssetDatas || []} />;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigGeneratorSectionProps {
|
export interface ConfigGeneratorSectionProps {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user