Open orders dialog
This commit is contained in:
parent
527177117f
commit
4a239d8a08
|
@ -6,6 +6,7 @@
|
|||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.4",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@project-serum/serum": "^0.13.34",
|
||||
"@project-serum/sol-wallet-adapter": "^0.2.0",
|
||||
"@project-serum/swap": "^0.1.0-alpha.2",
|
||||
"@solana/spl-token": "^0.1.4",
|
||||
|
@ -19,7 +20,9 @@
|
|||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"bs58": "^4.0.1",
|
||||
"material-ui-popup-state": "^1.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-async-hook": "^3.6.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"typescript": "^4.1.2",
|
||||
|
|
|
@ -18,7 +18,7 @@ function App() {
|
|||
preflightCommitment: "recent",
|
||||
commitment: "recent",
|
||||
};
|
||||
const network = "https://api.mainnet-beta.solana.com";
|
||||
const network = "https://solana-api.projectserum.com";
|
||||
const wallet = new Wallet("https://www.sollet.io", network);
|
||||
const connection = new Connection(network, opts.preflightCommitment);
|
||||
const provider = new Provider(connection, wallet, opts);
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import React, { useContext, useState, useEffect } from "react";
|
||||
import { useAsync } from "react-async-hook";
|
||||
import * as anchor from "@project-serum/anchor";
|
||||
import { Provider } from "@project-serum/anchor";
|
||||
import { Swap as SwapClient } from "@project-serum/swap";
|
||||
import { Market, OpenOrders } from "@project-serum/serum";
|
||||
import { PublicKey, Account } from "@solana/web3.js";
|
||||
import {
|
||||
AccountInfo as TokenAccount,
|
||||
|
@ -17,6 +21,7 @@ export const USDC_MINT = new PublicKey(
|
|||
export const USDT_MINT = new PublicKey(
|
||||
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
||||
);
|
||||
const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
|
||||
|
||||
const SwapContext = React.createContext<null | SwapContext>(null);
|
||||
|
||||
|
@ -30,7 +35,6 @@ export function SwapContextProvider(props: any) {
|
|||
const [toBalance, setToBalance] = useState(undefined);
|
||||
const [minExpectedAmount, setMinExpectedAmount] = useState(0);
|
||||
const [ownedTokenAccounts, setOwnedTokenAccounts] = useState(undefined);
|
||||
const [mintCache, setMintCache] = useState(new Map<string, MintInfo>());
|
||||
|
||||
// Fetch all the owned token accounts for the wallet.
|
||||
useEffect(() => {
|
||||
|
@ -43,51 +47,6 @@ export function SwapContextProvider(props: any) {
|
|||
swapClient.program.provider.connection,
|
||||
]);
|
||||
|
||||
// Fetch the mint account infos not already in the cache.
|
||||
useEffect(() => {
|
||||
const fromMintClient = new Token(
|
||||
swapClient.program.provider.connection,
|
||||
fromMint,
|
||||
TOKEN_PROGRAM_ID,
|
||||
new Account()
|
||||
);
|
||||
const toMintClient = new Token(
|
||||
swapClient.program.provider.connection,
|
||||
toMint,
|
||||
TOKEN_PROGRAM_ID,
|
||||
new Account()
|
||||
);
|
||||
|
||||
let promises = [];
|
||||
if (mintCache.get(fromMint.toString())) {
|
||||
promises.push(
|
||||
(async (): Promise<MintInfo> => {
|
||||
return mintCache.get(fromMint.toString()) as MintInfo;
|
||||
})()
|
||||
);
|
||||
} else {
|
||||
promises.push(fromMintClient.getMintInfo());
|
||||
}
|
||||
if (mintCache.get(toMint.toString())) {
|
||||
promises.push(
|
||||
(async (): Promise<MintInfo> => {
|
||||
return mintCache.get(toMint.toString()) as MintInfo;
|
||||
})()
|
||||
);
|
||||
} else {
|
||||
promises.push(toMintClient.getMintInfo());
|
||||
}
|
||||
|
||||
Promise.all(promises as [Promise<MintInfo>, Promise<MintInfo>]).then(
|
||||
([fromMintInfo, toMintInfo]: [MintInfo, MintInfo]) => {
|
||||
let cache = new Map(mintCache);
|
||||
cache.set(fromMint.toString(), fromMintInfo);
|
||||
cache.set(toMint.toString(), toMintInfo);
|
||||
setMintCache(cache);
|
||||
}
|
||||
);
|
||||
}, [fromMint, toMint]);
|
||||
|
||||
const swapToFromMints = () => {
|
||||
const oldFrom = fromMint;
|
||||
const oldFromAmount = fromAmount;
|
||||
|
@ -116,7 +75,6 @@ export function SwapContextProvider(props: any) {
|
|||
fromBalance,
|
||||
toBalance,
|
||||
ownedTokenAccounts,
|
||||
mintCache,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
@ -151,7 +109,6 @@ export type SwapContext = {
|
|||
ownedTokenAccounts:
|
||||
| { publicKey: PublicKey; account: TokenAccount }[]
|
||||
| undefined;
|
||||
mintCache: Map<string, MintInfo>;
|
||||
};
|
||||
|
||||
const TokenListContext = React.createContext<null | TokenListContext>(null);
|
||||
|
@ -207,10 +164,154 @@ export function useOwnedTokenAccount(
|
|||
return tokenAccounts[0];
|
||||
}
|
||||
|
||||
export function useMintAccount(mint: PublicKey): MintInfo | undefined {
|
||||
const ctx = useContext(SwapContext);
|
||||
const MintContext = React.createContext<null | MintContext>(null);
|
||||
type MintContext = {
|
||||
mintCache: Map<string, MintInfo>;
|
||||
setMintCache: (m: Map<string, MintInfo>) => void;
|
||||
provider: Provider;
|
||||
};
|
||||
|
||||
export function MintContextProvider(props: any) {
|
||||
const provider = props.provider;
|
||||
const [mintCache, setMintCache] = useState(new Map<string, MintInfo>());
|
||||
|
||||
return (
|
||||
<MintContext.Provider
|
||||
value={{
|
||||
mintCache,
|
||||
setMintCache,
|
||||
provider,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</MintContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useMint(mint: PublicKey): MintInfo | undefined | null {
|
||||
const ctx = useContext(MintContext);
|
||||
if (ctx === null) {
|
||||
throw new Error("Mint context not found");
|
||||
}
|
||||
|
||||
// Lazy load the mint account if needeed.
|
||||
const asyncMintInfo = useAsync(async () => {
|
||||
if (ctx.mintCache.get(mint.toString())) {
|
||||
return ctx.mintCache.get(mint.toString());
|
||||
}
|
||||
const mintClient = new Token(
|
||||
ctx.provider.connection,
|
||||
mint,
|
||||
TOKEN_PROGRAM_ID,
|
||||
new Account()
|
||||
);
|
||||
const mintInfo = await mintClient.getMintInfo();
|
||||
|
||||
let cache = new Map(ctx.mintCache);
|
||||
cache.set(mint.toString(), mintInfo);
|
||||
ctx.setMintCache(cache);
|
||||
|
||||
return mintInfo;
|
||||
}, [ctx.provider.connection, mint]);
|
||||
|
||||
if (asyncMintInfo.result) {
|
||||
return asyncMintInfo.result;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const SerumDexContext = React.createContext<SerumDexContext | null>(null);
|
||||
type SerumDexContext = {
|
||||
// Maps market address to open orders accounts.
|
||||
openOrders: Map<string, Array<OpenOrders>>;
|
||||
marketCache: Map<string, Market>;
|
||||
};
|
||||
|
||||
export function useOpenOrders(): Map<string, Array<OpenOrders>> {
|
||||
const ctx = useContext(SerumDexContext);
|
||||
if (ctx === null) {
|
||||
throw new Error("Context not available");
|
||||
}
|
||||
return ctx.mintCache.get(mint.toString());
|
||||
return ctx.openOrders;
|
||||
}
|
||||
|
||||
export function useMarket(market: PublicKey): Market | undefined {
|
||||
const ctx = useContext(SerumDexContext);
|
||||
if (ctx === null) {
|
||||
throw new Error("Context not available");
|
||||
}
|
||||
return ctx.marketCache.get(market.toString());
|
||||
}
|
||||
|
||||
export function SerumDexContextProvider(props: any) {
|
||||
const [ooAccounts, setOoAccounts] = useState<Map<string, Array<OpenOrders>>>(
|
||||
new Map()
|
||||
);
|
||||
const [marketCache, setMarketCache] = useState<Map<string, Market>>(
|
||||
new Map()
|
||||
);
|
||||
const provider = props.provider;
|
||||
|
||||
// Two operations:
|
||||
// 1. Fetch all open orders accounts for the connected wallet.
|
||||
// 2. Batch fetch all market accounts.
|
||||
useEffect(() => {
|
||||
OpenOrders.findForOwner(
|
||||
provider.connection,
|
||||
provider.wallet.publicKey,
|
||||
DEX_PID
|
||||
).then(async (openOrders) => {
|
||||
const newOoAccounts = new Map();
|
||||
let markets = new Set<string>();
|
||||
openOrders.forEach((oo) => {
|
||||
markets.add(oo.market.toString());
|
||||
if (newOoAccounts.get(oo.market.toString())) {
|
||||
newOoAccounts.get(oo.market.toString()).push(oo);
|
||||
} else {
|
||||
newOoAccounts.set(oo.market.toString(), [oo]);
|
||||
}
|
||||
});
|
||||
if (markets.size > 100) {
|
||||
// Punt request chunking until there's user demand.
|
||||
throw new Error(
|
||||
"Too many markets. Please file an issue to update this"
|
||||
);
|
||||
}
|
||||
const marketAccounts = (
|
||||
await anchor.utils.getMultipleAccounts(
|
||||
provider.connection,
|
||||
// @ts-ignore
|
||||
[...markets].map((m) => new PublicKey(m))
|
||||
)
|
||||
).map((programAccount) => {
|
||||
return {
|
||||
publicKey: programAccount?.publicKey,
|
||||
account: new Market(
|
||||
Market.getLayout(DEX_PID).decode(programAccount?.account.data),
|
||||
-1, // Not used so don't bother fetching.
|
||||
-1, // Not used so don't bother fetching.
|
||||
provider.opts,
|
||||
DEX_PID
|
||||
),
|
||||
};
|
||||
});
|
||||
const newMarketCache = new Map(marketCache);
|
||||
marketAccounts.forEach((m) => {
|
||||
newMarketCache.set(m.publicKey!.toString(), m.account);
|
||||
});
|
||||
|
||||
setMarketCache(newMarketCache);
|
||||
setOoAccounts(newOoAccounts);
|
||||
});
|
||||
}, [provider.connection, provider.wallet.publicKey, DEX_PID]);
|
||||
return (
|
||||
<SerumDexContext.Provider
|
||||
value={{
|
||||
openOrders: ooAccounts,
|
||||
marketCache,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</SerumDexContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
import { useState } from "react";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { MintInfo } from "@solana/spl-token";
|
||||
import { BN } from "@project-serum/anchor";
|
||||
import { OpenOrders } from "@project-serum/serum";
|
||||
import {
|
||||
makeStyles,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
Paper,
|
||||
Table,
|
||||
TableRow,
|
||||
TableHead,
|
||||
TableCell,
|
||||
TableBody,
|
||||
TableContainer,
|
||||
Popover,
|
||||
IconButton,
|
||||
Typography,
|
||||
Button,
|
||||
Select,
|
||||
MenuItem,
|
||||
} from "@material-ui/core";
|
||||
import { SettingsOutlined as Settings } from "@material-ui/icons";
|
||||
import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state";
|
||||
import { useOpenOrders, useMarket, useMint, useTokenList } from "./Context";
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
tab: {
|
||||
width: "50%",
|
||||
},
|
||||
table: {},
|
||||
settingsButton: {
|
||||
padding: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function SettingsButton() {
|
||||
const styles = useStyles();
|
||||
const [showSettingsDialog, setShowSettingsDialog] = useState(false);
|
||||
return (
|
||||
<PopupState variant="popover">
|
||||
{
|
||||
//@ts-ignore
|
||||
(popupState) => (
|
||||
<div>
|
||||
<IconButton
|
||||
{...bindTrigger(popupState)}
|
||||
className={styles.settingsButton}
|
||||
>
|
||||
<Settings />
|
||||
</IconButton>
|
||||
<Popover
|
||||
{...bindPopover(popupState)}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
PaperProps={{ style: { borderRadius: "10px" } }}
|
||||
>
|
||||
<div style={{ padding: "15px", width: "305px", height: "285px" }}>
|
||||
<Typography
|
||||
color="textSecondary"
|
||||
style={{ fontWeight: "bold" }}
|
||||
>
|
||||
Swap Settings
|
||||
</Typography>
|
||||
<div>
|
||||
<div>Slippage</div>
|
||||
<Button onClick={() => setShowSettingsDialog(true)}>
|
||||
Manage Dex Accounts
|
||||
</Button>
|
||||
</div>
|
||||
<SettingsDialog
|
||||
open={showSettingsDialog}
|
||||
onClose={() => setShowSettingsDialog(false)}
|
||||
/>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</PopupState>
|
||||
);
|
||||
}
|
||||
|
||||
export function SettingsDialog({
|
||||
open,
|
||||
onClose,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
maxWidth="lg"
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
PaperProps={{
|
||||
style: {
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<DialogContent style={{ paddingTop: 0 }}>
|
||||
<OpenOrdersAccounts />
|
||||
</DialogContent>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function OpenOrdersAccounts() {
|
||||
const styles = useStyles();
|
||||
const openOrders = useOpenOrders();
|
||||
return (
|
||||
<TableContainer component={Paper} elevation={0}>
|
||||
<Table className={styles.table} aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Market</TableCell>
|
||||
<TableCell align="right">Base Used</TableCell>
|
||||
<TableCell align="right">Base Free</TableCell>
|
||||
<TableCell align="right">Quote Used</TableCell>
|
||||
<TableCell align="right">Quote Free</TableCell>
|
||||
<TableCell align="right">Open Orders</TableCell>
|
||||
<TableCell align="right">Action</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{Array.from(openOrders.entries()).map(([market, oos]) => {
|
||||
return (
|
||||
<OpenOrdersRow
|
||||
key={market}
|
||||
market={new PublicKey(market)}
|
||||
openOrders={oos}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function OpenOrdersRow({
|
||||
market,
|
||||
openOrders,
|
||||
}: {
|
||||
market: PublicKey;
|
||||
openOrders: Array<OpenOrders>;
|
||||
}) {
|
||||
const [ooAccount, setOoAccount] = useState(openOrders[0]);
|
||||
const marketClient = useMarket(market);
|
||||
const tokenList = useTokenList();
|
||||
const base = useMint(marketClient!.baseMintAddress);
|
||||
const quote = useMint(marketClient!.quoteMintAddress);
|
||||
const baseTicker = tokenList
|
||||
.filter((t) => t.address === marketClient!.baseMintAddress.toString())
|
||||
.map((t) => t.symbol)[0];
|
||||
const quoteTicker = tokenList
|
||||
.filter((t) => t.address === marketClient!.quoteMintAddress.toString())
|
||||
.map((t) => t.symbol)[0];
|
||||
const marketName =
|
||||
baseTicker && quoteTicker
|
||||
? `${baseTicker} / ${quoteTicker}`
|
||||
: market.toString();
|
||||
const closeDisabled =
|
||||
ooAccount.baseTokenTotal.toNumber() +
|
||||
ooAccount.quoteTokenTotal.toNumber() !==
|
||||
0;
|
||||
|
||||
const closeOpenOrders = async () => {
|
||||
// todo
|
||||
};
|
||||
|
||||
return (
|
||||
<TableRow key={market.toString()}>
|
||||
<TableCell component="th" scope="row">
|
||||
{marketName}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{toDisplay(base, ooAccount.baseTokenTotal.sub(ooAccount.baseTokenFree))}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{toDisplay(base, ooAccount.baseTokenFree)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{toDisplay(
|
||||
quote,
|
||||
ooAccount.quoteTokenTotal.sub(ooAccount.quoteTokenFree)
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{toDisplay(quote, ooAccount.quoteTokenFree)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Select
|
||||
value={ooAccount.address.toString()}
|
||||
onChange={(e) =>
|
||||
setOoAccount(
|
||||
openOrders.filter(
|
||||
(oo) => oo.address.toString() === e.target.value
|
||||
)[0]
|
||||
)
|
||||
}
|
||||
>
|
||||
{openOrders.map((oo) => {
|
||||
return (
|
||||
<MenuItem value={oo.address.toString()}>
|
||||
{oo.address.toString()}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Button
|
||||
color="secondary"
|
||||
disabled={closeDisabled}
|
||||
onClick={closeOpenOrders}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
function toDisplay(mintInfo: MintInfo | undefined | null, value: BN): string {
|
||||
if (!mintInfo) {
|
||||
return value.toNumber().toString();
|
||||
}
|
||||
return (value.toNumber() / 10 ** mintInfo.decimals).toFixed(
|
||||
mintInfo.decimals
|
||||
);
|
||||
}
|
|
@ -7,23 +7,23 @@ import {
|
|||
makeStyles,
|
||||
Card,
|
||||
Button,
|
||||
Tabs,
|
||||
Tab,
|
||||
IconButton,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
} from "@material-ui/core";
|
||||
import { Settings, Info, ExpandMore } from "@material-ui/icons";
|
||||
import { Info, ExpandMore } from "@material-ui/icons";
|
||||
import {
|
||||
MintContextProvider,
|
||||
SwapContextProvider,
|
||||
TokenListContextProvider,
|
||||
SerumDexContextProvider,
|
||||
useSwapContext,
|
||||
useTokenList,
|
||||
useOwnedTokenAccount,
|
||||
useMintAccount,
|
||||
useMint,
|
||||
} from "./Context";
|
||||
import TokenDialog from "./TokenDialog";
|
||||
import SettingsButton from "./SettingsDialog";
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
|
@ -39,10 +39,6 @@ const useStyles = makeStyles(() => ({
|
|||
tab: {
|
||||
width: "50%",
|
||||
},
|
||||
settings: {
|
||||
display: "flex",
|
||||
flexDirection: "row-reverse",
|
||||
},
|
||||
settingsButton: {
|
||||
padding: 0,
|
||||
},
|
||||
|
@ -76,11 +72,15 @@ export default function Swap({
|
|||
}) {
|
||||
const swapClient = new SwapClient(provider, tokenList);
|
||||
return (
|
||||
<SwapContextProvider swapClient={swapClient}>
|
||||
<TokenListContextProvider tokenList={tokenList}>
|
||||
<SwapInner style={style} />
|
||||
</TokenListContextProvider>
|
||||
</SwapContextProvider>
|
||||
<MintContextProvider provider={provider}>
|
||||
<SwapContextProvider swapClient={swapClient}>
|
||||
<TokenListContextProvider tokenList={tokenList}>
|
||||
<SerumDexContextProvider provider={provider}>
|
||||
<SwapInner style={style} />
|
||||
</SerumDexContextProvider>
|
||||
</TokenListContextProvider>
|
||||
</SwapContextProvider>
|
||||
</MintContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ function AuxilliaryLabel() {
|
|||
|
||||
return (
|
||||
<div className={styles.auxilliaryLabel}>
|
||||
<Typography color="textSecondary">Serum</Typography>
|
||||
<Typography color="textSecondary"></Typography>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
|
@ -160,16 +160,6 @@ function AuxilliaryLabel() {
|
|||
);
|
||||
}
|
||||
|
||||
function TabSelector() {
|
||||
const styles = useStyles();
|
||||
return (
|
||||
<Tabs>
|
||||
<Tab label="Trade" className={styles.tab} />
|
||||
<Tab label="Accounts" className={styles.tab} />
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
export function SwapToFromButton() {
|
||||
const styles = useStyles();
|
||||
const { swapToFromMints } = useSwapContext();
|
||||
|
@ -180,22 +170,10 @@ export function SwapToFromButton() {
|
|||
);
|
||||
}
|
||||
|
||||
function SettingsButton() {
|
||||
const styles = useStyles();
|
||||
return (
|
||||
<div className={styles.settings}>
|
||||
<IconButton className={styles.settingsButton}>
|
||||
<Settings />
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SwapFromForm() {
|
||||
const { fromMint, setFromMint, fromAmount, setFromAmount } = useSwapContext();
|
||||
return (
|
||||
<SwapTokenForm
|
||||
isEstimate={false}
|
||||
mint={fromMint}
|
||||
setMint={setFromMint}
|
||||
amount={fromAmount}
|
||||
|
@ -208,7 +186,6 @@ function SwapToForm() {
|
|||
const { toMint, setToMint, toAmount, setToAmount } = useSwapContext();
|
||||
return (
|
||||
<SwapTokenForm
|
||||
isEstimate={true}
|
||||
mint={toMint}
|
||||
setMint={setToMint}
|
||||
amount={toAmount}
|
||||
|
@ -218,13 +195,11 @@ function SwapToForm() {
|
|||
}
|
||||
|
||||
function SwapTokenForm({
|
||||
isEstimate,
|
||||
mint,
|
||||
setMint,
|
||||
amount,
|
||||
setAmount,
|
||||
}: {
|
||||
isEstimate: boolean;
|
||||
mint: PublicKey;
|
||||
setMint: (m: PublicKey) => void;
|
||||
amount: number;
|
||||
|
@ -232,10 +207,10 @@ function SwapTokenForm({
|
|||
}) {
|
||||
const [showTokenDialog, setShowTokenDialog] = useState(false);
|
||||
const tokenAccount = useOwnedTokenAccount(mint);
|
||||
const mintAccount = useMintAccount(mint);
|
||||
const mintAccount = useMint(mint);
|
||||
|
||||
return (
|
||||
<Paper elevation={0} variant="outlined">
|
||||
<Paper elevation={0} variant="outlined" style={{ borderRadius: "10px" }}>
|
||||
<div
|
||||
style={{
|
||||
height: "50px",
|
||||
|
@ -283,7 +258,7 @@ function TokenButton({
|
|||
}) {
|
||||
return (
|
||||
<Button onClick={onClick} style={{ width: "116px" }}>
|
||||
<TokenIcon mint={mint} style={{ width: "25px" }} />
|
||||
<TokenIcon mint={mint} style={{ width: "25px", borderRadius: "13px" }} />
|
||||
<TokenName mint={mint} />
|
||||
<ExpandMore />
|
||||
</Button>
|
||||
|
@ -335,7 +310,3 @@ function SwapButton() {
|
|||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function TokenSelector() {
|
||||
return <div></div>;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ export default function TokenDialog({
|
|||
.concat([USDC_MINT, USDT_MINT])
|
||||
.map((mint) => (
|
||||
<TokenListItem
|
||||
key={mint.toString()}
|
||||
mint={mint}
|
||||
onClick={(mint) => {
|
||||
setMint(mint);
|
||||
|
|
27
yarn.lock
27
yarn.lock
|
@ -1472,6 +1472,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2"
|
||||
integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==
|
||||
|
||||
"@material-ui/types@^4.1.1":
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-4.1.1.tgz#b65e002d926089970a3271213a3ad7a21b17f02b"
|
||||
integrity sha512-AN+GZNXytX9yxGi0JOfxHrRTbhFybjUJ05rnsBVjcB+16e466Z0Xe5IxawuOayVZgTBNDxmPKo5j4V6OnMtaSQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@material-ui/utils@^4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.11.2.tgz#f1aefa7e7dff2ebcb97d31de51aecab1bb57540a"
|
||||
|
@ -3615,6 +3622,11 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@^2.2.6:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
||||
|
||||
clean-css@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
|
||||
|
@ -7815,6 +7827,16 @@ map-visit@^1.0.0:
|
|||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
material-ui-popup-state@^1.8.3:
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/material-ui-popup-state/-/material-ui-popup-state-1.8.3.tgz#1e263682659c6b39af38c68c21ce4f316f2bb56e"
|
||||
integrity sha512-qvrX5snjXwn3rv5i3+w/T1WF3NHRPZ6KzB/gOzh9cxowEvlWV0HkCSbPmEDTnfQt6s6r964cMV987QZ50eNhxA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@material-ui/types" "^4.1.1"
|
||||
classnames "^2.2.6"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||
|
@ -9813,6 +9835,11 @@ react-app-polyfill@^2.0.0:
|
|||
regenerator-runtime "^0.13.7"
|
||||
whatwg-fetch "^3.4.1"
|
||||
|
||||
react-async-hook@^3.6.2:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react-async-hook/-/react-async-hook-3.6.2.tgz#360018e5cd6ecc8841152a79875841b9e49dbdba"
|
||||
integrity sha512-RkwHCJ8V7I6umKZLHneapuTRWf+eO4LOj0qUwUDsSn27jrAOcW6ClbV3x22Z4hVxH9bA0zb7y+ozDJDJ8PnZoA==
|
||||
|
||||
react-dev-utils@^11.0.3:
|
||||
version "11.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a"
|
||||
|
|
Loading…
Reference in New Issue