Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Routes error on request pay #413

Merged
merged 6 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"format": "prettier --write ."
},
"dependencies": {
"@calcom/embed-react": "^1.5.1",
Expand Down
79 changes: 30 additions & 49 deletions src/components/Request/Pay/Views/Initial.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,9 @@ export const InitialView = ({
const { switchChainAsync } = useSwitchChain()
const { open } = useWeb3Modal()
const { setLoadingState, loadingState, isLoading } = useContext(context.loadingStateContext)
const {
selectedChainID,
setSelectedChainID,
selectedTokenAddress,
setSelectedTokenAddress,
selectedTokenDecimals,
isTokenPriceFetchingComplete,
setIsXChain,
} = useContext(context.tokenSelectorContext)
const { selectedTokenData, setSelectedChainID, setSelectedTokenAddress, isXChain, setIsXChain } = useContext(
context.tokenSelectorContext
)
const [errorState, setErrorState] = useState<{
showError: boolean
errorMessage: string
Expand All @@ -48,27 +42,31 @@ export const InitialView = ({
const [linkState, setLinkState] = useState<RequestStatus>(RequestStatus.NOT_CONNECTED)
const [estimatedFromValue, setEstimatedFromValue] = useState<string>('0')
const createXChainUnsignedTx = async () => {
// This function is only makes sense if selectedTokenData is defined
// Check that it is defined before calling this function
if (!selectedTokenData) {
throw new Error('selectedTokenData must be defined before estimating tx fee')
}

const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({
fromToken: selectedTokenAddress,
fromChainId: selectedChainID,
fromToken: selectedTokenData!.tokenAddress,
fromChainId: selectedTokenData!.chainId,
senderAddress: address ?? '',
link: requestLinkData.link,
squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route',
apiUrl: '/api/proxy/get',
provider: await peanut.getDefaultProvider(selectedChainID),
tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20,
fromTokenDecimals: selectedTokenDecimals as number,
provider: await peanut.getDefaultProvider(selectedTokenData!.chainId),
tokenType:
selectedTokenData!.tokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20,
fromTokenDecimals: selectedTokenData!.decimals as number,
})
return xchainUnsignedTxs
}

useEffect(() => {
const estimateTxFee = async () => {
setLinkState(RequestStatus.LOADING)
if (
selectedChainID === requestLinkData.chainId
&& utils.areTokenAddressesEqual(selectedTokenAddress, requestLinkData.tokenAddress)
) {
if (!isXChain) {
setErrorState({ showError: false, errorMessage: '' })
setIsFeeEstimationError(false)
setLinkState(RequestStatus.CLAIM)
Expand All @@ -80,6 +78,7 @@ export const InitialView = ({
const { feeEstimation, estimatedFromAmount } = txData
setEstimatedFromValue(estimatedFromAmount)
if (Number(feeEstimation) > 0) {
setErrorState({ showError: false, errorMessage: '' })
jjramirezn marked this conversation as resolved.
Show resolved Hide resolved
setIsFeeEstimationError(false)
setTxFee(Number(feeEstimation).toFixed(2))
setLinkState(RequestStatus.CLAIM)
Expand All @@ -97,26 +96,18 @@ export const InitialView = ({
}
}

const isXChain = selectedChainID !== requestLinkData.chainId
|| !utils.areTokenAddressesEqual(
selectedTokenAddress,
requestLinkData.tokenAddress
)
const isXChain =
selectedTokenData?.chainId !== requestLinkData.chainId ||
!utils.areTokenAddressesEqual(selectedTokenData?.tokenAddress, requestLinkData.tokenAddress)
setIsXChain(isXChain)

// wait for token selector to fetch token price, both effects depend on
// selectedTokenAddress and selectedChainID, but we depend on that
// effect being completed first
if (!isConnected || (isXChain && !isTokenPriceFetchingComplete)) return
if (!isConnected || (isXChain && !selectedTokenData)) return

estimateTxFee()
}, [
selectedTokenAddress,
selectedChainID,
selectedTokenDecimals,
isTokenPriceFetchingComplete,
requestLinkData
])
}, [isConnected, address, selectedTokenData, requestLinkData])

const handleConnectWallet = async () => {
open().finally(() => {
Expand Down Expand Up @@ -145,13 +136,10 @@ export const InitialView = ({
try {
setErrorState({ showError: false, errorMessage: '' })
if (!unsignedTx) return
if (
selectedChainID === requestLinkData.chainId
&& utils.areTokenAddressesEqual(selectedTokenAddress, requestLinkData.tokenAddress)
){
if (!isXChain) {
await checkUserHasEnoughBalance({ tokenValue: requestLinkData.tokenAmount })
if (selectedChainID !== String(currentChain?.id)) {
await switchNetwork(selectedChainID)
if (selectedTokenData?.chainId !== String(currentChain?.id)) {
await switchNetwork(selectedTokenData!.chainId)
}
setLoadingState('Sign in wallet')
const hash = await sendTransactions({
Expand Down Expand Up @@ -184,8 +172,8 @@ export const InitialView = ({
onNext()
} else {
await checkUserHasEnoughBalance({ tokenValue: estimatedFromValue })
if (selectedChainID !== String(currentChain?.id)) {
await switchNetwork(selectedChainID)
if (selectedTokenData!.chainId !== String(currentChain?.id)) {
await switchNetwork(selectedTokenData!.chainId)
}
setLoadingState('Sign in wallet')
const xchainUnsignedTxs = await createXChainUnsignedTx()
Expand Down Expand Up @@ -316,10 +304,7 @@ export const InitialView = ({
want to fulfill this request with.
</label>
</div>
<TokenSelector
classNameButton="w-full"
onReset={resetTokenAndChain}
/>
<TokenSelector classNameButton="w-full" onReset={resetTokenAndChain} />
<div className="flex w-full flex-col items-center justify-center gap-2">
{!isFeeEstimationError && (
<>
Expand All @@ -329,12 +314,8 @@ export const InitialView = ({
<label className="font-bold">Network cost</label>
</div>
<label className="flex flex-row items-center justify-center gap-1 text-center text-sm font-normal leading-4">
{requestLinkData.chainId === selectedChainID &&
requestLinkData.tokenAddress === selectedTokenAddress
? `$${utils.formatTokenAmount(estimatedGasCost, 3) ?? 0}`
: `$${txFee}`}
{requestLinkData.chainId === selectedChainID &&
requestLinkData.tokenAddress === selectedTokenAddress ? (
{!isXChain ? `$${utils.formatTokenAmount(estimatedGasCost, 3) ?? 0}` : `$${txFee}`}
{!isXChain ? (
<MoreInfo
text={
estimatedGasCost && estimatedGasCost > 0
Expand Down
31 changes: 20 additions & 11 deletions src/context/tokenSelector.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@ export const tokenSelectorContext = createContext({
refetchXchainRoute: false as boolean,
setRefetchXchainRoute: (value: boolean) => {},
resetTokenContextProvider: () => {},
isTokenPriceFetchingComplete: false as boolean,
isXChain: false as boolean,
setIsXChain: (value: boolean) => {},
selectedTokenData: undefined as ITokenData | undefined,
})

type ITokenData = {
tokenAddress: string
chainId: string
decimals: number
price: number
}

/**
* Context provider to manage the selected token and chain ID set in the tokenSelector. Token price is fetched here and input denomination can be set here too.
* It handles fetching token prices, updating context values, and resetting the provider based on user preferences and wallet connection status.
Expand All @@ -37,9 +44,8 @@ export const TokenContextProvider = ({ children }: { children: React.ReactNode }
const [inputDenomination, setInputDenomination] = useState<inputDenominationType>('TOKEN')
const [refetchXchainRoute, setRefetchXchainRoute] = useState<boolean>(false)
const [selectedTokenDecimals, setSelectedTokenDecimals] = useState<number | undefined>(18)
const [isTokenPriceFetchingComplete, setTokenPriceFetchingComplete] = useState<boolean>(false)
const [isXChain, setIsXChain] = useState<boolean>(false)

const [selectedTokenData, setSelectedTokenData] = useState<ITokenData | undefined>(undefined)

const { isConnected } = useAccount()
const preferences = utils.getPeanutPreferences()
Expand All @@ -66,6 +72,7 @@ export const TokenContextProvider = ({ children }: { children: React.ReactNode }
async function fetchAndSetTokenPrice(tokenAddress: string, chainId: string) {
jjramirezn marked this conversation as resolved.
Show resolved Hide resolved
try {
if (!consts.supportedMobulaChains.some((chain) => chain.chainId == chainId)) {
setSelectedTokenData(undefined)
setSelectedTokenPrice(undefined)
setSelectedTokenDecimals(undefined)
setInputDenomination('TOKEN')
Expand All @@ -78,13 +85,19 @@ export const TokenContextProvider = ({ children }: { children: React.ReactNode }
if (tokenPriceResponse?.price) {
setSelectedTokenPrice(tokenPriceResponse.price)
setSelectedTokenDecimals(tokenPriceResponse.decimals)
setTokenPriceFetchingComplete(true)
setSelectedTokenData({
tokenAddress,
chainId,
decimals: tokenPriceResponse.decimals,
price: tokenPriceResponse.price,
})
if (tokenPriceResponse.price === 1) {
setInputDenomination('TOKEN')
} else {
setInputDenomination('USD')
}
} else {
setSelectedTokenData(undefined)
setSelectedTokenPrice(undefined)
setSelectedTokenDecimals(undefined)
setInputDenomination('TOKEN')
Expand All @@ -95,22 +108,19 @@ export const TokenContextProvider = ({ children }: { children: React.ReactNode }
}
}


if (!isConnected) {
setSelectedTokenData(undefined)
setSelectedTokenPrice(undefined)
setSelectedTokenDecimals(undefined)
setInputDenomination('TOKEN')
return () => {
setTokenPriceFetchingComplete(false)
}
} else if (selectedTokenAddress && selectedChainID) {
setSelectedTokenData(undefined)
setSelectedTokenPrice(undefined)
setSelectedTokenDecimals(undefined)
setInputDenomination('TOKEN')
fetchAndSetTokenPrice(selectedTokenAddress, selectedChainID)
return () => {
jjramirezn marked this conversation as resolved.
Show resolved Hide resolved
isCurrent = false
setTokenPriceFetchingComplete(false)
}
}
}, [selectedTokenAddress, selectedChainID, isConnected])
Expand All @@ -121,7 +131,6 @@ export const TokenContextProvider = ({ children }: { children: React.ReactNode }
setSelectedTokenAddress(prefs.tokenAddress)
setSelectedChainID(prefs.chainId)
setSelectedTokenDecimals(prefs.decimals)
setTokenPriceFetchingComplete(true)
}
}, [])

Expand All @@ -141,9 +150,9 @@ export const TokenContextProvider = ({ children }: { children: React.ReactNode }
refetchXchainRoute,
setRefetchXchainRoute,
resetTokenContextProvider,
isTokenPriceFetchingComplete,
isXChain,
setIsXChain,
selectedTokenData,
}}
>
{children}
Expand Down