Execute swaps on mainnet
This commit is contained in:
parent
c3ebc2ac77
commit
5c3c0a5f25
|
@ -23,6 +23,7 @@
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
"bs58": "^4.0.1",
|
"bs58": "^4.0.1",
|
||||||
"material-ui-popup-state": "^1.8.3",
|
"material-ui-popup-state": "^1.8.3",
|
||||||
|
"notistack": "^1.0.7",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-async-hook": "^3.6.2",
|
"react-async-hook": "^3.6.2",
|
||||||
"react-dom": "^17.0.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 { useState, useEffect } from "react";
|
||||||
import { Typography } from "@material-ui/core";
|
import { SnackbarProvider, useSnackbar } from "notistack";
|
||||||
|
import { Button, Typography } from "@material-ui/core";
|
||||||
import { Provider } from "@project-serum/anchor";
|
import { Provider } from "@project-serum/anchor";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Wallet from "@project-serum/sol-wallet-adapter";
|
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 { TokenListProvider } from "@solana/spl-token-registry";
|
||||||
import Swap from "./components/Swap";
|
import Swap from "./components/Swap";
|
||||||
import "./App.css";
|
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() {
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -57,13 +36,99 @@ function App() {
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isConnected ? (
|
<SnackbarProvider maxSnack={5} autoHideDuration={8000}>
|
||||||
<Swap provider={params.provider} tokenList={params.tokenList} />
|
<AppInner />
|
||||||
) : (
|
</SnackbarProvider>
|
||||||
<Typography style={{ textAlign: "center" }}>Disconnected</Typography>
|
|
||||||
)}
|
|
||||||
</div>
|
</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;
|
export default App;
|
||||||
|
|
|
@ -188,25 +188,20 @@ export function SettingsDialog({
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginTop: "10px",
|
height: "24px",
|
||||||
paddingLeft: "24px",
|
|
||||||
paddingRight: "24px",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<IconButton
|
||||||
color="textSecondary"
|
onClick={onClose}
|
||||||
variant="h6"
|
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
padding: 0,
|
||||||
justifyContent: "center",
|
position: "absolute",
|
||||||
flexDirection: "column",
|
right: "8px",
|
||||||
|
top: "8px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Serum Accounts
|
|
||||||
</Typography>
|
|
||||||
<IconButton onClick={onClose}>
|
|
||||||
<Close />
|
<Close />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -279,7 +274,9 @@ function OpenOrdersRow({
|
||||||
0;
|
0;
|
||||||
|
|
||||||
const closeOpenOrders = async () => {
|
const closeOpenOrders = async () => {
|
||||||
// todo
|
// TODO.
|
||||||
|
//
|
||||||
|
// Blocked by https://github.com/project-serum/serum-dex/pull/112.
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { TokenListContainer } from "@solana/spl-token-registry";
|
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 { Swap as SwapClient } from "@project-serum/swap";
|
||||||
import {
|
import {
|
||||||
makeStyles,
|
makeStyles,
|
||||||
|
@ -13,7 +13,7 @@ import {
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { ExpandMore } from "@material-ui/icons";
|
import { ExpandMore } from "@material-ui/icons";
|
||||||
import { SwapContextProvider, useSwapContext } from "./context/Swap";
|
import { SwapContextProvider, useSwapContext } from "./context/Swap";
|
||||||
import { DexContextProvider } from "./context/Dex";
|
import { DexContextProvider, useDexContext } from "./context/Dex";
|
||||||
import { MintContextProvider, useMint } from "./context/Mint";
|
import { MintContextProvider, useMint } from "./context/Mint";
|
||||||
import { TokenListContextProvider, useTokenList } from "./context/TokenList";
|
import { TokenListContextProvider, useTokenList } from "./context/TokenList";
|
||||||
import { TokenContextProvider, useOwnedTokenAccount } from "./context/Token";
|
import { TokenContextProvider, useOwnedTokenAccount } from "./context/Token";
|
||||||
|
@ -251,10 +251,25 @@ function TokenName({ mint }: { mint: PublicKey }) {
|
||||||
|
|
||||||
function SwapButton() {
|
function SwapButton() {
|
||||||
const styles = useStyles();
|
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 () => {
|
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 (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -229,10 +229,18 @@ export function useFairRoute(
|
||||||
[swapClient, fromMint, toMint]
|
[swapClient, fromMint, toMint]
|
||||||
);
|
);
|
||||||
const fromFair = useFair(route[0]);
|
const fromFair = useFair(route[0]);
|
||||||
|
const fromMarket = useMarket(route[0]);
|
||||||
const toFair = useFair(route[1]);
|
const toFair = useFair(route[1]);
|
||||||
|
|
||||||
if (route.length === 1 && fromFair !== undefined) {
|
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) {
|
if (fromFair === undefined || toFair === undefined) {
|
||||||
return 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"
|
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||||
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
|
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
|
||||||
|
|
||||||
clsx@^1.0.4:
|
clsx@^1.0.4, clsx@^1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||||
|
@ -6004,7 +6004,7 @@ hmac-drbg@^1.0.1:
|
||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
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"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
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"
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
|
||||||
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
|
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:
|
npm-run-all@^4.1.5:
|
||||||
version "4.1.5"
|
version "4.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
|
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
|
||||||
|
|
Loading…
Reference in New Issue