Skip to content

Commit f368199

Browse files
[SDK] feat: Redesign token selection flow in PayEmbed
1 parent 52cbcd2 commit f368199

File tree

13 files changed

+167
-161
lines changed

13 files changed

+167
-161
lines changed

apps/playground-web/src/components/pay/direct-payment.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use client";
2-
3-
import { sepolia } from "thirdweb/chains";
2+
import { base } from "thirdweb/chains";
43
import { PayEmbed, getDefaultToken } from "thirdweb/react";
54
import { THIRDWEB_CLIENT } from "../../lib/client";
65
import { StyledConnectButton } from "../styled-connect-button";
@@ -16,9 +15,9 @@ export function BuyMerchPreview() {
1615
payOptions={{
1716
mode: "direct_payment",
1817
paymentInfo: {
19-
amount: "0.1",
20-
chain: sepolia,
21-
token: getDefaultToken(sepolia, "USDC"),
18+
amount: "2",
19+
chain: base,
20+
token: getDefaultToken(base, "USDC"),
2221
sellerAddress: "0xEb0effdFB4dC5b3d5d3aC6ce29F3ED213E95d675",
2322
},
2423
metadata: {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function PayTransactionPreview() {
5555
to: account?.address || "",
5656
}),
5757
metadata: nft?.metadata,
58+
buyWithFiat: false,
5859
}}
5960
/>
6061
)}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,16 +464,14 @@ function BuyScreenContent(props: BuyScreenContentProps) {
464464
backScreen: screen,
465465
});
466466
}}
467-
modalTitle="Available tokens"
467+
modalTitle="Available tokens" // TODO (pay) localize
468468
connectLocale={props.connectLocale}
469469
client={props.client}
470470
sourceTokens={sourceSupportedTokens}
471471
sourceSupportedTokens={sourceSupportedTokens}
472472
toChain={toChain}
473473
toToken={toToken}
474474
tokenAmount={tokenAmount}
475-
fromChain={fromChain}
476-
fromToken={fromToken}
477475
mode={payOptions.mode}
478476
hiddenWallets={props.hiddenWallets}
479477
onSelect={(w, token, chain) => {

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/TransactionModeScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
isNativeToken,
3434
} from "../nativeToken.js";
3535
import { useTransactionCostAndData } from "./main/useBuyTxStates.js";
36-
import { WalletRow } from "./swap/TokenSelectorScreen.js";
36+
import { WalletRow } from "./swap/WalletRow.js";
3737
import type { SupportedChainAndTokens } from "./swap/useSwapSupportedChains.js";
3838

3939
export function TransactionModeScreen(props: {

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

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,9 @@ export function useFromTokenSelectionStates(options: {
7676
payOptions: PayUIOptions;
7777
supportedSources: SupportedSourcesInputData[];
7878
}) {
79-
const { payOptions, supportedSources } = options;
79+
const { payOptions } = options;
8080

81-
// --------------------------------------------------------------------------
82-
const firstSupportedSource = supportedSources?.length
83-
? supportedSources.length === 1
84-
? supportedSources[0]
85-
: supportedSources.find((x) => x.chain.id !== 1) // dont use mainnet as a default source, unless its the only source
86-
: undefined;
81+
// TODO (pay) - auto select token based on connected wallet balances
8782

8883
// Source token and chain selection ---------------------------------------------------
8984
const [fromChain_, setFromChain] = useState<Chain>();
@@ -95,12 +90,7 @@ export function useFromTokenSelectionStates(options: {
9590
(payOptions.mode === "transaction" && payOptions.transaction?.chain) ||
9691
(payOptions.mode === "direct_payment" && payOptions.paymentInfo?.chain);
9792

98-
const fromChainFromApi = firstSupportedSource?.chain
99-
? firstSupportedSource.chain
100-
: undefined;
101-
102-
const fromChain =
103-
fromChain_ || fromChainDevSpecified || fromChainFromApi || polygon;
93+
const fromChain = fromChain_ || fromChainDevSpecified || undefined;
10494

10595
const [fromToken_, setFromToken] = useState<ERC20OrNativeToken>();
10696

@@ -110,12 +100,8 @@ export function useFromTokenSelectionStates(options: {
110100
payOptions.buyWithCrypto?.prefillSource?.token) ||
111101
(payOptions.mode === "direct_payment" && payOptions.paymentInfo.token);
112102

113-
// May be updated in the future
114-
const fromTokenFromApi = NATIVE_TOKEN;
115-
116103
// supported tokens query in here
117-
const fromToken =
118-
fromToken_ || fromTokenDevSpecified || fromTokenFromApi || NATIVE_TOKEN;
104+
const fromToken = fromToken_ || fromTokenDevSpecified || undefined;
119105

120106
return {
121107
fromChain,

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@ import {
1212
type WaitForReceiptOptions,
1313
waitForReceipt,
1414
} from "../../../../../../../transaction/actions/wait-for-tx-receipt.js";
15-
import { shortenAddress } from "../../../../../../../utils/address.js";
1615
import { formatNumber } from "../../../../../../../utils/formatNumber.js";
1716
import { useCustomTheme } from "../../../../../../core/design-system/CustomThemeProvider.js";
1817
import {
1918
fontSize,
2019
iconSize,
2120
} from "../../../../../../core/design-system/index.js";
2221
import { useChainName } from "../../../../../../core/hooks/others/useChainQuery.js";
23-
import { useEnsName } from "../../../../../../core/utils/wallet.js";
2422
import { Skeleton } from "../../../../components/Skeleton.js";
2523
import { Spacer } from "../../../../components/Spacer.js";
2624
import { Spinner } from "../../../../components/Spinner.js";
@@ -34,6 +32,7 @@ import type { ERC20OrNativeToken } from "../../nativeToken.js";
3432
import { PayTokenIcon } from "../PayTokenIcon.js";
3533
import { Step } from "../Stepper.js";
3634
import type { PayerInfo } from "../types.js";
35+
import { WalletRow } from "./WalletRow.js";
3736
import { formatSeconds } from "./formatSeconds.js";
3837
import { addPendingTx } from "./pendingSwapTx.js";
3938

@@ -76,8 +75,6 @@ export function SwapConfirmationScreen(props: {
7675
const sender = props.quote.swapDetails.fromAddress;
7776
const isDifferentRecipient = receiver.toLowerCase() !== sender.toLowerCase();
7877

79-
const ensName = useEnsName({ client: props.client, address: receiver });
80-
8178
return (
8279
<Container p="lg">
8380
<ModalHeader title={props.title} onBack={props.onBack} />
@@ -139,9 +136,12 @@ export function SwapConfirmationScreen(props: {
139136
{/* Send to */}
140137
{isDifferentRecipient && (
141138
<ConfirmItem label="Receiver">
142-
<Text color="primaryText" size="sm">
143-
{ensName.data || shortenAddress(receiver)}
144-
</Text>
139+
<WalletRow
140+
client={props.client}
141+
address={receiver}
142+
iconSize="sm"
143+
textSize="sm"
144+
/>
145145
</ConfirmItem>
146146
)}
147147

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { TokenRow } from "../../../../components/token/TokenRow.js";
1616
import { TokenSymbol } from "../../../../components/token/TokenSymbol.js";
1717
import { formatTokenBalance } from "../../formatTokenBalance.js";
1818
import { type NativeToken, isNativeToken } from "../../nativeToken.js";
19-
import { WalletRow } from "./TokenSelectorScreen.js";
19+
import { WalletRow } from "./WalletRow.js";
2020

2121
/**
2222
* Shows an amount "value" and renders the selected token and chain
@@ -26,8 +26,8 @@ import { WalletRow } from "./TokenSelectorScreen.js";
2626
*/
2727
export function PayWithCryptoQuoteInfo(props: {
2828
value: string;
29-
chain: Chain;
30-
token: TokenInfo | NativeToken;
29+
chain: Chain | undefined;
30+
token: TokenInfo | NativeToken | undefined;
3131
isLoading: boolean;
3232
client: ThirdwebClient;
3333
freezeChainAndTokenSelection?: boolean;
@@ -36,12 +36,19 @@ export function PayWithCryptoQuoteInfo(props: {
3636
onSelectToken: () => void;
3737
}) {
3838
const theme = useCustomTheme();
39-
const balanceQuery = useWalletBalance({
40-
address: props.payerAccount.address,
41-
chain: props.chain,
42-
tokenAddress: isNativeToken(props.token) ? undefined : props.token.address,
43-
client: props.client,
44-
});
39+
const balanceQuery = useWalletBalance(
40+
{
41+
address: props.payerAccount.address,
42+
chain: props.chain,
43+
tokenAddress: isNativeToken(props.token)
44+
? undefined
45+
: props.token?.address,
46+
client: props.client,
47+
},
48+
{
49+
enabled: !!props.chain && !!props.token,
50+
},
51+
);
4552

4653
return (
4754
<Container
@@ -69,10 +76,10 @@ export function PayWithCryptoQuoteInfo(props: {
6976
}}
7077
>
7178
<WalletRow client={props.client} address={props.payerAccount.address} />
72-
{balanceQuery.data ? (
79+
{props.token && props.chain && balanceQuery.data ? (
7380
<Container flex="row" gap="3xs" center="y">
7481
<Text size="xs" color="secondaryText" weight={500}>
75-
{formatTokenBalance(balanceQuery.data, false)}
82+
{formatTokenBalance(balanceQuery.data, false, 4)}
7683
</Text>
7784
<TokenSymbol
7885
token={props.token}
@@ -81,11 +88,11 @@ export function PayWithCryptoQuoteInfo(props: {
8188
color="secondaryText"
8289
/>
8390
</Container>
84-
) : (
91+
) : props.token && props.chain && balanceQuery.isLoading ? (
8592
<Skeleton width="70px" height={fontSize.xs} />
86-
)}
93+
) : null}
8794
</Container>
88-
{/* Quoted price */}
95+
{/* Quoted price & token selector */}
8996
<TokenRow
9097
token={props.token}
9198
chain={props.chain}
@@ -96,6 +103,10 @@ export function PayWithCryptoQuoteInfo(props: {
96103
style={{
97104
border: "none",
98105
borderRadius: 0,
106+
borderBottomLeftRadius:
107+
!props.token || !props.chain || !props.swapRequired ? radius.lg : 0,
108+
borderBottomRightRadius:
109+
!props.token || !props.chain || !props.swapRequired ? radius.lg : 0,
99110
}}
100111
/>
101112
</Container>

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.tsx

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ export function SwapScreenContent(props: {
4242
tokenAmount: string;
4343
toToken: ERC20OrNativeToken;
4444
toChain: Chain;
45-
fromChain: Chain;
46-
fromToken: ERC20OrNativeToken;
45+
fromChain: Chain | undefined;
46+
fromToken: ERC20OrNativeToken | undefined;
4747
showFromTokenSelector: () => void;
4848
payer: PayerInfo;
4949
client: ThirdwebClient;
@@ -81,42 +81,50 @@ export function SwapScreenContent(props: {
8181
"fees" | "receiver" | "payer"
8282
>("fees");
8383

84-
const fromTokenBalanceQuery = useWalletBalance({
85-
address: payer.account.address,
86-
chain: fromChain,
87-
tokenAddress: isNativeToken(fromToken) ? undefined : fromToken.address,
88-
client,
89-
});
84+
const fromTokenBalanceQuery = useWalletBalance(
85+
{
86+
address: payer.account.address,
87+
chain: fromChain,
88+
tokenAddress: isNativeToken(fromToken) ? undefined : fromToken?.address,
89+
client,
90+
},
91+
{
92+
enabled: !!fromChain && !!fromToken,
93+
},
94+
);
9095

9196
const fromTokenId = isNativeToken(fromToken)
9297
? NATIVE_TOKEN_ADDRESS
93-
: fromToken.address.toLowerCase();
98+
: fromToken?.address?.toLowerCase();
9499
const toTokenId = isNativeToken(toToken)
95100
? NATIVE_TOKEN_ADDRESS
96101
: toToken.address.toLowerCase();
97102
const swapRequired =
98103
!!tokenAmount &&
99-
!(fromChain.id === toChain.id && fromTokenId === toTokenId);
100-
const quoteParams: GetBuyWithCryptoQuoteParams | undefined = swapRequired
101-
? {
102-
// wallets
103-
fromAddress: payer.account.address,
104-
toAddress: receiverAddress,
105-
// from
106-
fromChainId: fromChain.id,
107-
fromTokenAddress: isNativeToken(fromToken)
108-
? NATIVE_TOKEN_ADDRESS
109-
: fromToken.address,
110-
// to
111-
toChainId: toChain.id,
112-
toTokenAddress: isNativeToken(toToken)
113-
? NATIVE_TOKEN_ADDRESS
114-
: toToken.address,
115-
toAmount: tokenAmount,
116-
client,
117-
purchaseData: payOptions.purchaseData,
118-
}
119-
: undefined;
104+
!!fromChain &&
105+
!!fromTokenId &&
106+
!(fromChain?.id === toChain.id && fromTokenId === toTokenId);
107+
const quoteParams: GetBuyWithCryptoQuoteParams | undefined =
108+
fromChain && fromToken && swapRequired
109+
? {
110+
// wallets
111+
fromAddress: payer.account.address,
112+
toAddress: receiverAddress,
113+
// from
114+
fromChainId: fromChain.id,
115+
fromTokenAddress: isNativeToken(fromToken)
116+
? NATIVE_TOKEN_ADDRESS
117+
: fromToken.address,
118+
// to
119+
toChainId: toChain.id,
120+
toTokenAddress: isNativeToken(toToken)
121+
? NATIVE_TOKEN_ADDRESS
122+
: toToken.address,
123+
toAmount: tokenAmount,
124+
client,
125+
purchaseData: payOptions.purchaseData,
126+
}
127+
: undefined;
120128

121129
const quoteQuery = useBuyWithCryptoQuote(quoteParams, {
122130
// refetch every 30 seconds
@@ -159,11 +167,13 @@ export function SwapScreenContent(props: {
159167
Number(fromTokenBalanceQuery.data.displayValue) < Number(sourceTokenAmount);
160168

161169
const disableContinue =
170+
!fromChain ||
171+
!fromToken ||
162172
(swapRequired && !quoteQuery.data) ||
163173
isNotEnoughBalance ||
164174
allowanceQuery.isLoading;
165175
const switchChainRequired =
166-
props.payer.wallet.getChain()?.id !== fromChain.id;
176+
props.payer.wallet.getChain()?.id !== fromChain?.id;
167177

168178
const errorMsg =
169179
!quoteQuery.isLoading && quoteQuery.error
@@ -252,7 +262,7 @@ export function SwapScreenContent(props: {
252262
swapRequired={swapRequired}
253263
onSelectToken={props.showFromTokenSelector}
254264
/>
255-
{swapRequired && (
265+
{swapRequired && fromChain && fromToken && (
256266
<EstimatedTimeAndFees
257267
quoteIsLoading={quoteQuery.isLoading}
258268
estimatedSeconds={
@@ -317,9 +327,10 @@ export function SwapScreenContent(props: {
317327
fullWidth
318328
onClick={() => props.showFromTokenSelector()}
319329
>
320-
Select another token
330+
Pay with another token
321331
</Button>
322332
) : switchChainRequired &&
333+
fromChain &&
323334
!quoteQuery.isLoading &&
324335
!allowanceQuery.isLoading &&
325336
!isNotEnoughBalance &&

0 commit comments

Comments
 (0)