Skip to content

Commit 9f779ad

Browse files
feat: add EURC support for Base and Stellar
1 parent 1af7153 commit 9f779ad

File tree

14 files changed

+121
-179
lines changed

14 files changed

+121
-179
lines changed

examples/nextjs-app/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"@farcaster/frame-sdk": "^0.0.26",
1414
"@headlessui/react": "^2.2.0",
1515
"@rainbow-me/rainbowkit": "^2.2.8",
16-
"@rozoai/intent-common": "0.1.6",
17-
"@rozoai/intent-pay": "0.1.5",
16+
"@rozoai/intent-common": "workspace:*",
17+
"@rozoai/intent-pay": "workspace:*",
1818
"@tanstack/react-query": "^5.51.11",
1919
"@types/react-syntax-highlighter": "^15.5.13",
2020
"@wagmi/core": "^2.22.0",

packages/connectkit/bundle-analysis.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/connectkit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"@albedo-link/intent": "^0.13.0",
4848
"@reown/appkit": "^1.7.0",
4949
"@rollup/plugin-typescript": "^12.1.2",
50-
"@rozoai/intent-common": "0.1.6",
50+
"@rozoai/intent-common": "workspace:*",
5151
"@solana/spl-token": "^0.4.14",
5252
"@solana/wallet-adapter-base": "^0.9.27",
5353
"@solana/wallet-adapter-react": "^0.15.39",

packages/connectkit/src/components/Common/PoweredByFooter/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ const PoweredByFooter = ({
2828
"Hi, I need help with my payment.",
2929
"",
3030
`Version: ${rozoPayVersion}`,
31-
`Order ID: ${pay.order?.externalId?.toString()}`,
31+
pay.order?.externalId
32+
? `Order ID: ${pay.order.externalId.toString()}`
33+
: null,
3234
preFilledMessage,
3335
]
3436
.filter(Boolean)

packages/connectkit/src/components/DaimoPayModal/index.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,12 @@ export const RozoPayModal: React.FC<{
269269
(!isMobile || !disableMobileInjector)
270270
) {
271271
paymentState.setTokenMode("evm");
272+
// Use selectedChainId if it exists (from previous selection), otherwise use current chain
273+
const chainId = paymentState.selectedChainId ?? chain?.id;
272274
context.setRoute(ROUTES.SELECT_TOKEN, {
273275
event: "eth_connected_on_open",
274276
walletId: connector?.id,
275-
chainId: chain?.id,
277+
chainId,
276278
address,
277279
});
278280
} else if (
@@ -324,12 +326,15 @@ export const RozoPayModal: React.FC<{
324326
if (isEthConnected) {
325327
paymentState.setTokenMode("evm");
326328
// Preserve chainId from routeMeta if it exists (from SelectWalletChain),
327-
// If chainId is explicitly null, don't use fallback to current chain ID (show all chains)
328-
// otherwise use the current chain ID
329+
// If chainId is explicitly null, don't use fallback (show all chains)
330+
// Otherwise, use selectedChainId if it exists (from previous selection),
331+
// or fall back to current chain ID
329332
const chainId =
330333
context.routeMeta?.chainId === null
331334
? undefined
332-
: context.routeMeta?.chainId ?? chain?.id;
335+
: context.routeMeta?.chainId ??
336+
paymentState.selectedChainId ??
337+
chain?.id;
333338
context.setRoute(ROUTES.SELECT_TOKEN, {
334339
event: "connected",
335340
walletId: connector?.id,
@@ -341,9 +346,16 @@ export const RozoPayModal: React.FC<{
341346
// eslint-disable-next-line react-hooks/exhaustive-deps
342347
}, [isEthConnected, context.route, connector?.id, chain?.id, address]);
343348

344-
// Extract chainId from routeMeta when navigating to SELECT_TOKEN
349+
// Extract chainId from routeMeta when navigating to CONNECT or SELECT_TOKEN
350+
// This preserves the selected chain when user selects a chain in SelectWalletChain
345351
useEffect(() => {
346-
if (context.route === ROUTES.SELECT_TOKEN) {
352+
// Set selectedChainId when navigating to CONNECT with a chainId (from SelectWalletChain)
353+
if (context.route === ROUTES.CONNECT) {
354+
if (context.routeMeta?.chainId != null) {
355+
const chainId = context.routeMeta.chainId as number;
356+
setSelectedChainId(chainId);
357+
}
358+
} else if (context.route === ROUTES.SELECT_TOKEN) {
347359
// Only set selectedChainId if chainId is explicitly provided and not null/undefined
348360
if (context.routeMeta?.chainId != null) {
349361
const chainId = context.routeMeta.chainId as number;
@@ -354,9 +366,13 @@ export const RozoPayModal: React.FC<{
354366
}
355367
} else if (
356368
context.route !== ROUTES.SELECT_TOKEN &&
357-
context.route !== ROUTES.SELECT_WALLET_CHAIN
369+
context.route !== ROUTES.SELECT_WALLET_CHAIN &&
370+
context.route !== ROUTES.CONNECT &&
371+
context.route !== ROUTES.SELECT_METHOD
358372
) {
359-
// Clear selectedChainId when leaving SELECT_TOKEN or SELECT_WALLET_CHAIN
373+
// Clear selectedChainId when leaving SELECT_TOKEN, SELECT_WALLET_CHAIN, CONNECT, or SELECT_METHOD
374+
// but preserve it when navigating between these routes (including SELECT_METHOD)
375+
// This allows the selected chain to persist when the modal closes and reopens
360376
setSelectedChainId(undefined);
361377
}
362378
}, [context.route, context.routeMeta, setSelectedChainId]);

packages/connectkit/src/components/Pages/Confirmation/index.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ import {
2525
LoadingCircleIcon,
2626
} from "../../../assets/icons";
2727
import defaultTheme from "../../../constants/defaultTheme";
28-
import {
29-
DEFAULT_ROZO_APP_ID,
30-
ROZO_INVOICE_URL,
31-
} from "../../../constants/rozoConfig";
28+
import { ROZO_INVOICE_URL } from "../../../constants/rozoConfig";
3229
import { useRozoPay } from "../../../hooks/useDaimoPay";
3330
import { usePayoutPolling } from "../../../hooks/usePayoutPolling";
3431
import { usePusherPayout } from "../../../hooks/usePusherPayout";
@@ -108,10 +105,7 @@ const Confirmation: React.FC = () => {
108105
// eslint-disable-next-line react-hooks/exhaustive-deps
109106
}, [rozoPaymentId, order?.externalId, paymentStateContext.rozoPaymentId]);
110107

111-
const { tokens: supportedTokens } = useSupportedChains(
112-
paymentStateContext.payParams?.appId ?? DEFAULT_ROZO_APP_ID,
113-
paymentStateContext.payParams?.preferredChains
114-
);
108+
const { tokens: supportedTokens } = useSupportedChains();
115109

116110
const { done, txURL, rawPayInHash } = useMemo(() => {
117111
const { tokenMode, txHash } = paymentStateContext;

packages/connectkit/src/components/Pages/Stellar/ConnectStellar/index.tsx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,16 @@ import { Stellar } from "../../../../assets/chains";
1010
import { SquircleIcon } from "../../../../assets/logos";
1111
import { ROUTES } from "../../../../constants/routes";
1212
import { usePayContext } from "../../../../hooks/usePayContext";
13-
import { useStellar } from "../../../../provider/StellarContextProvider";
13+
import {
14+
STELLAR_WALLET_STORAGE_KEY,
15+
useStellar,
16+
} from "../../../../provider/StellarContextProvider";
1417
import * as LocalStorage from "../../../../utils/localstorage";
1518
import { OptionsList } from "../../../Common/OptionsList";
1619
import { OrderHeader } from "../../../Common/OrderHeader";
1720
import SelectAnotherMethodButton from "../../../Common/SelectAnotherMethodButton";
1821
import WalletPaymentSpinner from "../../../Spinners/WalletPaymentSpinner";
1922

20-
// LocalStorage key for Stellar wallet persistence (same as in StellarContextProvider)
21-
const STELLAR_WALLET_STORAGE_KEY = "rozo-stellar-wallet";
22-
23-
interface Option {
24-
id: string;
25-
title: string;
26-
subtitle?: string;
27-
icons: (React.ReactNode | string)[];
28-
onClick: () => void;
29-
disabled?: boolean;
30-
}
31-
3223
const ConnectStellar: React.FC = () => {
3324
const { setStellarConnector, setRoute, log } = usePayContext();
3425
const { kit, setPublicKey, setConnector } = useStellar();

packages/connectkit/src/hooks/usePaymentState.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ export interface PaymentState {
225225
) => Promise<PaymentResponse | undefined>;
226226
}
227227

228+
export const SELECTED_CHAIN_ID_STORAGE_KEY =
229+
"rozo-intent-pay-selected-chain-id";
230+
228231
export function usePaymentState({
229232
trpc,
230233
lockPayParams,
@@ -298,9 +301,43 @@ export function usePaymentState({
298301
"evm" | "solana" | "stellar" | "all"
299302
>("evm");
300303

301-
const [selectedChainId, setSelectedChainId] = useState<number | undefined>(
302-
undefined
303-
);
304+
// Initialize selectedChainId from localStorage to persist across page refreshes
305+
const [selectedChainId, setSelectedChainIdState] = useState<
306+
number | undefined
307+
>(() => {
308+
if (typeof window === "undefined") return undefined;
309+
try {
310+
const saved = localStorage.getItem(SELECTED_CHAIN_ID_STORAGE_KEY);
311+
if (saved) {
312+
const parsed = parseInt(saved, 10);
313+
if (!isNaN(parsed)) {
314+
return parsed;
315+
}
316+
}
317+
} catch (error) {
318+
// Ignore localStorage errors
319+
}
320+
return undefined;
321+
});
322+
323+
// Wrapper function to persist selectedChainId to localStorage
324+
const setSelectedChainId = useCallback((chainId: number | undefined) => {
325+
setSelectedChainIdState(chainId);
326+
if (typeof window !== "undefined") {
327+
try {
328+
if (chainId !== undefined) {
329+
localStorage.setItem(
330+
SELECTED_CHAIN_ID_STORAGE_KEY,
331+
chainId.toString()
332+
);
333+
} else {
334+
localStorage.removeItem(SELECTED_CHAIN_ID_STORAGE_KEY);
335+
}
336+
} catch (error) {
337+
// Ignore localStorage errors
338+
}
339+
}
340+
}, []);
304341

305342
const [txHash, setTxHash] = useState<string | undefined>(undefined);
306343
const [rozoPaymentId, setRozoPaymentId] = useState<string | undefined>(

packages/connectkit/src/hooks/useStellarPaymentOptions.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { rozoStellarUSDC, WalletPaymentOption } from "@rozoai/intent-common";
1+
import { rozoStellar, WalletPaymentOption } from "@rozoai/intent-common";
22
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
33
import { PayParams } from "../payment/paymentFsm";
44
import { TrpcClient } from "../utils/trpc";
@@ -7,6 +7,7 @@ import {
77
setupRefreshState,
88
shouldSkipRefresh,
99
} from "./refreshUtils";
10+
import { useSupportedChains } from "./useSupportedChains";
1011

1112
/** Wallet payment options. User picks one. */
1213
export function useStellarPaymentOptions({
@@ -22,6 +23,8 @@ export function useStellarPaymentOptions({
2223
isDepositFlow: boolean;
2324
payParams: PayParams | undefined;
2425
}) {
26+
const { tokens } = useSupportedChains();
27+
2528
const [options, setOptions] = useState<WalletPaymentOption[] | null>(null);
2629
const [isLoading, setIsLoading] = useState(false);
2730

@@ -39,7 +42,13 @@ export function useStellarPaymentOptions({
3942
if (!options) return [];
4043

4144
return options
42-
.filter((option) => option.balance.token.token === rozoStellarUSDC.token)
45+
.filter((option) =>
46+
tokens.some(
47+
(t) =>
48+
t.token === option.balance.token.token &&
49+
t.chainId === rozoStellar.chainId
50+
)
51+
)
4352
.map((item) => {
4453
const usd = isDepositFlow ? 0 : usdRequired || 0;
4554

packages/connectkit/src/hooks/useSupportedChains.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,6 @@
11
import { supportedChains, supportedTokens, Token } from "@rozoai/intent-common";
22

3-
/**
4-
* React hook: Returns currently supported wallet payment chains and tokens.
5-
*
6-
* All other chains and tokens from @rozoai/intent-common are included by default.
7-
*
8-
* ### Output:
9-
* - **Chains**: Includes all default chains.
10-
* - **Tokens**: Includes all default tokens.
11-
*
12-
* @param {string} appId - Current Rozo appId.
13-
* @param {number[]} [preferredChains=[]] - Preferred chain IDs.
14-
* @returns {{
15-
* chains: Array<{ chainId: number; [k: string]: any }>;
16-
* tokens: Token[];
17-
* }} Object containing supported chain objects and tokens for wallet UI and payment logic.
18-
*
19-
* @example
20-
* // Usage: retrieve chains and tokens to render for this app/session
21-
* const { chains, tokens } = useSupportedChains("your_appId", [8453, 56]);
22-
*
23-
* // Output: all chains/tokens except Worldchain
24-
*/
25-
export function useSupportedChains(
26-
appId: string,
27-
preferredChains: number[] = []
28-
): {
3+
export function useSupportedChains(): {
294
chains: Array<{ chainId: number; [k: string]: any }>;
305
tokens: Token[];
316
} {

0 commit comments

Comments
 (0)