import React, { useContext, useState, useEffect } from "react"; import { Swap as SwapClient } from "@project-serum/swap"; import { PublicKey, Account } from "@solana/web3.js"; import { AccountInfo as TokenAccount, MintInfo, Token, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import { TokenListContainer, TokenInfo } from "@solana/spl-token-registry"; import { getOwnedTokenAccounts } from "../utils/tokens"; const SRM_MINT = new PublicKey("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const SwapContext = React.createContext(null); export function SwapContextProvider(props: any) { const swapClient = props.swapClient; const [fromMint, setFromMint] = useState(SRM_MINT); const [toMint, setToMint] = useState(USDC_MINT); const [fromAmount, setFromAmount] = useState(0); const [toAmount, setToAmount] = useState(0); const [fromBalance, setFromBalance] = useState(undefined); const [toBalance, setToBalance] = useState(undefined); const [minExpectedAmount, setMinExpectedAmount] = useState(0); const [ownedTokenAccounts, setOwnedTokenAccounts] = useState(undefined); const [mintCache, setMintCache] = useState(new Map()); // Fetch all the owned token accounts for the wallet. useEffect(() => { getOwnedTokenAccounts( swapClient.program.provider.connection, swapClient.program.provider.wallet.publicKey ).then(setOwnedTokenAccounts); }, [ swapClient.program.provider.wallet.publicKey, swapClient.program.provider.connection, ]); // Fetch the mint account infos not already in the cache. useEffect(() => { const fromMintClient = new Token( swapClient.program.provider.connection, fromMint, TOKEN_PROGRAM_ID, new Account() ); const toMintClient = new Token( swapClient.program.provider.connection, toMint, TOKEN_PROGRAM_ID, new Account() ); let promises = []; if (mintCache.get(fromMint.toString())) { promises.push( (async (): Promise => { return mintCache.get(fromMint.toString()) as MintInfo; })() ); } else { promises.push(fromMintClient.getMintInfo()); } if (mintCache.get(toMint.toString())) { promises.push( (async (): Promise => { return mintCache.get(toMint.toString()) as MintInfo; })() ); } else { promises.push(toMintClient.getMintInfo()); } Promise.all(promises as [Promise, Promise]).then( ([fromMintInfo, toMintInfo]: [MintInfo, MintInfo]) => { let cache = new Map(mintCache); cache.set(fromMint.toString(), fromMintInfo); cache.set(toMint.toString(), toMintInfo); setMintCache(cache); } ); }, [fromMint, toMint]); const swapToFromMints = () => { const oldFrom = fromMint; const oldFromAmount = fromAmount; const oldTo = toMint; const oldToAmount = toAmount; setFromMint(oldTo); setToMint(oldFrom); setFromAmount(oldToAmount); setToAmount(oldFromAmount); }; return ( {props.children} ); } export function useSwapContext(): SwapContext { const ctx = useContext(SwapContext); if (ctx === null) { throw new Error("Context not available"); } return ctx; } export type SwapContext = { swapClient: SwapClient; fromMint: PublicKey; setFromMint: (m: PublicKey) => void; toMint: PublicKey; setToMint: (m: PublicKey) => void; fromAmount: number; setFromAmount: (a: number) => void; toAmount: number; setToAmount: (a: number) => void; minExpectedAmount: number; swapToFromMints: () => void; fromBalance?: number; toBalance?: number; fromMintAccount?: MintInfo; toMintAccount?: MintInfo; ownedTokenAccounts: | { publicKey: PublicKey; account: TokenAccount }[] | undefined; mintCache: Map; }; const TokenListContext = React.createContext(null); export function TokenListContextProvider(props: any) { return ( {props.children} ); } type TokenListContext = { tokenList: TokenListContainer; }; export function useTokenList(): TokenInfo[] { const ctx = useContext(TokenListContext); if (ctx === null) { throw new Error("Context not available"); } return ctx.tokenList.getList(); } // Null => none exists. // Undefined => loading. export function useOwnedTokenAccount( mint: PublicKey ): { publicKey: PublicKey; account: TokenAccount } | null | undefined { const ctx = useContext(SwapContext); if (ctx === null) { throw new Error("Context not available"); } if (ctx.ownedTokenAccounts === undefined) { return undefined; } const tokenAccounts = ctx.ownedTokenAccounts.filter((account) => account.account.mint.equals(mint) ); if (tokenAccounts.length === 0) { return null; } // Take the account with the most tokens in it. tokenAccounts.sort((a, b) => a.account.amount < a.account.amount ? -1 : a.account.amount > b.account.amount ? 1 : 0 ); return tokenAccounts[0]; } export function useMintAccount(mint: PublicKey): MintInfo | undefined { const ctx = useContext(SwapContext); if (ctx === null) { throw new Error("Context not available"); } return ctx.mintCache.get(mint.toString()); }