Info popover
This commit is contained in:
parent
94ded3ab69
commit
dc5abad2c0
|
@ -0,0 +1,160 @@
|
|||
import { makeStyles, Typography, Link, Popover } from "@material-ui/core";
|
||||
import { Info } from "@material-ui/icons";
|
||||
import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state";
|
||||
import { useFair } from "./context/Dex";
|
||||
import { useTokenList } from "./context/TokenList";
|
||||
import { useSwapContext } from "./context/Swap";
|
||||
import { useMint } from "./context/Mint";
|
||||
import { useMarketRoute } from "./context/Dex";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
infoLabel: {
|
||||
marginTop: "10px",
|
||||
marginBottom: "10px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
},
|
||||
fairPriceLabel: {
|
||||
marginRight: "10px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}));
|
||||
|
||||
export function InfoLabel() {
|
||||
const styles = useStyles();
|
||||
|
||||
const { fromMint, toMint } = useSwapContext();
|
||||
const fromMintInfo = useMint(fromMint);
|
||||
const fair = useFair(fromMint, toMint);
|
||||
|
||||
const tokenList = useTokenList();
|
||||
let fromTokenInfo = tokenList.filter(
|
||||
(t) => t.address === fromMint.toString()
|
||||
)[0];
|
||||
let toTokenInfo = tokenList.filter((t) => t.address === toMint.toString())[0];
|
||||
|
||||
return (
|
||||
<div className={styles.infoLabel}>
|
||||
<Typography color="textSecondary"></Typography>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div className={styles.fairPriceLabel}>
|
||||
{fair !== undefined
|
||||
? `1 ${toTokenInfo.symbol} = ${fair.toFixed(
|
||||
fromMintInfo?.decimals
|
||||
)} ${fromTokenInfo.symbol}`
|
||||
: `-`}
|
||||
</div>
|
||||
<InfoPopover />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoPopover() {
|
||||
const { fromMint, toMint } = useSwapContext();
|
||||
const route = useMarketRoute(fromMint, toMint);
|
||||
const tokenList = useTokenList();
|
||||
const fromMintTicker = tokenList
|
||||
.filter((t) => t.address === fromMint.toString())
|
||||
.map((t) => t.symbol)[0];
|
||||
const toMintTicker = tokenList
|
||||
.filter((t) => t.address === toMint.toString())
|
||||
.map((t) => t.symbol)[0];
|
||||
const addresses = [
|
||||
{ ticker: fromMintTicker, mint: fromMint },
|
||||
{ ticker: toMintTicker, mint: toMint },
|
||||
];
|
||||
return (
|
||||
<PopupState variant="popover">
|
||||
{
|
||||
//@ts-ignore
|
||||
(popupState) => (
|
||||
<div style={{ display: "flex" }}>
|
||||
<Info {...bindTrigger(popupState)} />
|
||||
<Popover
|
||||
{...bindPopover(popupState)}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
PaperProps={{ style: { borderRadius: "10px" } }}
|
||||
disableRestoreFocus
|
||||
>
|
||||
<div style={{ padding: "15px", width: "250px" }}>
|
||||
<div>
|
||||
<Typography
|
||||
color="textSecondary"
|
||||
style={{ fontWeight: "bold", marginBottom: "5px" }}
|
||||
>
|
||||
Trade Route
|
||||
</Typography>
|
||||
{route.map((market) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
href={`https://dex.projectserum.com/#/market/${market.address.toString()}`}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{market.name}
|
||||
</Link>
|
||||
<code style={{ marginLeft: "10px" }}>
|
||||
{market.fair}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div style={{ marginTop: "15px" }}>
|
||||
<Typography
|
||||
color="textSecondary"
|
||||
style={{ fontWeight: "bold", marginBottom: "5px" }}
|
||||
>
|
||||
Tokens
|
||||
</Typography>
|
||||
{addresses.map((address) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginTop: "5px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
href={`https://explorer.solana.com/address/${address.mint.toString()}`}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{address.ticker}
|
||||
</Link>
|
||||
<code style={{ width: "128px", overflow: "hidden" }}>
|
||||
{address.mint.toString()}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</PopupState>
|
||||
);
|
||||
}
|
|
@ -22,6 +22,7 @@ import {
|
|||
MenuItem,
|
||||
TextField,
|
||||
InputAdornment,
|
||||
Link,
|
||||
} from "@material-ui/core";
|
||||
import { SettingsOutlined as Settings } from "@material-ui/icons";
|
||||
import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state";
|
||||
|
@ -154,12 +155,12 @@ function OpenOrdersAccounts() {
|
|||
<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 Account</TableCell>
|
||||
<TableCell align="right">Action</TableCell>
|
||||
<TableCell align="center">Open Orders Account</TableCell>
|
||||
<TableCell align="center">Base Used</TableCell>
|
||||
<TableCell align="center">Base Free</TableCell>
|
||||
<TableCell align="center">Quote Used</TableCell>
|
||||
<TableCell align="center">Quote Free</TableCell>
|
||||
<TableCell align="center">Action</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
|
@ -212,24 +213,17 @@ function OpenOrdersRow({
|
|||
return (
|
||||
<TableRow key={market.toString()}>
|
||||
<TableCell component="th" scope="row">
|
||||
{marketName}
|
||||
<Typography>
|
||||
<Link
|
||||
href={`https://dex.projectserum.com/#/market/${market.toString()}`}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{marketName}
|
||||
</Link>
|
||||
</Typography>
|
||||
</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">
|
||||
<TableCell align="center">
|
||||
<Select
|
||||
value={ooAccount.address.toString()}
|
||||
onChange={(e) =>
|
||||
|
@ -249,7 +243,22 @@ function OpenOrdersRow({
|
|||
})}
|
||||
</Select>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<TableCell align="center">
|
||||
{toDisplay(base, ooAccount.baseTokenTotal.sub(ooAccount.baseTokenFree))}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{toDisplay(base, ooAccount.baseTokenFree)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{toDisplay(
|
||||
quote,
|
||||
ooAccount.quoteTokenTotal.sub(ooAccount.quoteTokenFree)
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{toDisplay(quote, ooAccount.quoteTokenFree)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Button
|
||||
color="secondary"
|
||||
disabled={closeDisabled}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
Typography,
|
||||
TextField,
|
||||
} from "@material-ui/core";
|
||||
import { Info, ExpandMore } from "@material-ui/icons";
|
||||
import { ExpandMore } from "@material-ui/icons";
|
||||
import { SwapContextProvider, useSwapContext } from "./context/Swap";
|
||||
import { DexContextProvider } from "./context/Dex";
|
||||
import { MintContextProvider, useMint } from "./context/Mint";
|
||||
|
@ -19,6 +19,7 @@ import { TokenListContextProvider, useTokenList } from "./context/TokenList";
|
|||
import { TokenContextProvider, useOwnedTokenAccount } from "./context/Token";
|
||||
import TokenDialog from "./TokenDialog";
|
||||
import { SettingsButton } from "./Settings";
|
||||
import { InfoLabel } from "./Info";
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
|
@ -46,14 +47,6 @@ const useStyles = makeStyles(() => ({
|
|||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
},
|
||||
auxilliaryLabel: {
|
||||
marginTop: "10px",
|
||||
marginBottom: "10px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Swap({
|
||||
|
@ -89,9 +82,9 @@ function SwapCard({ style }: { style?: any }) {
|
|||
<SwapHeader />
|
||||
<div className={styles.cardContent}>
|
||||
<SwapFromForm />
|
||||
<SwapToFromButton />
|
||||
<ArrowButton />
|
||||
<SwapToForm />
|
||||
<AuxilliaryLabel />
|
||||
<InfoLabel />
|
||||
<SwapButton />
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -123,41 +116,7 @@ function SwapHeader() {
|
|||
);
|
||||
}
|
||||
|
||||
function AuxilliaryLabel() {
|
||||
const styles = useStyles();
|
||||
|
||||
const { fromMint, toMint, fromAmount, toAmount } = useSwapContext();
|
||||
const toPrice = (fromAmount / toAmount).toFixed(6); // TODO: decimals per mint type.
|
||||
|
||||
const tokenList = useTokenList();
|
||||
let fromTokenInfo = tokenList.filter(
|
||||
(t) => t.address === fromMint.toString()
|
||||
)[0];
|
||||
let toTokenInfo = tokenList.filter((t) => t.address === toMint.toString())[0];
|
||||
|
||||
return (
|
||||
<div className={styles.auxilliaryLabel}>
|
||||
<Typography color="textSecondary"></Typography>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{fromAmount !== 0 && toAmount !== 0
|
||||
? `1 ${toTokenInfo.symbol} = ${toPrice} ${fromTokenInfo.symbol}`
|
||||
: `-`}
|
||||
</div>
|
||||
<Info />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SwapToFromButton() {
|
||||
export function ArrowButton() {
|
||||
const styles = useStyles();
|
||||
const { swapToFromMints } = useSwapContext();
|
||||
return (
|
||||
|
|
|
@ -11,8 +11,9 @@ import {
|
|||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import { TokenIcon } from "./Swap";
|
||||
import { useSwapContext, USDC_MINT, USDT_MINT } from "./context/Swap";
|
||||
import { useSwapContext } from "./context/Swap";
|
||||
import { useTokenList } from "./context/TokenList";
|
||||
import { USDC_MINT, USDT_MINT } from "../utils/pubkeys";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
dialogContent: {
|
||||
|
|
|
@ -2,19 +2,24 @@ 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 { Market, OpenOrders } from "@project-serum/serum";
|
||||
import {
|
||||
Market,
|
||||
OpenOrders,
|
||||
Orderbook as OrderbookSide,
|
||||
} from "@project-serum/serum";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { DEX_PID } from "../../utils/pubkeys";
|
||||
|
||||
const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
|
||||
|
||||
const _DexContext = React.createContext<DexContext | null>(null);
|
||||
type DexContext = {
|
||||
// Maps market address to open orders accounts.
|
||||
openOrders: Map<string, Array<OpenOrders>>;
|
||||
marketCache: Map<string, Market>;
|
||||
setMarketCache: (c: Map<string, Market>) => void;
|
||||
orderbookCache: Map<string, Orderbook>;
|
||||
setOrderbookCache: (c: Map<string, Orderbook>) => void;
|
||||
provider: Provider;
|
||||
};
|
||||
const _DexContext = React.createContext<DexContext | null>(null);
|
||||
|
||||
export function DexContextProvider(props: any) {
|
||||
const [ooAccounts, setOoAccounts] = useState<Map<string, Array<OpenOrders>>>(
|
||||
|
@ -23,11 +28,16 @@ export function DexContextProvider(props: any) {
|
|||
const [marketCache, setMarketCache] = useState<Map<string, Market>>(
|
||||
new Map()
|
||||
);
|
||||
const [orderbookCache, setOrderbookCache] = useState<Map<string, Orderbook>>(
|
||||
new Map()
|
||||
);
|
||||
const provider = props.provider;
|
||||
|
||||
// Two operations:
|
||||
//
|
||||
// 1. Fetch all open orders accounts for the connected wallet.
|
||||
// 2. Batch fetch all market accounts.
|
||||
// 2. Batch fetch all market accounts for those open orders.
|
||||
//
|
||||
useEffect(() => {
|
||||
OpenOrders.findForOwner(
|
||||
provider.connection,
|
||||
|
@ -84,6 +94,8 @@ export function DexContextProvider(props: any) {
|
|||
openOrders: ooAccounts,
|
||||
marketCache,
|
||||
setMarketCache,
|
||||
orderbookCache,
|
||||
setOrderbookCache,
|
||||
provider,
|
||||
}}
|
||||
>
|
||||
|
@ -92,19 +104,22 @@ export function DexContextProvider(props: any) {
|
|||
);
|
||||
}
|
||||
|
||||
export function useOpenOrders(): Map<string, Array<OpenOrders>> {
|
||||
function useDexContext(): DexContext {
|
||||
const ctx = useContext(_DexContext);
|
||||
if (ctx === null) {
|
||||
throw new Error("Context not available");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function useOpenOrders(): Map<string, Array<OpenOrders>> {
|
||||
const ctx = useDexContext();
|
||||
return ctx.openOrders;
|
||||
}
|
||||
|
||||
// Lazy load a given market.
|
||||
export function useMarket(market: PublicKey): Market | undefined {
|
||||
const ctx = useContext(_DexContext);
|
||||
if (ctx === null) {
|
||||
throw new Error("Context not available");
|
||||
}
|
||||
const ctx = useDexContext();
|
||||
|
||||
const asyncMarket = useAsync(async () => {
|
||||
if (ctx.marketCache.get(market.toString())) {
|
||||
|
@ -130,3 +145,74 @@ export function useMarket(market: PublicKey): Market | undefined {
|
|||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Lazy load the orderbook for a given market.
|
||||
export function useOrderbook(market: PublicKey): Orderbook | undefined {
|
||||
const ctx = useDexContext();
|
||||
const marketClient = useMarket(market);
|
||||
|
||||
const asyncOrderbook = useAsync(async () => {
|
||||
if (!marketClient) {
|
||||
return undefined;
|
||||
}
|
||||
if (ctx.orderbookCache.get(market.toString())) {
|
||||
return ctx.orderbookCache.get(market.toString());
|
||||
}
|
||||
|
||||
const [bids, asks] = await Promise.all([
|
||||
marketClient.loadBids(ctx.provider.connection),
|
||||
marketClient.loadAsks(ctx.provider.connection),
|
||||
]);
|
||||
|
||||
const orderbook = {
|
||||
bids,
|
||||
asks,
|
||||
};
|
||||
|
||||
const cache = new Map(ctx.orderbookCache);
|
||||
cache.set(market.toString(), orderbook);
|
||||
ctx.setOrderbookCache(cache);
|
||||
|
||||
return orderbook;
|
||||
}, [ctx.provider.connection, market, marketClient]);
|
||||
|
||||
if (asyncOrderbook.result) {
|
||||
return asyncOrderbook.result;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function useMarketRoute(
|
||||
fromMint: PublicKey,
|
||||
toMint: PublicKey
|
||||
): Array<{ address: PublicKey; name: string; fair: number }> {
|
||||
// todo
|
||||
return [
|
||||
{
|
||||
address: new PublicKey("ByRys5tuUWDgL73G8JBAEfkdFf8JWBzPBDHsBVQ5vbQA"),
|
||||
name: "SRM / USDC",
|
||||
fair: 0.5,
|
||||
},
|
||||
{
|
||||
address: new PublicKey("J7cPYBrXVy8Qeki2crZkZavcojf2sMRyQU7nx438Mf8t"),
|
||||
name: "MATH / USDC",
|
||||
fair: 1.23,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Fair price for a theoretical toMint/fromMint market. I.e., the number
|
||||
// of `fromMint` tokens to purchase a single `toMint` token.
|
||||
export function useFair(
|
||||
fromMint: PublicKey,
|
||||
toMint: PublicKey
|
||||
): number | undefined {
|
||||
// todo
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
type Orderbook = {
|
||||
bids: OrderbookSide;
|
||||
asks: OrderbookSide;
|
||||
};
|
||||
|
|
|
@ -2,15 +2,25 @@ import React, { useContext, useState } from "react";
|
|||
import { Swap as SwapClient } from "@project-serum/swap";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { MintInfo } from "@solana/spl-token";
|
||||
import { SRM_MINT, USDC_MINT } from "../../utils/pubkeys";
|
||||
import { useFair } from "./Dex";
|
||||
|
||||
const SRM_MINT = new PublicKey("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt");
|
||||
export const USDC_MINT = new PublicKey(
|
||||
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
||||
);
|
||||
export const USDT_MINT = new PublicKey(
|
||||
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
||||
);
|
||||
|
||||
export type SwapContext = {
|
||||
swapClient: SwapClient;
|
||||
fromMint: PublicKey;
|
||||
setFromMint: (m: PublicKey) => void;
|
||||
toMint: PublicKey;
|
||||
setToMint: (m: PublicKey) => void;
|
||||
fromAmount: number;
|
||||
setFromAmount: (a: number) => void;
|
||||
toAmount: number;
|
||||
setToAmount: (a: number) => void;
|
||||
swapToFromMints: () => void;
|
||||
fromMintAccount?: MintInfo;
|
||||
toMintAccount?: MintInfo;
|
||||
slippage: number;
|
||||
setSlippage: (n: number) => void;
|
||||
};
|
||||
const _SwapContext = React.createContext<null | SwapContext>(null);
|
||||
|
||||
export function SwapContextProvider(props: any) {
|
||||
|
@ -21,6 +31,7 @@ export function SwapContextProvider(props: any) {
|
|||
const [toAmount, _setToAmount] = useState(0);
|
||||
// Percent units.
|
||||
const [slippage, setSlippage] = useState(0.5);
|
||||
const fair = useFair(fromMint, toMint);
|
||||
|
||||
const swapToFromMints = () => {
|
||||
const oldFrom = fromMint;
|
||||
|
@ -34,11 +45,19 @@ export function SwapContextProvider(props: any) {
|
|||
};
|
||||
|
||||
const setFromAmount = (amount: number) => {
|
||||
if (fair === undefined) {
|
||||
throw new Error("Fair price not found");
|
||||
}
|
||||
_setFromAmount(amount);
|
||||
_setToAmount(amount / fair);
|
||||
};
|
||||
|
||||
const setToAmount = (amount: number) => {
|
||||
if (fair === undefined) {
|
||||
throw new Error("Fair price not found");
|
||||
}
|
||||
_setToAmount(amount);
|
||||
_setFromAmount(amount * fair);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -70,20 +89,3 @@ export function useSwapContext(): SwapContext {
|
|||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export type SwapContext = {
|
||||
swapClient: SwapClient;
|
||||
fromMint: PublicKey;
|
||||
setFromMint: (m: PublicKey) => void;
|
||||
toMint: PublicKey;
|
||||
setToMint: (m: PublicKey) => void;
|
||||
fromAmount: number;
|
||||
setFromAmount: (a: number) => void;
|
||||
toAmount: number;
|
||||
setToAmount: (a: number) => void;
|
||||
swapToFromMints: () => void;
|
||||
fromMintAccount?: MintInfo;
|
||||
toMintAccount?: MintInfo;
|
||||
slippage: number;
|
||||
setSlippage: (n: number) => void;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const SRM_MINT = new PublicKey(
|
||||
"SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"
|
||||
);
|
||||
|
||||
export const USDC_MINT = new PublicKey(
|
||||
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
||||
);
|
||||
|
||||
export const USDT_MINT = new PublicKey(
|
||||
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
||||
);
|
||||
|
||||
export const DEX_PID = new PublicKey(
|
||||
"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
|
||||
);
|
Loading…
Reference in New Issue