Switch min swap quantity to execution price bounds (#11)
This commit is contained in:
parent
3a58375616
commit
7de26316a7
|
@ -7,7 +7,7 @@
|
|||
"dependencies": {
|
||||
"react-async-hook": "^3.6.2",
|
||||
"@project-serum/serum": "^0.13.34",
|
||||
"@project-serum/swap": "^0.1.0-alpha.9",
|
||||
"@project-serum/swap": "^0.1.0-alpha.11",
|
||||
"@solana/spl-token": "^0.1.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { PublicKey } from "@solana/web3.js";
|
|||
import { useTokenMap } from "../context/TokenList";
|
||||
import { useSwapContext, useSwapFair } from "../context/Swap";
|
||||
import { useMint } from "../context/Token";
|
||||
import { useRoute, useMarketName, useFair } from "../context/Dex";
|
||||
import { useRoute, useMarketName, useBbo } from "../context/Dex";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
infoLabel: {
|
||||
|
@ -163,7 +163,7 @@ function InfoDetails() {
|
|||
|
||||
function MarketRoute({ market }: { market: PublicKey }) {
|
||||
const marketName = useMarketName(market);
|
||||
const fair = useFair(market);
|
||||
const bbo = useBbo(market);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
@ -179,7 +179,9 @@ function MarketRoute({ market }: { market: PublicKey }) {
|
|||
>
|
||||
{marketName}
|
||||
</Link>
|
||||
<code style={{ marginLeft: "10px" }}>{fair ? fair.toFixed(6) : "-"}</code>
|
||||
<code style={{ marginLeft: "10px" }}>
|
||||
{bbo && bbo.mid ? bbo.mid.toFixed(6) : "-"}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,11 @@ function SettingsDetails() {
|
|||
const [showSettingsDialog, setShowSettingsDialog] = useState(false);
|
||||
const fair = useSwapFair();
|
||||
const { swapClient } = useDexContext();
|
||||
|
||||
const setSlippageHandler = (value?: number) => {
|
||||
setSlippage(!value || value < 0 ? 0 : value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "15px", width: "305px" }}>
|
||||
<Typography color="textSecondary" style={{ fontWeight: "bold" }}>
|
||||
|
@ -85,7 +90,7 @@ function SettingsDetails() {
|
|||
type="number"
|
||||
placeholder="Error tolerance percentage"
|
||||
value={slippage}
|
||||
onChange={(e) => setSlippage(parseFloat(e.target.value))}
|
||||
onChange={(e) => setSlippageHandler(parseFloat(e.target.value))}
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
|
|
|
@ -10,12 +10,13 @@ import {
|
|||
TextField,
|
||||
} from "@material-ui/core";
|
||||
import { ExpandMore } from "@material-ui/icons";
|
||||
import { useSwapContext } from "../context/Swap";
|
||||
import { useSwapContext, useSwapFair } from "../context/Swap";
|
||||
import {
|
||||
useDexContext,
|
||||
useOpenOrders,
|
||||
useRouteVerbose,
|
||||
useMarket,
|
||||
BASE_TAKER_FEE_BPS,
|
||||
} from "../context/Dex";
|
||||
import { useTokenMap } from "../context/TokenList";
|
||||
import { useMint, useOwnedTokenAccount } from "../context/Token";
|
||||
|
@ -229,14 +230,8 @@ function TokenName({ mint }: { mint: PublicKey }) {
|
|||
|
||||
function SwapButton() {
|
||||
const styles = useStyles();
|
||||
const {
|
||||
fromMint,
|
||||
toMint,
|
||||
fromAmount,
|
||||
toAmount,
|
||||
slippage,
|
||||
isClosingNewAccounts,
|
||||
} = useSwapContext();
|
||||
const { fromMint, toMint, fromAmount, slippage, isClosingNewAccounts } =
|
||||
useSwapContext();
|
||||
const { swapClient } = useDexContext();
|
||||
const fromMintInfo = useMint(fromMint);
|
||||
const toMintInfo = useMint(toMint);
|
||||
|
@ -250,19 +245,26 @@ function SwapButton() {
|
|||
);
|
||||
const canSwap = useCanSwap();
|
||||
const referral = useReferral(fromMarket);
|
||||
const fair = useSwapFair();
|
||||
|
||||
// Click handler.
|
||||
const sendSwapTransaction = async () => {
|
||||
if (!fromMintInfo || !toMintInfo) {
|
||||
throw new Error("Unable to calculate mint decimals");
|
||||
}
|
||||
if (!fair) {
|
||||
throw new Error("Invalid fair");
|
||||
}
|
||||
const amount = new BN(fromAmount).mul(
|
||||
new BN(10).pow(new BN(fromMintInfo.decimals))
|
||||
);
|
||||
const minExpectedSwapAmount = new BN(toAmount)
|
||||
.mul(new BN(10).pow(new BN(toMintInfo.decimals)))
|
||||
.muln(100 - slippage)
|
||||
.divn(100);
|
||||
const minExchangeRate = {
|
||||
rate: new BN(10 ** toMintInfo.decimals * (1 - BASE_TAKER_FEE_BPS))
|
||||
.divn(fair)
|
||||
.muln(100 - slippage)
|
||||
.divn(100),
|
||||
decimals: fromMintInfo.decimals,
|
||||
};
|
||||
const fromOpenOrders = fromMarket
|
||||
? openOrders.get(fromMarket?.address.toString())
|
||||
: undefined;
|
||||
|
@ -273,7 +275,7 @@ function SwapButton() {
|
|||
fromMint,
|
||||
toMint,
|
||||
amount,
|
||||
minExpectedSwapAmount,
|
||||
minExchangeRate,
|
||||
referral,
|
||||
// Pass in the below parameters so that the client doesn't perform
|
||||
// wasteful network requests when we already have the data.
|
||||
|
|
|
@ -22,6 +22,8 @@ import {
|
|||
import { useTokenMap, useTokenListContext } from "./TokenList";
|
||||
import { fetchSolletInfo, requestWormholeSwapMarketIfNeeded } from "./Sollet";
|
||||
|
||||
export const BASE_TAKER_FEE_BPS = 0.0022;
|
||||
|
||||
type DexContext = {
|
||||
// Maps market address to open orders accounts.
|
||||
openOrders: Map<string, Array<OpenOrders>>;
|
||||
|
@ -293,7 +295,7 @@ export function useMarketName(market: PublicKey): string | null {
|
|||
}
|
||||
|
||||
// Fair price for a given market, as defined by the mid.
|
||||
export function useFair(market?: PublicKey): number | undefined {
|
||||
export function useBbo(market?: PublicKey): Bbo | undefined {
|
||||
const orderbook = useOrderbook(market);
|
||||
if (orderbook === undefined) {
|
||||
return undefined;
|
||||
|
@ -301,13 +303,13 @@ export function useFair(market?: PublicKey): number | undefined {
|
|||
const bestBid = orderbook.bids.items(true).next().value;
|
||||
const bestOffer = orderbook.asks.items(false).next().value;
|
||||
if (!bestBid) {
|
||||
return bestOffer.price;
|
||||
return { bestOffer: bestOffer.price };
|
||||
}
|
||||
if (!bestOffer) {
|
||||
return bestBid.price;
|
||||
return { bestBid: bestBid.price };
|
||||
}
|
||||
const mid = (bestBid.price + bestOffer.price) / 2.0;
|
||||
return mid;
|
||||
return { bestBid: bestBid.price, bestOffer: bestOffer.price, mid };
|
||||
}
|
||||
|
||||
// Fair price for a theoretical toMint/fromMint market. I.e., the number
|
||||
|
@ -318,28 +320,33 @@ export function useFairRoute(
|
|||
toMint: PublicKey
|
||||
): number | undefined {
|
||||
const route = useRoute(fromMint, toMint);
|
||||
const fromFair = useFair(route ? route[0] : undefined);
|
||||
const fromBbo = useBbo(route ? route[0] : undefined);
|
||||
const fromMarket = useMarket(route ? route[0] : undefined);
|
||||
const toFair = useFair(route ? route[1] : undefined);
|
||||
const toBbo = useBbo(route ? route[1] : undefined);
|
||||
|
||||
if (route === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (route.length === 1 && fromFair !== undefined) {
|
||||
if (route.length === 1 && fromBbo !== undefined) {
|
||||
if (fromMarket === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (fromMarket?.baseMintAddress.equals(fromMint)) {
|
||||
return 1.0 / fromFair;
|
||||
return fromBbo.bestBid && 1.0 / fromBbo.bestBid;
|
||||
} else {
|
||||
return fromFair;
|
||||
return fromBbo.bestOffer && fromBbo.bestOffer;
|
||||
}
|
||||
}
|
||||
if (fromFair === undefined || toFair === undefined) {
|
||||
if (
|
||||
fromBbo === undefined ||
|
||||
fromBbo.bestBid === undefined ||
|
||||
toBbo === undefined ||
|
||||
toBbo.bestOffer === undefined
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return toFair / fromFair;
|
||||
return toBbo.bestOffer / fromBbo.bestBid;
|
||||
}
|
||||
|
||||
export function useRoute(
|
||||
|
@ -525,3 +532,9 @@ async function deriveWormholeMarket(
|
|||
padToTwo(version);
|
||||
return await PublicKey.createWithSeed(WORM_MARKET_BASE, seed, DEX_PID);
|
||||
}
|
||||
|
||||
type Bbo = {
|
||||
bestBid?: number;
|
||||
bestOffer?: number;
|
||||
mid?: number;
|
||||
};
|
||||
|
|
|
@ -1588,10 +1588,10 @@
|
|||
bs58 "^4.0.1"
|
||||
eventemitter3 "^4.0.4"
|
||||
|
||||
"@project-serum/swap@^0.1.0-alpha.9":
|
||||
version "0.1.0-alpha.9"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/swap/-/swap-0.1.0-alpha.9.tgz#bd3b7a48426a9dbeaea457375b9ffe9b6d0272ee"
|
||||
integrity sha512-jrKpot+WPNBPTNcDoPD09XLFkr68wDVtob3vVx1eEcgN2xHtfjg0EKh42BM2BYasPsi7VSmS4yGwXui8j84jBw==
|
||||
"@project-serum/swap@^0.1.0-alpha.11":
|
||||
version "0.1.0-alpha.11"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/swap/-/swap-0.1.0-alpha.11.tgz#65c6393bdc511208453c853095eba32e0748191b"
|
||||
integrity sha512-cxjxweWrZWcxfazkxSjDXqLquKlCCOoJ7z+cfAky/l29gRet+rLbGjyd+TAcZRDrIfh+kut9svPlL/bXK9vIOw==
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.5.1-beta.2"
|
||||
"@project-serum/serum" "^0.13.34"
|
||||
|
|
Loading…
Reference in New Issue