Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/stupid-experts-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Requery allowances when getting back to quote screen
29 changes: 2 additions & 27 deletions packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type { Hash } from "viem";
import { getCachedChain } from "../../chains/utils.js";
import type { ThirdwebClient } from "../../client/client.js";
import { getContract } from "../../contract/contract.js";
import { allowance } from "../../extensions/erc20/__generated__/IERC20/read/allowance.js";
import { approve } from "../../extensions/erc20/write/approve.js";
import type { PrepareTransactionOptions } from "../../transaction/prepare-transaction.js";
import { getClientFetch } from "../../utils/fetch.js";
import { stringify } from "../../utils/json.js";
Expand Down Expand Up @@ -151,7 +148,7 @@
*/
export type BuyWithCryptoQuote = {
transactionRequest: PrepareTransactionOptions;
approval?: PrepareTransactionOptions;
approvalData?: QuoteApprovalInfo;

swapDetails: {
fromAddress: string;
Expand Down Expand Up @@ -254,28 +251,6 @@

// check if the fromAddress already has approval for the given amount
const approvalData = data.approval;
let approval = undefined;
if (approvalData) {
const contract = getContract({
client: params.client,
address: approvalData.tokenAddress,
chain: getCachedChain(approvalData.chainId),
});

const approvedAmount = await allowance({
contract,
spender: approvalData.spenderAddress,
owner: params.fromAddress,
});

if (approvedAmount < BigInt(approvalData.amountWei)) {
approval = approve({
contract,
spender: approvalData.spenderAddress,
amountWei: BigInt(approvalData.amountWei),
});
}
}

const swapRoute: BuyWithCryptoQuote = {
transactionRequest: {
Expand All @@ -287,7 +262,7 @@
gas: BigInt(data.transactionRequest.gasLimit),
gasPrice: undefined, // ignore gas price returned by the quote, we handle it ourselves
},
approval: approval,
approvalData,

Check warning on line 265 in packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts#L265

Added line #L265 was not covered by tests
swapDetails: {
fromAddress: data.fromAddress,
toAddress: data.toAddress,
Expand Down
16 changes: 2 additions & 14 deletions packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { Hash } from "viem";
import { getCachedChain } from "../../chains/utils.js";
import type { ThirdwebClient } from "../../client/client.js";
import { getContract } from "../../contract/contract.js";
import { approve } from "../../extensions/erc20/write/approve.js";
import type { PrepareTransactionOptions } from "../../transaction/prepare-transaction.js";
import type { Address } from "../../utils/address.js";
import { getClientFetch } from "../../utils/fetch.js";
Expand Down Expand Up @@ -83,7 +81,7 @@
*/
export type BuyWithCryptoTransfer = {
transactionRequest: PrepareTransactionOptions;
approval?: PrepareTransactionOptions;
approvalData?: QuoteApprovalInfo;
fromAddress: string;
toAddress: string;
paymentToken: QuotePaymentToken;
Expand Down Expand Up @@ -159,17 +157,7 @@
value: BigInt(data.transactionRequest.value),
gas: BigInt(data.transactionRequest.gasLimit),
},
approval: data.approval
? approve({
contract: getContract({
client: params.client,
address: data.approval.tokenAddress,
chain: getCachedChain(data.approval.chainId),
}),
spender: data.approval.spenderAddress as Address,
amountWei: BigInt(data.approval.amountWei),
})
: undefined,
approvalData: data.approval,

Check warning on line 160 in packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts#L160

Added line #L160 was not covered by tests
fromAddress: data.fromAddress,
toAddress: data.toAddress,
paymentToken: data.paymentToken,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useQueryClient } from "@tanstack/react-query";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useMemo, useState } from "react";
import type { Chain } from "../../../../../../chains/types.js";
import { getCachedChain } from "../../../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../constants/addresses.js";
import { getContract } from "../../../../../../contract/contract.js";
import { allowance } from "../../../../../../extensions/erc20/__generated__/IERC20/read/allowance.js";
import type { GetBuyWithCryptoQuoteParams } from "../../../../../../pay/buyWithCrypto/getQuote.js";
import type { BuyWithCryptoStatus } from "../../../../../../pay/buyWithCrypto/getStatus.js";
import type { BuyWithFiatStatus } from "../../../../../../pay/buyWithFiat/getStatus.js";
Expand Down Expand Up @@ -322,6 +325,7 @@
});
}}
onSuccess={onSwapSuccess}
approvalAmount={screen.approvalAmount}

Check warning on line 328 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L328

Added line #L328 was not covered by tests
/>
);
}
Expand Down Expand Up @@ -992,6 +996,30 @@
gcTime: 30 * 1000,
});

const allowanceQuery = useQuery({
queryKey: [
"allowance",
payer.account.address,
quoteQuery.data?.approvalData,
],
queryFn: () => {
if (!quoteQuery.data?.approvalData) {
return null;
}
return allowance({
contract: getContract({
client: props.client,
address: quoteQuery.data.swapDetails.fromToken.tokenAddress,
chain: getCachedChain(quoteQuery.data.swapDetails.fromToken.chainId),
}),
spender: quoteQuery.data.approvalData.spenderAddress,
owner: props.payer.account.address,
});
},
enabled: !!quoteQuery.data?.approvalData,
refetchOnMount: true,
});

Check warning on line 1021 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L999-L1021

Added lines #L999 - L1021 were not covered by tests

const sourceTokenAmount = swapRequired
? quoteQuery.data?.swapDetails.fromAmount
: tokenAmount;
Expand All @@ -1002,7 +1030,9 @@
Number(fromTokenBalanceQuery.data.displayValue) < Number(sourceTokenAmount);

const disableContinue =
(swapRequired && !quoteQuery.data) || isNotEnoughBalance;
(swapRequired && !quoteQuery.data) ||
isNotEnoughBalance ||
allowanceQuery.isLoading;

Check warning on line 1035 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L1033-L1035

Added lines #L1033 - L1035 were not covered by tests
const switchChainRequired =
props.payer.wallet.getChain()?.id !== fromChain.id;

Expand Down Expand Up @@ -1047,6 +1077,7 @@
setScreen({
id: "swap-flow",
quote: quoteQuery.data,
approvalAmount: allowanceQuery.data ?? undefined,

Check warning on line 1080 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L1080

Added line #L1080 was not covered by tests
});
}

Expand Down Expand Up @@ -1155,6 +1186,7 @@
</Button>
) : switchChainRequired &&
!quoteQuery.isLoading &&
!allowanceQuery.isLoading &&

Check warning on line 1189 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L1189

Added line #L1189 was not covered by tests
!isNotEnoughBalance &&
!quoteQuery.error ? (
<SwitchNetworkButton
Expand Down Expand Up @@ -1493,7 +1525,7 @@
const defaultMessage = "Unable to get price quote";
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
function getErrorMessage(err: any): ApiError {
if (typeof err.error === "object") {
if (typeof err.error === "object" && err.error.code) {

Check warning on line 1528 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L1528

Added line #L1528 was not covered by tests
return err.error;
}
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { getCachedChain } from "../../../../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
import { getContract } from "../../../../../../../contract/contract.js";
import { allowance } from "../../../../../../../extensions/erc20/__generated__/IERC20/read/allowance.js";
import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
import type { BuyWithCryptoStatus } from "../../../../../../../pay/buyWithCrypto/getStatus.js";
import { getPostOnRampQuote } from "../../../../../../../pay/buyWithFiat/getPostOnRampQuote.js";
Expand Down Expand Up @@ -43,18 +46,46 @@
refetchOnWindowFocus: false,
});

const allowanceQuery = useQuery({
queryKey: [
"allowance",
props.payer.account.address,
postOnRampQuoteQuery.data?.approvalData,
],
queryFn: () => {
if (!postOnRampQuoteQuery.data?.approvalData) {
return null;
}
return allowance({
contract: getContract({
client: props.client,
address: postOnRampQuoteQuery.data.swapDetails.fromToken.tokenAddress,
chain: getCachedChain(
postOnRampQuoteQuery.data.swapDetails.fromToken.chainId,
),
}),
spender: postOnRampQuoteQuery.data.approvalData.spenderAddress,
owner: props.payer.account.address,
});
},
enabled: !!postOnRampQuoteQuery.data?.approvalData,
refetchOnMount: true,
});

Check warning on line 73 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx#L49-L73

Added lines #L49 - L73 were not covered by tests

useEffect(() => {
if (
postOnRampQuoteQuery.data &&
!lockedOnRampQuote &&
!postOnRampQuoteQuery.isRefetching
!postOnRampQuoteQuery.isRefetching &&
!allowanceQuery.isLoading

Check warning on line 80 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx#L79-L80

Added lines #L79 - L80 were not covered by tests
) {
setLockedOnRampQuote(postOnRampQuoteQuery.data);
}
}, [
postOnRampQuoteQuery.data,
lockedOnRampQuote,
postOnRampQuoteQuery.isRefetching,
allowanceQuery.isLoading,

Check warning on line 88 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx#L88

Added line #L88 was not covered by tests
]);

if (postOnRampQuoteQuery.isError) {
Expand Down Expand Up @@ -133,6 +164,7 @@
transactionMode={props.transactionMode}
isEmbed={props.isEmbed}
onSuccess={props.onSuccess}
approvalAmount={allowanceQuery.data ?? undefined}

Check warning on line 167 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx#L167

Added line #L167 was not covered by tests
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type SelectedScreen =
| {
id: "swap-flow";
quote: BuyWithCryptoQuote;
approvalAmount?: bigint;
}
| {
id: "fiat-flow";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { trackPayEvent } from "../../../../../../../analytics/track/pay.js";
import type { Chain } from "../../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
import { getContract } from "../../../../../../../contract/contract.js";
import { approve } from "../../../../../../../extensions/erc20/write/approve.js";
import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
import { sendTransaction } from "../../../../../../../transaction/actions/send-transaction.js";
import { waitForReceipt } from "../../../../../../../transaction/actions/wait-for-tx-receipt.js";
Expand Down Expand Up @@ -51,9 +53,13 @@
fromTokenSymbol: string;
isFiatFlow: boolean;
payer: PayerInfo;
preApprovedAmount?: bigint;
}) {
const isApprovalRequired = props.quote.approval !== undefined;
const initialStep = isApprovalRequired ? "approval" : "swap";
const needsApproval =
props.quote.approvalData &&
props.preApprovedAmount !== undefined &&
props.preApprovedAmount < BigInt(props.quote.approvalData.amountWei);
const initialStep = needsApproval ? "approval" : "swap";

Check warning on line 62 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx#L58-L62

Added lines #L58 - L62 were not covered by tests

const [step, setStep] = useState<"approval" | "swap">(initialStep);
const [status, setStatus] = useState<
Expand Down Expand Up @@ -136,7 +142,7 @@
<Spacer y="xl" />

{/* Show 2 steps - Approve and confirm */}
{isApprovalRequired && (
{needsApproval && (

Check warning on line 145 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx#L145

Added line #L145 was not covered by tests
<>
<Spacer y="sm" />
<Container
Expand Down Expand Up @@ -187,7 +193,7 @@
fullWidth
disabled={status === "pending"}
onClick={async () => {
if (step === "approval" && props.quote.approval) {
if (step === "approval" && props.quote.approvalData) {

Check warning on line 196 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx#L196

Added line #L196 was not covered by tests
try {
setStatus("pending");

Expand All @@ -204,13 +210,22 @@
dstChainId: props.quote.swapDetails.toToken.chainId,
});

const transaction = approve({
contract: getContract({
client: props.client,
address: props.quote.swapDetails.fromToken.tokenAddress,
chain: props.fromChain,
}),
spender: props.quote.approvalData.spenderAddress,
amountWei: BigInt(props.quote.approvalData.amountWei),
});

Check warning on line 221 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx#L213-L221

Added lines #L213 - L221 were not covered by tests

const tx = await sendTransaction({
account: props.payer.account,
transaction: props.quote.approval,
transaction,

Check warning on line 225 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx#L225

Added line #L225 was not covered by tests
});

await waitForReceipt({ ...tx, maxBlocksWaitTime: 50 });
// props.onQuoteFinalized(props.quote);

trackPayEvent({
event: "swap_approval_success",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
transactionMode: boolean;
isEmbed: boolean;
onSuccess: ((status: BuyWithCryptoStatus) => void) | undefined;
approvalAmount?: bigint;
};

export function SwapFlow(props: SwapFlowProps) {
Expand Down Expand Up @@ -109,6 +110,7 @@
quote={quote}
isFiatFlow={props.isFiatFlow}
payer={props.payer}
preApprovedAmount={props.approvalAmount}

Check warning on line 113 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.tsx#L113

Added line #L113 was not covered by tests
/>
);
}
Loading
Loading