import * as assert from "assert" ; import React , { useContext , useState , useEffect } from "react" ; import { useAsync } from "react-async-hook" ; import { PublicKey } from "@solana/web3.js" ; import { Token , ASSOCIATED_TOKEN_PROGRAM_ID , TOKEN_PROGRAM_ID , } from "@solana/spl-token" ; import { Market } from "@project-serum/serum" ; import { SRM_MINT , USDC_MINT , USDT_MINT } from "../utils/pubkeys" ; import { useFairRoute , useRouteVerbose , useDexContext , FEE_MULTIPLIER , } from "./Dex" ; import { useTokenListContext , SPL_REGISTRY_SOLLET_TAG , SPL_REGISTRY_WORM_TAG , } from "./TokenList" ; import { useOwnedTokenAccount } from "../context/Token" ; const DEFAULT_SLIPPAGE_PERCENT = 0.5 ; export type SwapContext = { fromMint : PublicKey ; setFromMint : ( m : PublicKey ) => void ; toMint : PublicKey ; setToMint : ( m : PublicKey ) => void ; fromAmount : number ; setFromAmount : ( a : number ) => void ; toAmount : number ; setToAmount : ( a : number ) => void ; swapToFromMints : ( ) => void ; slippage : number ; setSlippage : ( n : number ) => void ; fairOverride : number | null ; setFairOverride : ( n : number | null ) => void ; referral ? : PublicKey ; isClosingNewAccounts : boolean ; isStrict : boolean ; setIsStrict : ( isStrict : boolean ) => void ; setIsClosingNewAccounts : ( b : boolean ) => void ; } ; const _SwapContext = React . createContext < null | SwapContext > ( null ) ; export function SwapContextProvider ( props ) { const [ fromMint , setFromMint ] = useState ( props . fromMint ?? SRM_MINT ) ; const [ toMint , setToMint ] = useState ( props . toMint ?? USDC_MINT ) ; const [ fromAmount , _setFromAmount ] = useState ( props . fromAmount ?? 0 ) ; const [ toAmount , _setToAmount ] = useState ( props . toAmount ?? 0 ) ; const [ isClosingNewAccounts , setIsClosingNewAccounts ] = useState ( false ) ; const [ isStrict , setIsStrict ] = useState ( false ) ; const [ slippage , setSlippage ] = useState ( DEFAULT_SLIPPAGE_PERCENT ) ; const [ fairOverride , setFairOverride ] = useState < number | null > ( null ) ; const fair = _useSwapFair ( fromMint , toMint , fairOverride ) ; const referral = props . referral ; assert . ok ( slippage >= 0 ) ; useEffect ( ( ) => { if ( ! fair ) { return ; } setFromAmount ( fromAmount ) ; } , [ fair ] ) ; const swapToFromMints = ( ) => { const oldFrom = fromMint ; const oldTo = toMint ; const oldToAmount = toAmount ; _setFromAmount ( oldToAmount ) ; setFromMint ( oldTo ) ; setToMint ( oldFrom ) ; } ; const setFromAmount = ( amount ) => { if ( fair === undefined ) { _setFromAmount ( 0 ) ; _setToAmount ( 0 ) ; return ; } _setFromAmount ( amount ) ; _setToAmount ( FEE_MULTIPLIER * ( amount / fair ) ) ; } ; const setToAmount = ( amount ) => { if ( fair === undefined ) { _setFromAmount ( 0 ) ; _setToAmount ( 0 ) ; return ; } _setToAmount ( amount ) ; _setFromAmount ( ( amount * fair ) / FEE_MULTIPLIER ) ; } ; return ( < _SwapContext . Provider value = { { fromMint , setFromMint , toMint , setToMint , fromAmount , setFromAmount , toAmount , setToAmount , swapToFromMints , slippage , setSlippage , fairOverride , setFairOverride , isClosingNewAccounts , isStrict , setIsStrict , setIsClosingNewAccounts , referral , } } > { props . children } < /_SwapContext.Provider>); } export function useSwapContext ( ) { const ctx = useContext ( _SwapContext ) ; if ( ctx === null ) { throw new Error ( "Context not available" ) ; } return ctx ; } export function useSwapFair ( ) { const { fairOverride , fromMint , toMint } = useSwapContext ( ) ; return _useSwapFair ( fromMint , toMint , fairOverride ) ; } function _useSwapFair ( fromMint , toMint , fairOverride ) { const fairRoute = useFairRoute ( fromMint , toMint ) ; const fair = fairOverride === null ? fairRoute : fairOverride ; return fair ; } export function useCanSwap ( ) { const { fromMint , toMint , fromAmount , toAmount } = useSwapContext ( ) ; const { swapClient } = useDexContext ( ) ; const { wormholeMap , solletMap } = useTokenListContext ( ) ; const fromWallet = useOwnedTokenAccount ( fromMint ) ; const fair = useSwapFair ( ) ; const route = useRouteVerbose ( fromMint , toMint ) ; if ( route === null ) { return false ; } return ( fromWallet !== undefined && fromWallet !== null && fair !== undefined && fair > 0 && fromMint . equals ( toMint ) === false && swapClient . program . provider . wallet . publicKey !== null && fromAmount > 0 && toAmount > 0 && route !== null && ( route . kind !== "wormhole-native" || wormholeMap . get ( fromMint . toString ( ) ) ?. tags ?. includes ( SPL_REGISTRY_WORM_TAG ) !== undefined ) && ( route . kind !== "wormhole-sollet" || solletMap . get ( fromMint . toString ( ) ) ?. tags ?. includes ( SPL_REGISTRY_SOLLET_TAG ) !== undefined ) ) ; } export function useReferral ( fromMarket ? ) { const { referral } = useSwapContext ( ) ; const asyncReferral = useAsync ( async ( ) => { if ( ! referral ) { return undefined ; } if ( ! fromMarket ) { return undefined ; } if ( ! fromMarket . quoteMintAddress . equals ( USDC_MINT ) && ! fromMarket . quoteMintAddress . equals ( USDT_MINT ) ) { return undefined ; } return Token . getAssociatedTokenAddress ( ASSOCIATED_TOKEN_PROGRAM_ID , TOKEN_PROGRAM_ID , fromMarket . quoteMintAddress , referral ) ; } , [ fromMarket ] ) ; if ( ! asyncReferral . result ) { return undefined ; } return asyncReferral . result ; }