@@ -17,6 +17,7 @@ import { useFungibleToken } from "@/entities/_shared/token";
1717import { extractMatchingPots } from "@/entities/pot" ;
1818import { useDispatch } from "@/store/hooks" ;
1919
20+ import { useCrossChainTokens } from "./cross-chain-tokens" ;
2021import {
2122 DONATION_DEFAULT_MIN_AMOUNT_FLOAT ,
2223 DONATION_INSUFFICIENT_BALANCE_ERROR ,
@@ -145,6 +146,42 @@ export const useDonationForm = ({ cachedTokenId, ...params }: DonationFormParams
145146 enabled : ! isCrossChainToken ,
146147 } ) ;
147148
149+ // Fetch cross-chain token list to get NEAR price and selected token price
150+ const { data : crossChainTokenList } = useCrossChainTokens ( ) ;
151+
152+ const { crossChainNearPrice, crossChainTokenPrice, crossChainTokenSymbol } = useMemo ( ( ) => {
153+ if ( ! isCrossChainToken || ! crossChainTokenList || ! values . tokenId ) {
154+ return { crossChainNearPrice : 0 , crossChainTokenPrice : 0 , crossChainTokenSymbol : "" } ;
155+ }
156+
157+ const nearToken = crossChainTokenList . find (
158+ ( t ) => t . symbol === "wNEAR" || t . assetId === "nep141:wrap.near" ,
159+ ) ;
160+
161+ const parts = values . tokenId . split ( ":" ) ;
162+ const blockchain = parts [ 0 ] ;
163+ const assetId = parts . slice ( 1 ) . join ( ":" ) ;
164+
165+ const selectedToken = crossChainTokenList . find (
166+ ( t ) => t . assetId === assetId && t . blockchain . toLowerCase ( ) === blockchain . toLowerCase ( ) ,
167+ ) ;
168+
169+ return {
170+ crossChainNearPrice : nearToken ?. price ?? 0 ,
171+ crossChainTokenPrice : selectedToken ?. price ?? 0 ,
172+ crossChainTokenSymbol : selectedToken ?. symbol ?? "" ,
173+ } ;
174+ } , [ isCrossChainToken , crossChainTokenList , values . tokenId ] ) ;
175+
176+ // Minimum amount in the selected cross-chain token equivalent to 0.1 NEAR
177+ const crossChainMinAmount = useMemo ( ( ) => {
178+ if ( crossChainTokenPrice > 0 && crossChainNearPrice > 0 ) {
179+ return ( 0.1 * crossChainNearPrice ) / crossChainTokenPrice ;
180+ }
181+
182+ return 0 ;
183+ } , [ crossChainNearPrice , crossChainTokenPrice ] ) ;
184+
148185 const { data : pot } = indexer . usePot ( {
149186 enabled : isGroupPotDonation || isSingleRecipientPotDonation ,
150187 potId : groupDonationPotId ?? values . potAccountId ?? NOOP_STRING ,
@@ -296,8 +333,21 @@ export const useDonationForm = ({ cachedTokenId, ...params }: DonationFormParams
296333 }
297334 }
298335
336+ //* Cross-chain minimum amount validation (0.1 NEAR equivalent)
337+ else if (
338+ isCrossChainToken &&
339+ crossChainMinAmount > 0 &&
340+ Big ( parsedAmount ) . lt ( crossChainMinAmount )
341+ ) {
342+ const errorMessage = `Amount must be at least ${ crossChainMinAmount . toFixed ( 4 ) } ${ crossChainTokenSymbol || "tokens" } (equivalent to 0.1 NEAR).` ;
343+
344+ if ( customErrors ?. amount ?. message !== errorMessage || self . formState . isValid ) {
345+ setCustomErrors ( { amount : { message : errorMessage } } ) ;
346+ }
347+ }
348+
299349 //* Addressing single-recipient and group donation scenarios with evenly distributed funds
300- //* Skip minimum amount validation for cross-chain tokens (they don't have min requirements )
350+ //* Skip minimum amount validation for cross-chain tokens (handled above )
301351 else if (
302352 ! isCrossChainToken &&
303353 minTotalAmountFloat !== undefined &&
@@ -344,6 +394,8 @@ export const useDonationForm = ({ cachedTokenId, ...params }: DonationFormParams
344394 }
345395 } , [
346396 customErrors ,
397+ crossChainMinAmount ,
398+ crossChainTokenSymbol ,
347399 isCrossChainToken ,
348400 isFtDonation ,
349401 isGroupDonation ,
@@ -375,5 +427,8 @@ export const useDonationForm = ({ cachedTokenId, ...params }: DonationFormParams
375427 // TODO: Likely not needed to be exposed anymore, try using `amount` everywhere
376428 // TODO: in the consuming code instead and remove this if no issues detected.
377429 totalAmountFloat,
430+ crossChainMinAmount,
431+ crossChainTokenSymbol,
432+ isCrossChainToken,
378433 } ;
379434} ;
0 commit comments