diff --git a/package.json b/package.json index 7dfabfd..4013595 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@project-serum/swap-ui", - "version": "0.1.8", + "version": "0.1.9", "main": "dist/index.js", "types": "dist/index.d.ts", "homepage": "https://github.com/project-serum/swap-ui", @@ -8,7 +8,7 @@ "dependencies": { "@fontsource/roboto": "^4.3.0", "@project-serum/serum": "^0.13.34", - "@project-serum/swap": "^0.1.0-alpha.28", + "@project-serum/swap": "^0.1.0-alpha.31", "@solana/spl-token": "^0.1.4" }, "peerDependencies": { @@ -42,7 +42,7 @@ "@project-serum/anchor": "^0.7.0", "@project-serum/serum": "^0.13.34", "@project-serum/sol-wallet-adapter": "^0.2.0", - "@project-serum/swap": "^0.1.0-alpha.28", + "@project-serum/swap": "^0.1.0-alpha.31", "@solana/spl-token": "^0.1.4", "@solana/spl-token-registry": "^0.2.86", "@solana/web3.js": "^1.17.0", diff --git a/src/components/Swap.tsx b/src/components/Swap.tsx index 704d55a..8dd15b3 100644 --- a/src/components/Swap.tsx +++ b/src/components/Swap.tsx @@ -1,6 +1,13 @@ import { useState } from "react"; -import { PublicKey } from "@solana/web3.js"; -import { BN } from "@project-serum/anchor"; +import { + PublicKey, + Keypair, + Transaction, + SystemProgram, + Signer, +} from "@solana/web3.js"; +import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { BN, Provider } from "@project-serum/anchor"; import { makeStyles, Card, @@ -24,6 +31,7 @@ import { useCanSwap, useReferral } from "../context/Swap"; import TokenDialog from "./TokenDialog"; import { SettingsButton } from "./Settings"; import { InfoLabel } from "./Info"; +import { WRAPPED_SOL_MINT } from "../utils/pubkeys"; const useStyles = makeStyles((theme) => ({ card: { @@ -334,9 +342,11 @@ export function SwapButton() { const canSwap = useCanSwap(); const referral = useReferral(fromMarket); const fair = useSwapFair(); - const fromWallet = useOwnedTokenAccount(fromMint); - const toWallet = useOwnedTokenAccount(toMint); - const quoteMint = useMint(fromMarket && fromMarket.quoteMintAddress); + let fromWallet = useOwnedTokenAccount(fromMint); + let toWallet = useOwnedTokenAccount(toMint); + const quoteMint = fromMarket && fromMarket.quoteMintAddress; + const quoteMintInfo = useMint(quoteMint); + const quoteWallet = useOwnedTokenAccount(quoteMint); // Click handler. const sendSwapTransaction = async () => { @@ -346,41 +356,93 @@ export function SwapButton() { if (!fair) { throw new Error("Invalid fair"); } - if (!quoteMint) { + if (!quoteMint || !quoteMintInfo) { throw new Error("Quote mint not found"); } + + // All transactions to send for the swap. + let txs: { tx: Transaction; signers: Array }[] = []; const amount = new BN(fromAmount * 10 ** fromMintInfo.decimals); - const minExchangeRate = { - rate: new BN((10 ** toMintInfo.decimals * FEE_MULTIPLIER) / fair) - .muln(100 - slippage) - .divn(100), - fromDecimals: fromMintInfo.decimals, - quoteDecimals: quoteMint.decimals, - strict: isStrict, - }; - const fromOpenOrders = fromMarket - ? openOrders.get(fromMarket?.address.toString()) - : undefined; - const toOpenOrders = toMarket - ? openOrders.get(toMarket?.address.toString()) - : undefined; - await swapClient.swap({ - fromMint, - toMint, - fromWallet: fromWallet ? fromWallet.publicKey : undefined, - toWallet: toWallet ? toWallet.publicKey : undefined, - amount, - minExchangeRate, - referral, - // Pass in the below parameters so that the client doesn't perform - // wasteful network requests when we already have the data. - fromMarket, - toMarket, - fromOpenOrders: fromOpenOrders ? fromOpenOrders[0].address : undefined, - toOpenOrders: toOpenOrders ? toOpenOrders[0].address : undefined, - // Auto close newly created open orders accounts. - close: isClosingNewAccounts, - }); + + const isSol = + fromMint.equals(WRAPPED_SOL_MINT) || toMint.equals(WRAPPED_SOL_MINT); + const wrappedSolAccount = isSol ? Keypair.generate() : undefined; + + // Wrap the SOL into an SPL token. + if (isSol) { + txs.push( + await wrapSol( + swapClient.program.provider, + wrappedSolAccount as Keypair, + fromMint, + amount + ) + ); + } + + // Build the swap. + txs.push( + ...(await (async () => { + if (!fromMarket) { + throw new Error("Market undefined"); + } + + const minExchangeRate = { + rate: new BN((10 ** toMintInfo.decimals * FEE_MULTIPLIER) / fair) + .muln(100 - slippage) + .divn(100), + fromDecimals: fromMintInfo.decimals, + quoteDecimals: quoteMintInfo.decimals, + strict: isStrict, + }; + const fromOpenOrders = fromMarket + ? openOrders.get(fromMarket?.address.toString()) + : undefined; + const toOpenOrders = toMarket + ? openOrders.get(toMarket?.address.toString()) + : undefined; + const fromWalletAddr = fromMint.equals(WRAPPED_SOL_MINT) + ? wrappedSolAccount!.publicKey + : fromWallet + ? fromWallet.publicKey + : undefined; + const toWalletAddr = toMint.equals(WRAPPED_SOL_MINT) + ? wrappedSolAccount!.publicKey + : toWallet + ? toWallet.publicKey + : undefined; + + return await swapClient.swapTxs({ + fromMint, + toMint, + quoteMint, + amount, + minExchangeRate, + referral, + fromMarket, + toMarket, + // Automatically created if undefined. + fromOpenOrders: fromOpenOrders + ? fromOpenOrders[0].address + : undefined, + toOpenOrders: toOpenOrders ? toOpenOrders[0].address : undefined, + fromWallet: fromWalletAddr, + toWallet: toWalletAddr, + quoteWallet: quoteWallet ? quoteWallet.publicKey : undefined, + // Auto close newly created open orders accounts. + close: isClosingNewAccounts, + }); + })()) + ); + + // Unwrap the SOL. + if (isSol) { + txs.push( + unwrapSol(swapClient.program.provider, wrappedSolAccount as Keypair) + ); + } + + await swapClient.program.provider.sendAll(txs); }; return (