mirror of https://github.com/certusone/oyster.git
Added solana tokens to token list and allow to select either network
This commit is contained in:
parent
b2f60da823
commit
684d922ab3
|
@ -12,6 +12,7 @@ export function Input(props: {
|
|||
chain?: ASSET_CHAIN;
|
||||
setAsset: (asset: string) => void;
|
||||
amount?: number | null;
|
||||
onChain: (chain: ASSET_CHAIN) => void;
|
||||
onInputChange: (value: number | undefined) => void;
|
||||
}) {
|
||||
const [lastAmount, setLastAmount] = useState<string>('');
|
||||
|
@ -64,6 +65,7 @@ export function Input(props: {
|
|||
<div className="ccy-input-header-right" style={{ display: 'flex' }}>
|
||||
<TokenSelectModal
|
||||
onSelectToken={token => props.setAsset(token)}
|
||||
onChain={(chain: ASSET_CHAIN) => props.onChain(chain)}
|
||||
asset={props.asset}
|
||||
chain={props.chain}
|
||||
/>
|
||||
|
|
|
@ -1,22 +1,32 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import List from 'react-virtualized/dist/commonjs/List';
|
||||
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
|
||||
import _ from 'lodash';
|
||||
import './style.less';
|
||||
import { Modal, Input } from 'antd';
|
||||
import { Input, Modal } from 'antd';
|
||||
import { useEthereum } from '../../contexts';
|
||||
import { TokenDisplay } from '../TokenDisplay';
|
||||
import { ASSET_CHAIN } from '../../models/bridge/constants';
|
||||
import { useConnectionConfig } from '@oyster/common';
|
||||
import { filterModalSolTokens } from '../../utils/assets';
|
||||
|
||||
export const TokenSelectModal = (props: {
|
||||
onSelectToken: (token: string) => void;
|
||||
onChain: (chain: ASSET_CHAIN) => void;
|
||||
asset?: string;
|
||||
chain?: ASSET_CHAIN;
|
||||
}) => {
|
||||
const { tokens } = useEthereum();
|
||||
const { tokens: ethTokens } = useEthereum();
|
||||
const { tokens: solTokens } = useConnectionConfig();
|
||||
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
||||
const [search, setSearch] = useState<string>('');
|
||||
|
||||
const inputRef = useRef<Input>(null);
|
||||
const tokens = useMemo(
|
||||
() => [...ethTokens, ...filterModalSolTokens(solTokens)],
|
||||
[ethTokens, solTokens],
|
||||
);
|
||||
|
||||
const tokenList = useMemo(() => {
|
||||
if (tokens && search) {
|
||||
return tokens.filter(token => {
|
||||
|
@ -30,6 +40,9 @@ export const TokenSelectModal = (props: {
|
|||
}, [tokens, search]);
|
||||
|
||||
const showModal = () => {
|
||||
if (inputRef && inputRef.current) {
|
||||
setTimeout(() => inputRef.current?.focus(), 300);
|
||||
}
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
|
@ -47,29 +60,40 @@ export const TokenSelectModal = (props: {
|
|||
const rowRender = (rowProps: { index: number; key: string; style: any }) => {
|
||||
const token = tokenList[rowProps.index];
|
||||
const mint = token.address;
|
||||
return (
|
||||
<div
|
||||
key={rowProps.key}
|
||||
className="multichain-option"
|
||||
title={token.name}
|
||||
onClick={() => {
|
||||
props.onSelectToken(mint);
|
||||
hideModal();
|
||||
}}
|
||||
style={{ ...rowProps.style, cursor: 'pointer' }}
|
||||
>
|
||||
<div className="multichain-option-content">
|
||||
<TokenDisplay asset={props.asset} token={token} chain={props.chain} />
|
||||
return [ASSET_CHAIN.Solana, ASSET_CHAIN.Ethereum].map((chain, index) => {
|
||||
return (
|
||||
<div
|
||||
key={rowProps.key + mint + chain}
|
||||
className="multichain-option"
|
||||
title={token.name}
|
||||
onClick={() => {
|
||||
props.onSelectToken(mint);
|
||||
props.onChain(chain);
|
||||
hideModal();
|
||||
}}
|
||||
style={{
|
||||
...rowProps.style,
|
||||
cursor: 'pointer',
|
||||
height: '70px',
|
||||
top: `${rowProps.style.top + 70 * index}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="multichain-option-name"
|
||||
style={{ marginLeft: '20px' }}
|
||||
className="multichain-option-content"
|
||||
style={{ position: 'relative' }}
|
||||
>
|
||||
<em className={'token-symbol'}>{token.symbol}</em>
|
||||
<span className={'token-name'}>{token.name}</span>
|
||||
<TokenDisplay asset={props.asset} token={token} chain={chain} />
|
||||
<div
|
||||
className="multichain-option-name"
|
||||
style={{ marginLeft: '20px' }}
|
||||
>
|
||||
<em className={'token-symbol'}>{token.symbol}</em>
|
||||
<span className={'token-name'}>{token.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -97,6 +121,8 @@ export const TokenSelectModal = (props: {
|
|||
footer={null}
|
||||
>
|
||||
<Input
|
||||
autoFocus
|
||||
ref={inputRef}
|
||||
className={'input-token-search'}
|
||||
placeholder={'SOL, SRM, ... etc'}
|
||||
value={search}
|
||||
|
@ -111,7 +137,7 @@ export const TokenSelectModal = (props: {
|
|||
<List
|
||||
ref="List"
|
||||
height={height}
|
||||
rowHeight={70}
|
||||
rowHeight={140}
|
||||
rowCount={tokenList.length || 0}
|
||||
rowRenderer={rowRender}
|
||||
width={width}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.multichain-option:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.ant-modal-body {
|
||||
height: 80vh;
|
||||
::-webkit-scrollbar-track {
|
||||
|
@ -13,7 +15,7 @@
|
|||
background-color: transparent;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
width: 10px;
|
||||
background-color: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
|
@ -23,3 +25,7 @@
|
|||
background-color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.ReactVirtualized__Grid.ReactVirtualized__List:focus {
|
||||
outline: none !important;
|
||||
}
|
|
@ -148,6 +148,11 @@ export const Transfer = () => {
|
|||
setAsset={asset => setAssetInformation(asset)}
|
||||
chain={A.chain}
|
||||
amount={A.amount}
|
||||
onChain={(chain: ASSET_CHAIN) => {
|
||||
const from = A.chain;
|
||||
A.setChain(chain);
|
||||
B.setChain(from);
|
||||
}}
|
||||
onInputChange={amount => {
|
||||
setLastTypedAccount(A.chain);
|
||||
A.setAmount(amount || 0);
|
||||
|
@ -156,6 +161,7 @@ export const Transfer = () => {
|
|||
<Button
|
||||
type="primary"
|
||||
className="swap-button"
|
||||
style={{ paddingLeft: '11px' }}
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
const from = A.chain;
|
||||
|
@ -174,6 +180,11 @@ export const Transfer = () => {
|
|||
setAsset={asset => setAssetInformation(asset)}
|
||||
chain={B.chain}
|
||||
amount={B.amount}
|
||||
onChain={(chain: ASSET_CHAIN) => {
|
||||
const to = B.chain;
|
||||
B.setChain(chain);
|
||||
A.setChain(to);
|
||||
}}
|
||||
onInputChange={amount => {
|
||||
setLastTypedAccount(B.chain);
|
||||
B.setAmount(amount || 0);
|
||||
|
@ -225,7 +236,9 @@ export const Transfer = () => {
|
|||
<div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div>
|
||||
<h5>{`${chainToName(request.from)} Mainnet -> ${chainToName(request.toChain)} Mainnet`}</h5>
|
||||
<h5>{`${chainToName(request.from)} Mainnet -> ${chainToName(
|
||||
request.toChain,
|
||||
)} Mainnet`}</h5>
|
||||
<h2>
|
||||
{request.amount?.toString()} {request.info?.name}
|
||||
</h2>
|
||||
|
|
|
@ -7,9 +7,9 @@ import React, {
|
|||
} from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import bs58 from 'bs58';
|
||||
import { useConnection } from '@oyster/common';
|
||||
import { useConnection, useConnectionConfig } from '@oyster/common';
|
||||
import { TokenInfo } from '@solana/spl-token-registry';
|
||||
import { ASSET_CHAIN } from '../utils/assets';
|
||||
import { ASSET_CHAIN, filterModalSolTokens } from '../utils/assets';
|
||||
import { useEthereum } from './ethereum';
|
||||
|
||||
export interface TokenChainContextState {
|
||||
|
@ -88,7 +88,8 @@ export const useCurrencyLeg = () => {
|
|||
};
|
||||
|
||||
export function TokenChainPairProvider({ children = null as any }) {
|
||||
const { tokens } = useEthereum();
|
||||
const { tokens: ethTokens } = useEthereum();
|
||||
const { tokens: solTokens } = useConnectionConfig();
|
||||
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
@ -107,6 +108,11 @@ export function TokenChainPairProvider({ children = null as any }) {
|
|||
const chainB = quote.chain;
|
||||
const setChainB = quote.setChain;
|
||||
|
||||
const tokens = useMemo(
|
||||
() => [...ethTokens, ...filterModalSolTokens(solTokens)],
|
||||
[ethTokens, solTokens],
|
||||
);
|
||||
|
||||
// updates browser history on token changes
|
||||
useEffect(() => {
|
||||
// set history
|
||||
|
|
|
@ -50,13 +50,17 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
|
|||
(async () => {
|
||||
const map = new Map<string, TokenInfo>();
|
||||
const listResponse: TokenList[] = await Promise.all([
|
||||
fetch('https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/tokenlist.json').then(_ =>
|
||||
_.json(),
|
||||
).catch(_ => []),
|
||||
fetch('https://tokenlist.aave.eth.link/').then(_ => _.json()).catch(() => []),
|
||||
fetch('https://tokens.coingecko.com/uniswap/all.json').then(_ =>
|
||||
_.json(),
|
||||
).catch(() => []),
|
||||
fetch(
|
||||
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/tokenlist.json',
|
||||
)
|
||||
.then(_ => _.json())
|
||||
.catch(_ => ({ tokens: [] })),
|
||||
fetch('https://tokenlist.aave.eth.link/')
|
||||
.then(_ => _.json())
|
||||
.catch(() => ({ tokens: [] })),
|
||||
fetch('https://tokens.coingecko.com/uniswap/all.json')
|
||||
.then(_ => _.json())
|
||||
.catch(() => ({ tokens: [] })),
|
||||
]);
|
||||
|
||||
listResponse.forEach((list, i) =>
|
||||
|
@ -67,12 +71,14 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
|
|||
|
||||
const item = {
|
||||
...val,
|
||||
logoURI: current?.logoURI || (val.logoURI
|
||||
? val.logoURI?.replace(
|
||||
'ipfs://',
|
||||
'https://cloudflare-ipfs.com/ipfs/',
|
||||
)
|
||||
: ` https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${val.address}/logo.png`),
|
||||
logoURI:
|
||||
current?.logoURI ||
|
||||
(val.logoURI
|
||||
? val.logoURI?.replace(
|
||||
'ipfs://',
|
||||
'https://cloudflare-ipfs.com/ipfs/',
|
||||
)
|
||||
: ` https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${val.address}/logo.png`),
|
||||
tags: val.tags ? [...val.tags, extraTag] : [extraTag],
|
||||
};
|
||||
|
||||
|
|
|
@ -47,3 +47,7 @@ const CHAIN_NAME = {
|
|||
export const chainToName = (chain?: ASSET_CHAIN) => {
|
||||
return CHAIN_NAME[chain || ASSET_CHAIN.Ethereum];
|
||||
};
|
||||
|
||||
export const filterModalSolTokens = (tokens: any[]) => {
|
||||
return tokens;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue