Skip to content

Commit 6713fe8

Browse files
committed
feat: ens resolution
1 parent 2792071 commit 6713fe8

File tree

3 files changed

+78
-42
lines changed

3 files changed

+78
-42
lines changed

apps/dashboard/src/@/components/blocks/TokenSelector.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ export function TokenSelector(props: {
2222
placeholder?: string;
2323
client: ThirdwebClient;
2424
disabled?: boolean;
25+
enabled?: boolean;
2526
}) {
26-
const { tokens, isLoading } = useTokensData({
27+
const { tokens, isFetching } = useTokensData({
2728
clientId: props.client.clientId,
2829
chainId: props.chainId,
30+
enabled: props.enabled,
2931
});
3032

3133
const options = useMemo(() => {
@@ -108,13 +110,13 @@ export function TokenSelector(props: {
108110
closeOnSelect={true}
109111
showCheck={false}
110112
placeholder={
111-
isLoading ? "Loading Tokens..." : props.placeholder || "Select Token"
113+
isFetching ? "Loading Tokens..." : props.placeholder || "Select Token"
112114
}
113115
overrideSearchFn={searchFn}
114116
renderOption={renderOption}
115117
className={props.className}
116118
popoverContentClassName={props.popoverContentClassName}
117-
disabled={isLoading || props.disabled}
119+
disabled={isFetching || props.disabled}
118120
side={props.side}
119121
align={props.align}
120122
/>

apps/dashboard/src/app/checkout/components/client/CheckoutLinkForm.client.tsx

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import { useThirdwebClient } from "@/constants/thirdweb.client";
1010
import { CreditCardIcon } from "lucide-react";
1111
import { useState } from "react";
1212
import { toast } from "sonner";
13-
import { defineChain, getContract } from "thirdweb";
13+
import { type ThirdwebClient, defineChain, getContract } from "thirdweb";
1414
import { getCurrencyMetadata } from "thirdweb/extensions/erc20";
15-
import { checksumAddress } from "thirdweb/utils";
15+
import { resolveEns } from "../../../../lib/ens";
1616

1717
export function CheckoutLinkForm() {
1818
const client = useThirdwebClient();
@@ -33,41 +33,20 @@ export function CheckoutLinkForm() {
3333
throw new Error("All fields are required");
3434
}
3535

36-
// Validate addresses
37-
if (!checksumAddress(recipientAddress)) {
38-
throw new Error("Invalid recipient address");
39-
}
40-
if (!checksumAddress(tokenAddressWithChain)) {
41-
throw new Error("Invalid token address");
42-
}
43-
44-
const [_chainId, tokenAddress] = tokenAddressWithChain.split(":");
45-
if (Number(_chainId) !== chainId) {
46-
throw new Error("Chain ID does not match token chain");
47-
}
48-
if (!tokenAddress) {
49-
throw new Error("Missing token address");
50-
}
51-
// Get token decimals
52-
const tokenContract = getContract({
36+
const inputs = await parseInputs(
5337
client,
54-
// eslint-disable-next-line no-restricted-syntax
55-
chain: defineChain(chainId),
56-
address: tokenAddress,
57-
});
58-
const { decimals } = await getCurrencyMetadata({
59-
contract: tokenContract,
60-
});
61-
62-
// Convert amount to wei
63-
const amountInWei = BigInt(Number.parseFloat(amount) * 10 ** decimals);
38+
chainId,
39+
tokenAddressWithChain,
40+
recipientAddress,
41+
amount,
42+
);
6443

6544
// Build checkout URL
6645
const params = new URLSearchParams({
67-
chainId: chainId.toString(),
68-
recipientAddress,
69-
tokenAddress: tokenAddressWithChain,
70-
amount: amountInWei.toString(),
46+
chainId: inputs.chainId.toString(),
47+
recipientAddress: inputs.recipientAddress,
48+
tokenAddress: inputs.tokenAddress,
49+
amount: inputs.amount.toString(),
7150
});
7251

7352
const checkoutUrl = `${window.location.origin}/checkout?${params.toString()}`;
@@ -121,6 +100,7 @@ export function CheckoutLinkForm() {
121100
className="w-full"
122101
client={client}
123102
disabled={!chainId}
103+
enabled={!!chainId}
124104
/>
125105
</div>
126106

@@ -132,7 +112,7 @@ export function CheckoutLinkForm() {
132112
id="recipient"
133113
value={recipientAddress}
134114
onChange={(e) => setRecipientAddress(e.target.value)}
135-
placeholder="0x..."
115+
placeholder="Address or ENS"
136116
required
137117
className="w-full"
138118
/>
@@ -161,7 +141,7 @@ export function CheckoutLinkForm() {
161141
type="button"
162142
variant="outline"
163143
className="flex-1"
164-
onClick={() => {
144+
onClick={async () => {
165145
if (
166146
!chainId ||
167147
!recipientAddress ||
@@ -171,11 +151,18 @@ export function CheckoutLinkForm() {
171151
toast.error("Please fill in all fields first");
172152
return;
173153
}
174-
const params = new URLSearchParams({
175-
chainId: chainId.toString(),
154+
const inputs = await parseInputs(
155+
client,
156+
chainId,
157+
tokenAddressWithChain,
176158
recipientAddress,
177-
tokenAddress: tokenAddressWithChain,
178159
amount,
160+
);
161+
const params = new URLSearchParams({
162+
chainId: inputs.chainId.toString(),
163+
recipientAddress: inputs.recipientAddress,
164+
tokenAddress: inputs.tokenAddress,
165+
amount: inputs.amount.toString(),
179166
});
180167
window.open(`/checkout?${params.toString()}`, "_blank");
181168
}}
@@ -191,3 +178,47 @@ export function CheckoutLinkForm() {
191178
</Card>
192179
);
193180
}
181+
182+
async function parseInputs(
183+
client: ThirdwebClient,
184+
chainId: number,
185+
tokenAddressWithChain: string,
186+
recipientAddressOrEns: string,
187+
decimalAmount: string,
188+
) {
189+
const [_chainId, tokenAddress] = tokenAddressWithChain.split(":");
190+
if (Number(_chainId) !== chainId) {
191+
throw new Error("Chain ID does not match token chain");
192+
}
193+
if (!tokenAddress) {
194+
throw new Error("Missing token address");
195+
}
196+
197+
const ensPromise = resolveEns(recipientAddressOrEns, client);
198+
const currencyPromise = getCurrencyMetadata({
199+
contract: getContract({
200+
client,
201+
// eslint-disable-next-line no-restricted-syntax
202+
chain: defineChain(chainId),
203+
address: tokenAddress,
204+
}),
205+
});
206+
const [ens, currencyMetadata] = await Promise.all([
207+
ensPromise,
208+
currencyPromise,
209+
]);
210+
if (!ens.address) {
211+
throw new Error("Invalid recipient address");
212+
}
213+
214+
const amountInWei = BigInt(
215+
Number.parseFloat(decimalAmount) * 10 ** currencyMetadata.decimals,
216+
);
217+
218+
return {
219+
chainId,
220+
tokenAddress,
221+
recipientAddress: ens.address,
222+
amount: amountInWei,
223+
};
224+
}

apps/dashboard/src/hooks/tokens/tokens.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,12 @@ const structuredTokensStore = /* @__PURE__ */ createStructuredTokensStore();
8484
export function useTokensData({
8585
clientId,
8686
chainId,
87-
}: { clientId: string; chainId?: number }) {
87+
enabled,
88+
}: { clientId: string; chainId?: number; enabled?: boolean }) {
8889
const tokensQuery = useQuery({
8990
queryKey: ["universal-bridge-tokens", chainId],
9091
queryFn: () => getUniversalBrigeTokens({ clientId, chainId }),
92+
enabled,
9193
});
9294

9395
// eslint-disable-next-line no-restricted-syntax
@@ -107,5 +109,6 @@ export function useTokensData({
107109
return {
108110
tokens: useStore(structuredTokensStore),
109111
isLoading: tokensQuery.isLoading,
112+
isFetching: tokensQuery.isFetching,
110113
};
111114
}

0 commit comments

Comments
 (0)