22
33import * as Tokens from "@rozoai/intent-common" ;
44import {
5+ baseEURC ,
56 FeeType ,
67 getChainName ,
78 getChainNativeToken ,
9+ getKnownToken ,
810 knownTokens ,
911 rozoSolana ,
1012 rozoStellar ,
13+ rozoStellarEURC ,
1114 TokenSymbol ,
1215} from "@rozoai/intent-common" ;
1316import {
@@ -187,10 +190,32 @@ export default function DemoBasic() {
187190 TokenSymbol . USDC ,
188191 TokenSymbol . USDT ,
189192 ] ) ;
193+ const [ eurcValidationError , setEurcValidationError ] = useState < string > ( "" ) ;
190194
191195 const handleSetConfig = useCallback (
192196 ( newConfig : Config , symbols ?: TokenSymbol [ ] ) => {
193197 const symbolsToUse = symbols ?? preferredSymbol ;
198+
199+ // Validate EURC: EURC can only be sent to EURC
200+ const hasEURC = symbolsToUse . includes ( TokenSymbol . EURC ) ;
201+ if ( hasEURC && newConfig . tokenAddress ) {
202+ const destinationToken = getKnownToken (
203+ newConfig . chainId ,
204+ newConfig . tokenAddress
205+ ) ;
206+ const isDestinationEURC = destinationToken ?. symbol === TokenSymbol . EURC ;
207+
208+ if ( ! isDestinationEURC ) {
209+ setEurcValidationError (
210+ `EURC can only be sent to another EURC. Please select an EURC token as the destination token.`
211+ ) ;
212+ return ; // Don't update config if validation fails
213+ }
214+ }
215+
216+ // Clear error if validation passes
217+ setEurcValidationError ( "" ) ;
218+
194219 const configWithSymbols = {
195220 ...newConfig ,
196221 preferredSymbol : symbolsToUse ,
@@ -261,6 +286,27 @@ export default function DemoBasic() {
261286 "chainId" in parsedConfig &&
262287 "tokenAddress" in parsedConfig
263288 ) {
289+ // Validate EURC: EURC can only be sent to EURC
290+ const hasEURC = parsedConfig . preferredSymbol ?. includes (
291+ TokenSymbol . EURC
292+ ) ;
293+ if ( hasEURC && parsedConfig . tokenAddress && parsedConfig . chainId ) {
294+ const destinationToken = getKnownToken (
295+ parsedConfig . chainId ,
296+ parsedConfig . tokenAddress
297+ ) ;
298+ const isDestinationEURC =
299+ destinationToken ?. symbol === TokenSymbol . EURC ;
300+
301+ if ( ! isDestinationEURC ) {
302+ // Reset preferredSymbol to default if EURC validation fails
303+ parsedConfig . preferredSymbol = [ TokenSymbol . USDC , TokenSymbol . USDT ] ;
304+ setEurcValidationError (
305+ `EURC can only be sent to another EURC. Configuration has been reset to default.`
306+ ) ;
307+ }
308+ }
309+
264310 setConfig ( parsedConfig ) ;
265311 setParsedConfig ( parsedConfig ) ;
266312 if ( parsedConfig . preferredSymbol ) {
@@ -279,6 +325,41 @@ export default function DemoBasic() {
279325 parsedConfig . tokenAddress &&
280326 parsedConfig . amount ;
281327
328+ // Check if destination token is Base EURC or Stellar EURC
329+ const isDestinationEURC = useMemo ( ( ) => {
330+ if ( ! parsedConfig || ! parsedConfig . tokenAddress || ! parsedConfig . chainId ) {
331+ return false ;
332+ }
333+
334+ const destinationToken = getKnownToken (
335+ parsedConfig . chainId ,
336+ parsedConfig . tokenAddress
337+ ) ;
338+
339+ if ( ! destinationToken ) return false ;
340+
341+ // Check if it's Base EURC
342+ if (
343+ parsedConfig . chainId === baseEURC . chainId &&
344+ isEvmChain ( parsedConfig . chainId )
345+ ) {
346+ try {
347+ return (
348+ getAddress ( destinationToken . token ) === getAddress ( baseEURC . token )
349+ ) ;
350+ } catch {
351+ return destinationToken . token === baseEURC . token ;
352+ }
353+ }
354+
355+ // Check if it's Stellar EURC
356+ if ( parsedConfig . chainId === rozoStellarEURC . chainId ) {
357+ return destinationToken . token === rozoStellarEURC . token ;
358+ }
359+
360+ return false ;
361+ } , [ parsedConfig ] ) ;
362+
282363 // Generate code snippet when config changes
283364 const codeSnippet = useMemo ( ( ) => {
284365 if ( ! hasValidConfig || ! parsedConfig ) return "" ;
@@ -299,6 +380,30 @@ export default function DemoBasic() {
299380 ? [ TokenSymbol . USDC , TokenSymbol . USDT ]
300381 : [ TokenSymbol . EURC ] ;
301382
383+ // If switching to EURC, find and set an EURC token for the current chain
384+ if ( nextSymbols . includes ( TokenSymbol . EURC ) && config . chainId ) {
385+ const eurcToken = knownTokens . find (
386+ ( t : any ) =>
387+ t . chainId === config . chainId && t . symbol === TokenSymbol . EURC
388+ ) ;
389+
390+ if ( eurcToken ) {
391+ const updatedConfig : Config = {
392+ ...config ,
393+ tokenAddress : eurcToken . token ,
394+ preferredSymbol : nextSymbols ,
395+ } ;
396+ setPreferredSymbol ( nextSymbols ) ;
397+ handleSetConfig ( updatedConfig , nextSymbols ) ;
398+ return ;
399+ } else {
400+ setEurcValidationError (
401+ `EURC is not available on the selected chain. Please select a chain that supports EURC (Base, Ethereum, or Stellar).`
402+ ) ;
403+ return ;
404+ }
405+ }
406+
302407 setPreferredSymbol ( nextSymbols ) ;
303408 handleSetConfig ( config , nextSymbols ) ;
304409 } , [ config , preferredSymbol , handleSetConfig ] ) ;
@@ -317,7 +422,19 @@ export default function DemoBasic() {
317422 </ Text >
318423 </ div >
319424
320- < button onClick = { handleChangeCurrency } > Change currency</ button >
425+ < div className = "mb-4 flex items-center gap-4" >
426+ < button
427+ onClick = { handleChangeCurrency }
428+ className = "px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors font-medium mx-auto"
429+ >
430+ Change currency
431+ </ button >
432+ { eurcValidationError && (
433+ < div className = "flex-1 bg-red-50 border border-red-200 rounded-lg p-3" >
434+ < p className = "text-sm text-red-800" > { eurcValidationError } </ p >
435+ </ div >
436+ ) }
437+ </ div >
321438
322439 { /* Main Content */ }
323440 < div className = "flex flex-col items-center gap-6" >
@@ -328,6 +445,14 @@ export default function DemoBasic() {
328445 < h2 className = "text-xl font-semibold text-gray-800 mb-4" >
329446 Try the Payment
330447 </ h2 >
448+ { isDestinationEURC && (
449+ < div className = "mb-4 bg-yellow-50 border border-yellow-200 rounded-lg p-3" >
450+ < p className = "text-sm text-yellow-800" >
451+ < strong > ⚠️ EURC Restriction:</ strong > EURC can only be sent
452+ to another EURC token. The destination token must be EURC.
453+ </ p >
454+ </ div >
455+ ) }
331456 < div className = "flex flex-col gap-3" >
332457 < RozoPayButton . Custom
333458 appId = { APP_ID }
@@ -512,6 +637,12 @@ export default function DemoBasic() {
512637 </ code >
513638 . Automatically finds matching tokens across all supported
514639 chains.
640+ < br />
641+ < strong className = "text-red-600" >
642+ ⚠️ Important: EURC can only be sent to another EURC token.
643+ If EURC is in preferredSymbol, the destination token must
644+ also be EURC.
645+ </ strong >
515646 </ dd >
516647 </ div >
517648 < div >
0 commit comments