From 0b7ad74962f6b527455137a4a8654464db509894 Mon Sep 17 00:00:00 2001 From: willyogo Date: Tue, 20 Jan 2026 16:27:34 -0600 Subject: [PATCH 1/2] Add Matcha referral tracking parameter to swap URLs Appends ref query parameter to Matcha trade URLs in both the Swap fidget and the getMatchaUrl helper. Uses NEXT_PUBLIC_MATCHA_REF env var with "nounspace" as the default value. Co-Authored-By: Claude Opus 4.5 --- src/common/lib/utils/links.ts | 3 ++- src/fidgets/swap/Swap.tsx | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/lib/utils/links.ts b/src/common/lib/utils/links.ts index 11e6f6ab8..b5bb9032e 100644 --- a/src/common/lib/utils/links.ts +++ b/src/common/lib/utils/links.ts @@ -15,7 +15,8 @@ export function getGeckoIframe(address: Address, network: EtherScanChainName) { export function getMatchaUrl(address: Address, network: EtherScanChainName) { const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - return `https://matcha.xyz/trade?sellAddress=${ethAddress}&buyAddress=${address}&sellChain=${network}&buyChain=${EtherScanChains[network]}`; + const matchaRef = process.env.NEXT_PUBLIC_MATCHA_REF || "nounspace"; + return `https://matcha.xyz/trade?sellAddress=${ethAddress}&buyAddress=${address}&sellChain=${network}&buyChain=${EtherScanChains[network]}&ref=${matchaRef}`; } export function getDexScreenerUrl( diff --git a/src/fidgets/swap/Swap.tsx b/src/fidgets/swap/Swap.tsx index 7368e4cdd..e2f048489 100644 --- a/src/fidgets/swap/Swap.tsx +++ b/src/fidgets/swap/Swap.tsx @@ -146,6 +146,8 @@ const Swap: React.FC> = ({ } if (optionalFeeRecipient) params.append("feeRecipient", optionalFeeRecipient); + const matchaRef = process.env.NEXT_PUBLIC_MATCHA_REF || "nounspace"; + params.append("ref", matchaRef); return `${matchaBaseUrl}?${params.toString()}`; }; From 599865a2f6ad6733eb8bfb84370e2d39038bdb09 Mon Sep 17 00:00:00 2001 From: willyogo Date: Tue, 20 Jan 2026 16:39:48 -0600 Subject: [PATCH 2/2] Use community config displayName for Matcha ref parameter Replaces environment variable approach with dynamic community config. Creates SystemConfigProvider to expose systemConfig to client components, allowing the Swap fidget to use brand.displayName as the Matcha referral parameter. Changes: - Add SystemConfigProvider with useSystemConfig hook - Update Providers to accept and wrap with SystemConfigProvider - Update Swap fidget to use useSystemConfig for ref parameter - Update getMatchaUrl helper to accept optional ref parameter Co-Authored-By: Claude Opus 4.5 --- src/app/layout.tsx | 2 +- src/common/lib/utils/links.ts | 4 +- src/common/providers/SystemConfigProvider.tsx | 27 +++++++++ src/common/providers/index.tsx | 58 +++++++++++-------- src/fidgets/swap/Swap.tsx | 16 ++--- 5 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 src/common/providers/SystemConfigProvider.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8c04d7fe9..a912d8369 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -186,7 +186,7 @@ export default async function RootLayout({ } > - {sidebarLayout(children, systemConfig)} + {sidebarLayout(children, systemConfig)} ); diff --git a/src/common/lib/utils/links.ts b/src/common/lib/utils/links.ts index b5bb9032e..6fc3f2a9d 100644 --- a/src/common/lib/utils/links.ts +++ b/src/common/lib/utils/links.ts @@ -13,9 +13,9 @@ export function getGeckoIframe(address: Address, network: EtherScanChainName) { return `https://www.geckoterminal.com/${getGeckoNetwork(network)}/pools/${address}?embed=1&info=0&swaps=0&grayscale=0&light_chart=1`; } -export function getMatchaUrl(address: Address, network: EtherScanChainName) { +export function getMatchaUrl(address: Address, network: EtherScanChainName, ref?: string) { const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - const matchaRef = process.env.NEXT_PUBLIC_MATCHA_REF || "nounspace"; + const matchaRef = ref || "nounspace"; return `https://matcha.xyz/trade?sellAddress=${ethAddress}&buyAddress=${address}&sellChain=${network}&buyChain=${EtherScanChains[network]}&ref=${matchaRef}`; } diff --git a/src/common/providers/SystemConfigProvider.tsx b/src/common/providers/SystemConfigProvider.tsx new file mode 100644 index 000000000..8e5e973e9 --- /dev/null +++ b/src/common/providers/SystemConfigProvider.tsx @@ -0,0 +1,27 @@ +"use client"; + +import React, { createContext, useContext } from "react"; +import { SystemConfig } from "@/config"; + +const SystemConfigContext = createContext(null); + +export const SystemConfigProvider: React.FC<{ + children: React.ReactNode; + systemConfig: SystemConfig; +}> = ({ children, systemConfig }) => { + return ( + + {children} + + ); +}; + +export const useSystemConfig = (): SystemConfig => { + const context = useContext(SystemConfigContext); + if (!context) { + throw new Error("useSystemConfig must be used within SystemConfigProvider"); + } + return context; +}; + +export default SystemConfigProvider; diff --git a/src/common/providers/index.tsx b/src/common/providers/index.tsx index 32b5278dd..72eede3fb 100644 --- a/src/common/providers/index.tsx +++ b/src/common/providers/index.tsx @@ -17,6 +17,8 @@ import MobilePreviewProvider from "./MobilePreviewProvider"; import { SharedDataProvider } from "./SharedDataProvider"; import { MiniKitContextProvider } from "./MiniKitProvider"; import { GlobalErrorHandler } from "./GlobalErrorHandler"; +import { SystemConfigProvider } from "./SystemConfigProvider"; +import { SystemConfig } from "@/config"; const RarelyUpdatedProviders = React.memo( function RarelyUpdatedProviders({ @@ -38,33 +40,41 @@ const RarelyUpdatedProviders = React.memo( }, ); -export default function Providers({ children }: { children: React.ReactNode }) { +export default function Providers({ + children, + systemConfig, +}: { + children: React.ReactNode; + systemConfig: SystemConfig; +}) { return ( <> - - - - - - - - - - - - {children} - - - - - - - - - - - + + + + + + + + + + + + + {children} + + + + + + + + + + + + ); } diff --git a/src/fidgets/swap/Swap.tsx b/src/fidgets/swap/Swap.tsx index e2f048489..64847bac5 100644 --- a/src/fidgets/swap/Swap.tsx +++ b/src/fidgets/swap/Swap.tsx @@ -11,6 +11,7 @@ import { import { BsArrowRepeat } from "react-icons/bs"; import { mobileStyleSettings, WithMargin } from "../helpers"; import ShadowSelector from "@/common/components/molecules/ShadowSelector"; +import { useSystemConfig } from "@/common/providers/SystemConfigProvider"; type MatchaFidgetSettings = { defaultSellToken: string; @@ -131,10 +132,12 @@ const Swap: React.FC> = ({ size = 0.8, }, }) => { + const systemConfig = useSystemConfig(); + const matchaRef = systemConfig.brand.displayName; const matchaBaseUrl = "https://matcha.xyz/trade"; const [url, setUrl] = React.useState(""); - const buildMatchaUrl = () => { + const buildMatchaUrl = React.useCallback(() => { const params = new URLSearchParams(); if (defaultSellToken) params.append("sellAddress", defaultSellToken); if (defaultBuyToken) params.append("buyAddress", defaultBuyToken); @@ -146,20 +149,13 @@ const Swap: React.FC> = ({ } if (optionalFeeRecipient) params.append("feeRecipient", optionalFeeRecipient); - const matchaRef = process.env.NEXT_PUBLIC_MATCHA_REF || "nounspace"; params.append("ref", matchaRef); return `${matchaBaseUrl}?${params.toString()}`; - }; + }, [defaultSellToken, defaultBuyToken, fromChain, toChain, optionalFeeRecipient, matchaRef]); React.useEffect(() => { setUrl(buildMatchaUrl()); - }, [ - defaultSellToken, - defaultBuyToken, - fromChain, - toChain, - optionalFeeRecipient, - ]); + }, [buildMatchaUrl]); const scaleValue = size;