From eac4ae3c8d8cf7b24e312b698efc3412a853373d Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Wed, 21 Oct 2020 10:53:11 -0700 Subject: [PATCH] Allow SPL WUSDC -> SPL USDC swaps --- package.json | 2 +- src/components/SendDialog.js | 102 +++++++++++++++++++++++++++-------- src/utils/wallet.js | 13 +++++ yarn.lock | 8 +-- 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 40df32f..c9b9f51 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dependencies": { "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", - "@project-serum/serum": "^0.13.5", + "@project-serum/serum": "^0.13.6", "@solana/web3.js": "^0.78.2", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", diff --git a/src/components/SendDialog.js b/src/components/SendDialog.js index a35cd71..0f8ec45 100644 --- a/src/components/SendDialog.js +++ b/src/components/SendDialog.js @@ -5,7 +5,7 @@ import DialogTitle from '@material-ui/core/DialogTitle'; import DialogContent from '@material-ui/core/DialogContent'; import TextField from '@material-ui/core/TextField'; import DialogForm from './DialogForm'; -import { useWallet } from '../utils/wallet'; +import { useWallet, useWalletAddressForMint } from '../utils/wallet'; import { PublicKey } from '@solana/web3.js'; import { abbreviateAddress } from '../utils/utils'; import InputAdornment from '@material-ui/core/InputAdornment'; @@ -29,9 +29,14 @@ import Typography from '@material-ui/core/Typography'; import { useAsyncData } from '../utils/fetch-loop'; import CircularProgress from '@material-ui/core/CircularProgress'; +const WUSDC_MINT = new PublicKey( + 'BXXkv6z8ykpG1yuvUDPgh732wzVHB69RnB9YgSYh3itW', +); +const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); + export default function SendDialog({ open, onClose, publicKey, balanceInfo }) { const isProdNetwork = useIsProdNetwork(); - const [tab, setTab] = useState(0); + const [tab, setTab] = useState('spl'); const onSubmitRef = useRef(); const [swapCoinInfo] = useSwapApiGet( @@ -62,23 +67,52 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) { textColor="primary" indicatorColor="primary" > - - + {mint?.equals(WUSDC_MINT) + ? [ + , + , + , + ] + : [ + , + , + ]} ) : null} - {tab === 0 ? ( + {tab === 'spl' ? ( + ) : tab === 'wusdcToSplUsdc' ? ( + ) : ( { @@ -170,19 +208,34 @@ function SendSwapDialog({ } }, [blockchain, ethAccount, setDestinationAddress]); + let splUsdcWalletAddress = useWalletAddressForMint( + wusdcToSplUsdc ? USDC_MINT : null, + ); + useEffect(() => { + if (wusdcToSplUsdc && splUsdcWalletAddress) { + setDestinationAddress(splUsdcWalletAddress); + } + }, [setDestinationAddress, wusdcToSplUsdc, splUsdcWalletAddress]); + async function makeTransaction() { let amount = Math.round(parseFloat(transferAmountString) * 10 ** decimals); if (!amount || amount <= 0) { throw new Error('Invalid amount'); } - const swapInfo = await swapApiRequest('POST', 'swap_to', { + const params = { blockchain, - coin: swapCoinInfo.erc20Contract, address: destinationAddress, size: amount / 10 ** decimals, - wusdcToUsdc: - mint?.toBase58() === 'BXXkv6z8ykpG1yuvUDPgh732wzVHB69RnB9YgSYh3itW', - }); + }; + if (blockchain === 'sol') { + params.coin = swapCoinInfo.splMint; + } else if (blockchain === 'eth') { + params.coin = swapCoinInfo.erc20Contract; + } + if (mint?.equals(WUSDC_MINT)) { + params.wusdcToUsdc = true; + } + const swapInfo = await swapApiRequest('POST', 'swap_to', params); if (swapInfo.blockchain !== 'sol') { throw new Error('Unexpected blockchain'); } @@ -205,6 +258,7 @@ function SendSwapDialog({ key={signature} publicKey={publicKey} signature={signature} + blockchain={blockchain} onClose={onClose} /> ); @@ -215,7 +269,11 @@ function SendSwapDialog({ SPL {tokenName} can be converted to{' '} - {swapCoinInfo.erc20Contract ? 'ERC20' : 'native'}{' '} + {blockchain === 'eth' && swapCoinInfo.erc20Contract + ? 'ERC20' + : blockchain === 'sol' && swapCoinInfo.splMint + ? 'SPL' + : 'native'}{' '} {swapCoinInfo.ticker} {needMetamask ? ' via MetaMask' : null}. @@ -235,7 +293,7 @@ function SendSwapDialog({ ); } -function SendSwapProgress({ publicKey, signature, onClose }) { +function SendSwapProgress({ publicKey, signature, onClose, blockchain }) { const connection = useConnection(); const [swaps] = useSwapApiGet(`swaps_from/sol/${publicKey.toBase58()}`, { refreshInterval: 1000, @@ -257,6 +315,8 @@ function SendSwapProgress({ publicKey, signature, onClose }) { if (withdrawal.txid?.startsWith('0x')) { step = 3; ethTxid = withdrawal.txid; + } else if (withdrawal.txid && blockchain !== 'eth') { + step = 3; } else { step = 2; } @@ -287,7 +347,7 @@ function SendSwapProgress({ publicKey, signature, onClose }) { View on Etherscan - ) : ( + ) : step < 3 ? (
Transaction Pending )}
- )} - {!ethTxid ? ( + ) : null} + {!ethTxid && blockchain === 'eth' ? ( Please keep this window open. You will need to approve the request on MetaMask to complete the transaction. diff --git a/src/utils/wallet.js b/src/utils/wallet.js index 0d1e2e1..b0a5cef 100644 --- a/src/utils/wallet.js +++ b/src/utils/wallet.js @@ -165,6 +165,19 @@ export function refreshWalletPublicKeys(wallet) { refreshCache(wallet.getTokenAccountInfo); } +export function useWalletAddressForMint(mint) { + const [walletAccounts] = useWalletTokenAccounts(); + return useMemo( + () => + mint + ? walletAccounts + ?.find((account) => account.parsed?.mint?.equals(mint)) + ?.publicKey.toBase58() + : null, + [walletAccounts, mint], + ); +} + export function useBalanceInfo(publicKey) { let [accountInfo, accountInfoLoaded] = useAccountInfo(publicKey); let { mint, owner, amount } = accountInfo?.owner.equals(TOKEN_PROGRAM_ID) diff --git a/yarn.lock b/yarn.lock index dc61433..a2d3d8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1578,10 +1578,10 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@project-serum/serum@^0.13.5": - version "0.13.5" - resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.5.tgz#9e8591886b3fc7f2486a94c242e5728d97da0938" - integrity sha512-qMnxYKR/z8pqN+LFUIPRFwdSsJ1bGD39Jy6nooQl+aFp+ZpS8+hjIsJQGjpJA4kf+H+U8v6kcMhgMJ6diAWUHw== +"@project-serum/serum@^0.13.6": + version "0.13.6" + resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.6.tgz#d45fa52a31d2820d19030964fd3a418e87b8a1f9" + integrity sha512-DoiqJABmGKWvrOLI0/ZctnXlzsPxIiKVHeIsFua3ODXqFWSDojUQDs8y1Rd26wFPEqB6Vsq1Xq8sqsku1leziQ== dependencies: "@solana/web3.js" "^0.71.10" bn.js "^5.1.2"