@@ -10,9 +10,9 @@ import { useThirdwebClient } from "@/constants/thirdweb.client";
1010import { CreditCardIcon } from "lucide-react" ;
1111import { useState } from "react" ;
1212import { toast } from "sonner" ;
13- import { defineChain , getContract } from "thirdweb" ;
13+ import { type ThirdwebClient , defineChain , getContract } from "thirdweb" ;
1414import { getCurrencyMetadata } from "thirdweb/extensions/erc20" ;
15- import { checksumAddress } from "thirdweb/utils " ;
15+ import { resolveEns } from "../../../../lib/ens " ;
1616
1717export 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+ }
0 commit comments