Skip to content

Commit d623978

Browse files
joaquim-vergesgregfromstlclaudekumaryash90d4mr
authored
[SDK] Always show pay modal even on non-supported UB chains (#7092)
Signed-off-by: samina <57885104+saminacodes@users.noreply.github.com> Signed-off-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: gregfromstl <gregfromstl@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: kumaryash90 <kumaryashcse@gmail.com> Co-authored-by: Prithvish Baidya <deformercoding@gmail.com> Co-authored-by: samina <57885104+saminacodes@users.noreply.github.com> Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Jonas Daniels <jonas.daniels@outlook.com> Co-authored-by: arcoraven <arcoraven@gmail.com>
1 parent 20fdd5b commit d623978

File tree

12 files changed

+435
-68
lines changed

12 files changed

+435
-68
lines changed

.changeset/sharp-symbols-unite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Show deposit modal for tokens that don't have any UB routes

apps/playground-web/src/components/pay/transaction-button.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import { useTheme } from "next-themes";
44
import { getContract } from "thirdweb";
5-
import { base, polygon } from "thirdweb/chains";
5+
import { prepareTransaction } from "thirdweb";
6+
import { base, baseSepolia, polygon } from "thirdweb/chains";
67
import { transfer } from "thirdweb/extensions/erc20";
78
import { claimTo, getNFT } from "thirdweb/extensions/erc1155";
89
import {
@@ -12,6 +13,7 @@ import {
1213
useActiveAccount,
1314
useReadContract,
1415
} from "thirdweb/react";
16+
import { toWei } from "thirdweb/utils";
1517
import { THIRDWEB_CLIENT } from "../../lib/client";
1618
import { StyledConnectButton } from "../styled-connect-button";
1719

@@ -99,6 +101,27 @@ export function PayTransactionButtonPreview() {
99101
>
100102
Buy VIP Pass
101103
</TransactionButton>
104+
<div className="h-10" />
105+
<div className="flex items-center gap-2">Price: 0.1 ETH</div>
106+
<TransactionButton
107+
transaction={() => {
108+
if (!account) throw new Error("No active account");
109+
return prepareTransaction({
110+
chain: baseSepolia,
111+
client: THIRDWEB_CLIENT,
112+
to: account.address,
113+
value: toWei("0.1"),
114+
});
115+
}}
116+
onError={(e) => {
117+
console.error(e);
118+
}}
119+
payModal={{
120+
theme: theme === "light" ? "light" : "dark",
121+
}}
122+
>
123+
Send 0.1 ETH
124+
</TransactionButton>
102125
</div>
103126
)}
104127
</>

apps/portal/src/app/pay/guides/build-a-custom-experience/page.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ The `quote` object contains detailed transaction information including the estim
9494

9595
</Step>
9696
<Step title='Check for a Buy With Crypto Step'>
97-
The `quote` object contains `quote.onRampToken` and `quote.toToken` objects containing intermediate and detination token information.
97+
The `quote` object contains `quote.onRampToken` and `quote.toToken` objects containing intermediate and destination token information.
9898

9999
If `quote.onRampToken` is not the same as `quote.toToken`, then your users will need to onramp to intermediary token before arriving at their destination token.
100100

packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export type SendTransactionConfig = {
104104
};
105105

106106
export type ShowModalData = {
107+
mode: "buy" | "deposit";
107108
tx: PreparedTransaction;
108109
sendTx: () => void;
109110
rejectTx: (reason: Error) => void;
@@ -184,47 +185,6 @@ export function useSendTransactionCore(args: {
184185
resolvePromisedValue(tx.erc20Value),
185186
]);
186187

187-
const supportedDestinations = await Bridge.routes({
188-
client: tx.client,
189-
destinationChainId: tx.chain.id,
190-
destinationTokenAddress: _erc20Value?.tokenAddress,
191-
}).catch((err) => {
192-
trackPayEvent({
193-
client: tx.client,
194-
walletAddress: account.address,
195-
walletType: wallet?.id,
196-
toChainId: tx.chain.id,
197-
event: "pay_transaction_modal_pay_api_error",
198-
error: err?.message,
199-
});
200-
return null;
201-
});
202-
203-
if (!supportedDestinations) {
204-
// could not fetch supported destinations, just send the tx
205-
sendTx();
206-
return;
207-
}
208-
209-
if (supportedDestinations.length === 0) {
210-
trackPayEvent({
211-
client: tx.client,
212-
walletAddress: account.address,
213-
walletType: wallet?.id,
214-
toChainId: tx.chain.id,
215-
toToken: _erc20Value?.tokenAddress || undefined,
216-
event: "pay_transaction_modal_chain_token_not_supported",
217-
error: JSON.stringify({
218-
chain: tx.chain.id,
219-
token: _erc20Value?.tokenAddress,
220-
message: "chain/token not supported",
221-
}),
222-
});
223-
// chain/token not supported, just send the tx
224-
sendTx();
225-
return;
226-
}
227-
228188
const nativeValue = _nativeValue || 0n;
229189
const erc20Value = _erc20Value?.amountWei || 0n;
230190

@@ -256,7 +216,54 @@ export function useSendTransactionCore(args: {
256216
(nativeCost > 0n && nativeBalance.value < nativeCost);
257217

258218
if (shouldShowModal) {
219+
const supportedDestinations = await Bridge.routes({
220+
client: tx.client,
221+
destinationChainId: tx.chain.id,
222+
destinationTokenAddress: _erc20Value?.tokenAddress,
223+
}).catch((err) => {
224+
trackPayEvent({
225+
client: tx.client,
226+
walletAddress: account.address,
227+
walletType: wallet?.id,
228+
toChainId: tx.chain.id,
229+
event: "pay_transaction_modal_pay_api_error",
230+
error: err?.message,
231+
});
232+
return null;
233+
});
234+
235+
if (
236+
!supportedDestinations ||
237+
supportedDestinations.length === 0
238+
) {
239+
// not a supported destination -> show deposit screen
240+
trackPayEvent({
241+
client: tx.client,
242+
walletAddress: account.address,
243+
walletType: wallet?.id,
244+
toChainId: tx.chain.id,
245+
toToken: _erc20Value?.tokenAddress || undefined,
246+
event: "pay_transaction_modal_chain_token_not_supported",
247+
error: JSON.stringify({
248+
chain: tx.chain.id,
249+
token: _erc20Value?.tokenAddress,
250+
message: "chain/token not supported",
251+
}),
252+
});
253+
254+
showPayModal({
255+
mode: "deposit",
256+
tx,
257+
sendTx,
258+
rejectTx: reject,
259+
resolveTx: resolve,
260+
});
261+
return;
262+
}
263+
264+
// chain is supported, show buy mode
259265
showPayModal({
266+
mode: "buy",
260267
tx,
261268
sendTx,
262269
rejectTx: reject,

packages/thirdweb/src/react/core/utils/wallet.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,21 @@ export function hasSponsoredTransactionsEnabled(wallet: Wallet | undefined) {
244244
sponsoredTransactionsEnabled = smartOptions.gasless;
245245
}
246246
}
247+
if (options?.executionMode) {
248+
const execMode = options.executionMode;
249+
if (execMode.mode === "EIP4337") {
250+
const smartOptions = execMode.smartAccount;
251+
if (smartOptions && "sponsorGas" in smartOptions) {
252+
sponsoredTransactionsEnabled = smartOptions.sponsorGas;
253+
}
254+
if (smartOptions && "gasless" in smartOptions) {
255+
sponsoredTransactionsEnabled = smartOptions.gasless;
256+
}
257+
}
258+
if (execMode.mode === "EIP7702") {
259+
sponsoredTransactionsEnabled = execMode.sponsorGas || false;
260+
}
261+
}
247262
}
248263
return sponsoredTransactionsEnabled;
249264
}

packages/thirdweb/src/react/web/hooks/transaction/useSendTransaction.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export function useSendTransaction(config: SendTransactionConfig = {}) {
121121
localeId={payModal?.locale || "en_US"}
122122
supportedTokens={payModal?.supportedTokens}
123123
theme={payModal?.theme || "dark"}
124+
modalMode={data.mode}
124125
payOptions={{
125126
buyWithCrypto: payModal?.buyWithCrypto,
126127
buyWithFiat: payModal?.buyWithFiat,

packages/thirdweb/src/react/web/ui/ConnectWallet/locale/en.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const connectLocaleEn: ConnectLocale = {
6868
},
6969
receiveFundsScreen: {
7070
title: "Receive Funds",
71-
instruction: "Copy the wallet address to send funds to this wallet",
71+
instruction: "Copy the address to send funds to this wallet",
7272
},
7373
sendFundsScreen: {
7474
title: "Send Funds",

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ChainMetadata } from "../../../../../../../chains/types.js";
12
import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
23
import type { BuyWithFiatQuote } from "../../../../../../../pay/buyWithFiat/getQuote.js";
34
import type { GetWalletBalanceResult } from "../../../../../../../wallets/utils/getWalletBalance.js";
@@ -9,6 +10,7 @@ export type TransactionCostAndData = {
910
walletBalance: GetWalletBalanceResult;
1011
transactionValueWei: bigint;
1112
gasCostWei: bigint;
13+
chainMetadata: ChainMetadata;
1214
};
1315

1416
export type SelectedScreen =

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function useTransactionCostAndData(args: {
1818
transaction: PreparedTransaction;
1919
account: Account | undefined;
2020
supportedDestinations: SupportedChainAndTokens;
21+
refetchIntervalMs?: number;
2122
}) {
2223
const { transaction, account, supportedDestinations } = args;
2324
// Compute query key of the transaction first
@@ -62,22 +63,24 @@ export function useTransactionCostAndData(args: {
6263

6364
const erc20Value = await resolvePromisedValue(transaction.erc20Value);
6465
if (erc20Value) {
65-
const [tokenBalance, tokenMeta, gasCostWei] = await Promise.all([
66-
getWalletBalance({
67-
address: account.address,
68-
chain: transaction.chain,
69-
client: transaction.client,
70-
tokenAddress: erc20Value.tokenAddress,
71-
}),
72-
getCurrencyMetadata({
73-
contract: getContract({
74-
address: erc20Value.tokenAddress,
66+
const [tokenBalance, tokenMeta, gasCostWei, chainMetadata] =
67+
await Promise.all([
68+
getWalletBalance({
69+
address: account.address,
7570
chain: transaction.chain,
7671
client: transaction.client,
72+
tokenAddress: erc20Value.tokenAddress,
7773
}),
78-
}),
79-
getTransactionGasCost(transaction, account?.address),
80-
]);
74+
getCurrencyMetadata({
75+
contract: getContract({
76+
address: erc20Value.tokenAddress,
77+
chain: transaction.chain,
78+
client: transaction.client,
79+
}),
80+
}),
81+
getTransactionGasCost(transaction, account?.address),
82+
getChainMetadata(transaction.chain),
83+
]);
8184
const transactionValueWei = erc20Value.amountWei;
8285
const walletBalance = tokenBalance;
8386
const currency = {
@@ -95,6 +98,7 @@ export function useTransactionCostAndData(args: {
9598
return {
9699
token: currency,
97100
decimals: tokenMeta.decimals,
101+
chainMetadata,
98102
walletBalance,
99103
gasCostWei,
100104
transactionValueWei,
@@ -121,19 +125,14 @@ export function useTransactionCostAndData(args: {
121125
symbol: chainMetadata.nativeCurrency.symbol,
122126
icon: chainMetadata.icon?.url,
123127
},
128+
chainMetadata,
124129
decimals: 18,
125130
walletBalance,
126131
gasCostWei,
127132
transactionValueWei,
128133
} satisfies TransactionCostAndData;
129134
},
130135
enabled: !!transaction && !!txQueryKey,
131-
refetchInterval: () => {
132-
if (transaction.erc20Value) {
133-
// if erc20 value is set, we don't need to poll
134-
return undefined;
135-
}
136-
return 30_000;
137-
},
136+
refetchInterval: args.refetchIntervalMs || 30_000,
138137
});
139138
}

0 commit comments

Comments
 (0)