Execute swaps on mainnet
This commit is contained in:
parent
c3ebc2ac77
commit
5c3c0a5f25
|
@ -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",
|
||||
|
|
145
src/App.tsx
145
src/App.tsx
|
@ -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;
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
return 1.0 / fromFair;
|
||||
if (fromMarket === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (fromMarket?.baseMintAddress.equals(fromMint)) {
|
||||
return 1.0 / fromFair;
|
||||
} else {
|
||||
return fromFair;
|
||||
}
|
||||
}
|
||||
if (fromFair === undefined || toFair === undefined) {
|
||||
return undefined;
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue