diff options
author | fragosti <francesco.agosti93@gmail.com> | 2018-11-30 10:42:25 +0800 |
---|---|---|
committer | Fred Carlsen <fred@sjelfull.no> | 2018-12-06 18:56:11 +0800 |
commit | cc235aac3866c44a7b27bb0343390224a02b8c55 (patch) | |
tree | a3161e0f1cf6265a64c7d53bd9dd44cc75afb12f | |
parent | ec24bf84019ef16c32149040d096e436442828b8 (diff) | |
download | dexon-sol-tools-cc235aac3866c44a7b27bb0343390224a02b8c55.tar.gz dexon-sol-tools-cc235aac3866c44a7b27bb0343390224a02b8c55.tar.zst dexon-sol-tools-cc235aac3866c44a7b27bb0343390224a02b8c55.zip |
feat: implement available token fetching
-rw-r--r-- | packages/website/package.json | 2 | ||||
-rw-r--r-- | packages/website/ts/components/ui/multi_select.tsx | 13 | ||||
-rw-r--r-- | packages/website/ts/pages/instant/config_generator.tsx | 107 |
3 files changed, 116 insertions, 6 deletions
diff --git a/packages/website/package.json b/packages/website/package.json index f2ca3c2d3..38056bcfd 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -20,6 +20,8 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { + "@0x/asset-buyer": "^3.0.2", + "@0x/contract-addresses": "^2.0.0", "@0x/contract-wrappers": "^4.1.1", "@0x/json-schemas": "^2.1.2", "@0x/order-utils": "^3.0.4", diff --git a/packages/website/ts/components/ui/multi_select.tsx b/packages/website/ts/components/ui/multi_select.tsx index 329e76bd5..bf80443af 100644 --- a/packages/website/ts/components/ui/multi_select.tsx +++ b/packages/website/ts/components/ui/multi_select.tsx @@ -10,7 +10,7 @@ import { Text } from './text'; export interface MultiSelectItemConfig { value: string; - displayText: string; + displayText: React.ReactNode; onClick?: () => void; } @@ -27,11 +27,16 @@ export class MultiSelect extends React.Component<MultiSelectProps> { textColor: colors.darkGrey, }; public render(): React.ReactNode { - const { items, backgroundColor } = this.props; + const { items, backgroundColor, selectedValues } = this.props; return ( <Container backgroundColor={backgroundColor} borderRadius="4px"> {_.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> ); @@ -39,7 +44,7 @@ export class MultiSelect extends React.Component<MultiSelectProps> { } export interface MultiSelectItemProps { - displayText: string; + displayText: React.ReactNode; isSelected?: boolean; onClick?: () => void; } diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 0dac0f9ec..cd215bc61 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -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 React from 'react'; import { Container } from 'ts/components/ui/container'; +import { MultiSelect } from 'ts/components/ui/multi_select'; import { Select, SelectItemConfig } from 'ts/components/ui/select'; +import { Spinner } from 'ts/components/ui/spinner'; import { Text } from 'ts/components/ui/text'; 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'; @@ -13,17 +22,41 @@ export interface ConfigGeneratorProps { 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/']; 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 { const { value } = this.props; + if (!_.isString(value.orderSource)) { + throw new Error('ConfigGenerator component only supports string values as an orderSource.'); + } return ( <Container> <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 title="What tokens can users buy?">BLAH</ConfigGeneratorSection> </Container> ); } @@ -40,6 +73,76 @@ export class ConfigGenerator extends React.Component<ConfigGeneratorProps> { }; 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 { |