Execute swaps on mainnet

This commit is contained in:
armaniferrante 2021-05-15 00:39:56 -07:00
parent c3ebc2ac77
commit 5c3c0a5f25
No known key found for this signature in database
GPG Key ID: 58BEF301E91F7828
6 changed files with 154 additions and 60 deletions

View File

@ -23,6 +23,7 @@
"@types/react-dom": "^17.0.0",
"bs58": "^4.0.1",
"material-ui-popup-state": "^1.8.3",
"notistack": "^1.0.7",
"react": "^17.0.2",
"react-async-hook": "^3.6.2",
"react-dom": "^17.0.2",

View File

@ -1,46 +1,25 @@
import React, { useState, useEffect } from "react";
import { Typography } from "@material-ui/core";
import { useState, useEffect } from "react";
import { SnackbarProvider, useSnackbar } from "notistack";
import { Button, Typography } from "@material-ui/core";
import { Provider } from "@project-serum/anchor";
// @ts-ignore
import Wallet from "@project-serum/sol-wallet-adapter";
import { ConfirmOptions, Connection } from "@solana/web3.js";
import {
Account,
ConfirmOptions,
Connection,
Transaction,
TransactionSignature,
} from "@solana/web3.js";
import { TokenListProvider } from "@solana/spl-token-registry";
import Swap from "./components/Swap";
import "./App.css";
// App illustrating the use of the Swap component.
//
// One needs to just provide an Anchor `Provider` and a `TokenListContainer`
// to the `Swap` component, and then everything else is taken care of.
function App() {
const [params, setParams] = useState<any>(null);
const [isConnected, setIsConnected] = useState(false);
// Create the provider and token list.
useEffect(() => {
const opts: ConfirmOptions = {
preflightCommitment: "recent",
commitment: "recent",
};
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);
new TokenListProvider().resolve().then((tokenList) => {
setParams({
provider,
tokenList,
});
wallet.connect();
});
}, [setParams]);
// Connect to the wallet.
useEffect(() => {
if (params !== null) {
params.provider.wallet.on("connect", () => {
setIsConnected(true);
});
params.provider.wallet.connect();
}
}, [params]);
return (
<div
style={{
@ -57,13 +36,99 @@ function App() {
flexDirection: "column",
}}
>
{isConnected ? (
<Swap provider={params.provider} tokenList={params.tokenList} />
) : (
<Typography style={{ textAlign: "center" }}>Disconnected</Typography>
)}
<SnackbarProvider maxSnack={5} autoHideDuration={8000}>
<AppInner />
</SnackbarProvider>
</div>
);
}
function AppInner() {
const { enqueueSnackbar } = useSnackbar();
const [params, setParams] = useState<any>(null);
const [isConnected, setIsConnected] = useState(false);
// Create the provider and token list.
useEffect(() => {
const opts: ConfirmOptions = {
preflightCommitment: "recent",
commitment: "recent",
};
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 NotifyingProvider(connection, wallet, opts, (tx) => {
enqueueSnackbar("Transaction sent", {
variant: "success",
action: (
<Button
color="inherit"
component="a"
target="_blank"
rel="noopener"
href={`https://explorer.solana.com/tx/${tx}`}
>
View on Solana Explorer
</Button>
),
});
});
new TokenListProvider().resolve().then((tokenList) => {
setParams({
provider,
tokenList,
});
wallet.connect();
});
}, [enqueueSnackbar, setParams]);
// Connect to the wallet.
useEffect(() => {
if (params !== null) {
params.provider.wallet.on("connect", () => {
setIsConnected(true);
});
params.provider.wallet.connect();
}
}, [params]);
return isConnected ? (
<Swap provider={params.provider} tokenList={params.tokenList} />
) : (
<Typography style={{ textAlign: "center" }}>Disconnected</Typography>
);
}
// Custom provider to display notifications whenever a transaction is sent.
//
// Note that this is an Anchor wallet/network provider--not a React provider,
// so all transactions will be flowing through here, which allows us to
// hook in to display all transactions sent from the `Swap` component
// as notifications in the parent app.
class NotifyingProvider extends Provider {
// Function to call whenever the provider sends a transaction;
private onTransaction: (tx: TransactionSignature) => void;
constructor(
connection: Connection,
wallet: Wallet,
opts: ConfirmOptions,
onTransaction: (tx: TransactionSignature) => void
) {
super(connection, wallet, opts);
this.onTransaction = onTransaction;
}
async send(
tx: Transaction,
signers?: Array<Account | undefined>,
opts?: ConfirmOptions
): Promise<TransactionSignature> {
// A production implementation should handle error notifications as well.
const txSig = await super.send(tx, signers, opts);
this.onTransaction(txSig);
return txSig;
}
}
export default App;

View File

@ -188,25 +188,20 @@ export function SettingsDialog({
<div>
<div
style={{
marginTop: "10px",
paddingLeft: "24px",
paddingRight: "24px",
height: "24px",
display: "flex",
justifyContent: "space-between",
}}
>
<Typography
color="textSecondary"
variant="h6"
<IconButton
onClick={onClose}
style={{
display: "flex",
justifyContent: "center",
flexDirection: "column",
padding: 0,
position: "absolute",
right: "8px",
top: "8px",
}}
>
Serum Accounts
</Typography>
<IconButton onClick={onClose}>
<Close />
</IconButton>
</div>
@ -279,7 +274,9 @@ function OpenOrdersRow({
0;
const closeOpenOrders = async () => {
// todo
// TODO.
//
// Blocked by https://github.com/project-serum/serum-dex/pull/112.
};
return (

View File

@ -1,7 +1,7 @@
import { useState } from "react";
import { PublicKey } from "@solana/web3.js";
import { TokenListContainer } from "@solana/spl-token-registry";
import { Provider } from "@project-serum/anchor";
import { BN, Provider } from "@project-serum/anchor";
import { Swap as SwapClient } from "@project-serum/swap";
import {
makeStyles,
@ -13,7 +13,7 @@ import {
} from "@material-ui/core";
import { ExpandMore } from "@material-ui/icons";
import { SwapContextProvider, useSwapContext } from "./context/Swap";
import { DexContextProvider } from "./context/Dex";
import { DexContextProvider, useDexContext } from "./context/Dex";
import { MintContextProvider, useMint } from "./context/Mint";
import { TokenListContextProvider, useTokenList } from "./context/TokenList";
import { TokenContextProvider, useOwnedTokenAccount } from "./context/Token";
@ -251,10 +251,25 @@ function TokenName({ mint }: { mint: PublicKey }) {
function SwapButton() {
const styles = useStyles();
const { fromMint, toMint, fromAmount, slippage } = useSwapContext();
const { fromMint, toMint, fromAmount, toAmount, slippage } = useSwapContext();
const { swapClient } = useDexContext();
const fromMintInfo = useMint(fromMint);
const toMintInfo = useMint(toMint);
const sendSwapTransaction = async () => {
console.log("sending swap");
if (!fromMintInfo || !toMintInfo) {
throw new Error("Unable to calculate mint decimals");
}
const amount = new BN(fromAmount).muln(10 ** fromMintInfo.decimals);
const minExpectedSwapAmount = new BN(
(toAmount * (100 - slippage)) / 100
).muln(10 ** toMintInfo.decimals);
await swapClient.swap({
fromMint,
toMint,
amount,
minExpectedSwapAmount,
});
};
return (
<Button

View File

@ -229,10 +229,18 @@ export function useFairRoute(
[swapClient, fromMint, toMint]
);
const fromFair = useFair(route[0]);
const fromMarket = useMarket(route[0]);
const toFair = useFair(route[1]);
if (route.length === 1 && fromFair !== undefined) {
if (fromMarket === undefined) {
return undefined;
}
if (fromMarket?.baseMintAddress.equals(fromMint)) {
return 1.0 / fromFair;
} else {
return fromFair;
}
}
if (fromFair === undefined || toFair === undefined) {
return undefined;

View File

@ -3673,7 +3673,7 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
clsx@^1.0.4:
clsx@^1.0.4, clsx@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
@ -6004,7 +6004,7 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.3.2:
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -8328,6 +8328,14 @@ normalize-url@^3.0.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
notistack@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/notistack/-/notistack-1.0.7.tgz#1a5a3a8ba91d6ce9bd34cfd27eaff17b16c5d45c"
integrity sha512-D6pHvYgSGmS86r8KspQb4A75DXnIRrCdmUX79Gg8eJ1/I4IMDy0DULUqjytwEkQT02naeSnTLc9WkepLjHKQug==
dependencies:
clsx "^1.1.0"
hoist-non-react-statics "^3.3.0"
npm-run-all@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"