Repository: Agoric/fast-usdc-lp-ui
Pull request #1: Faq page
Author: login: samsiegart
Created: 2025-03-06T08:15:49Z | Merged: 2025-03-08T03:35:23Z

- Adds FAQ page (see preview)
- Had to add a `.node-version` file to make cloudflare pages work (nvmrc and wrangler.toml and in-cloudflare settings stopped working for some reason)
- Added prettier to repo and CI checks for prettier/eslint


Diff:
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
new file mode 100644
index 0000000..edeae61
--- /dev/null
+++ b/.github/workflows/code-quality.yml
@@ -0,0 +1,31 @@
+name: Code Quality Checks
+
+on:
+  pull_request:
+    branches: [main]
+  push:
+    branches: [main]
+
+jobs:
+  format-lint:
+    name: Format & Lint Check
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v3
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v3
+        with:
+          node-version: '18.18.0'
+          cache: 'yarn'
+
+      - name: Install dependencies
+        run: yarn install --frozen-lockfile
+
+      - name: Check formatting
+        run: yarn format:check
+
+      - name: Run linter
+        run: yarn lint
diff --git a/.node-version b/.node-version
new file mode 100644
index 0000000..67a228a
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+18.18.0
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..8173981
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,8 @@
+{
+  "tabWidth": 2,
+  "useTabs": false,
+  "singleQuote": true,
+  "trailingComma": "all",
+  "printWidth": 80,
+  "arrowParens": "avoid"
+}
diff --git a/eslint.config.js b/eslint.config.js
index 82c2e20..2b3e6cc 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -24,5 +24,5 @@ export default tseslint.config(
         { allowConstantExport: true },
       ],
     },
-  }
+  },
 );
diff --git a/index.html b/index.html
index aeb18b0..8bbacc3 100644
--- a/index.html
+++ b/index.html
@@ -1,10 +1,10 @@
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>FastUSDC LP</title>
+    <title>Fast USDC LP</title>
   </head>
   <body class="bg-slate-100">
     <div id="root"></div>
diff --git a/package.json b/package.json
index 4833746..31c2b08 100644
--- a/package.json
+++ b/package.json
@@ -7,11 +7,14 @@
     "dev": "vite",
     "build": "tsc && NODE_OPTIONS=--max-old-space-size=4096 vite build",
     "lint": "eslint .",
+    "format": "prettier --write \"src/**/*\"",
+    "format:check": "prettier --check \"src/**/*\"",
     "preview": "vite preview"
   },
   "dependencies": {
     "react": "^18.3.1",
-    "react-dom": "^18.3.1"
+    "react-dom": "^18.3.1",
+    "react-router-dom": "6.22.2"
   },
   "devDependencies": {
     "@agoric/ertp": "0.16.3-u18.1",
@@ -23,6 +26,7 @@
     "@interchain-ui/react": "1.21.18",
     "@types/react": "^18.3.18",
     "@types/react-dom": "^18.3.5",
+    "@types/react-router-dom": "^5.3.3",
     "@vitejs/plugin-react": "^4.3.4",
     "autoprefixer": "^10.4.20",
     "buffer": "^6.0.3",
@@ -33,6 +37,7 @@
     "eslint-plugin-react-refresh": "^0.4.16",
     "globals": "^15.14.0",
     "postcss": "^8.4.49",
+    "prettier": "^3.5.3",
     "react-loader-spinner": "6.1.6",
     "react-toastify": "11.0.2",
     "ses": "^1.10.0",
diff --git a/src/App.tsx b/src/App.tsx
index 5fcb702..e9e53b5 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -5,6 +5,9 @@ import { ThemeProvider, useTheme } from '@interchain-ui/react';
 import { useEffect, useState } from 'react';
 import Content from './components/Content';
 import { toast, type Id as ToastId } from 'react-toastify';
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import FAQ from './components/FAQ';
+
 import '@agoric/react-components/dist/style.css';
 
 const localnet = {
@@ -55,19 +58,25 @@ function App() {
   };
 
   return (
-    <ThemeProvider defaultTheme="light">
-      <div className={themeClass}>
-        <AgoricProvider
-          wallets={wallets.extension}
-          agoricNetworkConfigs={[localnet, devnet, mainnet]}
-          onConnectionError={onError}
-          modalTheme={{ defaultTheme: 'light' }}
-        >
-          <Header />
-          <Content />
-        </AgoricProvider>
-      </div>
-    </ThemeProvider>
+    <BrowserRouter>
+      <ThemeProvider defaultTheme="light">
+        <div className={themeClass}>
+          <AgoricProvider
+            wallets={wallets.extension}
+            agoricNetworkConfigs={[localnet, devnet, mainnet]}
+            onConnectionError={onError}
+            modalTheme={{ defaultTheme: 'light' }}
+            defaultChainName="agoric"
+          >
+            <Header />
+            <Routes>
+              <Route path="/" element={<Content />} />
+              <Route path="/faq" element={<FAQ />} />
+            </Routes>
+          </AgoricProvider>
+        </div>
+      </ThemeProvider>
+    </BrowserRouter>
   );
 }
 
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
index 9d7f3fa..99db560 100644
--- a/src/components/Content.tsx
+++ b/src/components/Content.tsx
@@ -31,13 +31,13 @@ const usePoolMetrics = () => {
           if (errorId && toast.isActive(errorId)) return;
           const id = toast.error(
             'Could not read FastUSDC contract, is it deployed on chain?',
-            { autoClose: false }
+            { autoClose: false },
           );
           setErrorId(id);
           return;
         }
         setMetrics(metrics);
-      }
+      },
     );
 
     return () => {
@@ -64,7 +64,7 @@ const Content = () => {
     : '0.00';
 
   const fastLPBalance = purses?.find(
-    ({ pursePetname }) => pursePetname === 'FastLP'
+    ({ pursePetname }) => pursePetname === 'FastLP',
   )?.currentAmount as Amount<'nat'>;
 
   const shareWorth = metrics?.shareWorth;
diff --git a/src/components/Deposit.tsx b/src/components/Deposit.tsx
index 1893b02..ddc80e4 100644
--- a/src/components/Deposit.tsx
+++ b/src/components/Deposit.tsx
@@ -16,7 +16,7 @@ const Deposit = ({ shareWorth }: Props) => {
   const [inProgress, setInProgress] = useState(false);
   const { purses, makeOffer } = useAgoric();
   const usdcPurseAmount = purses?.find(
-    ({ pursePetname }) => pursePetname === 'USDC'
+    ({ pursePetname }) => pursePetname === 'USDC',
   )?.currentAmount as Amount<'nat'>;
   const usdcBalance = usdcPurseAmount?.value ?? 0n;
 
@@ -62,7 +62,7 @@ const Deposit = ({ shareWorth }: Props) => {
           toast.warning('Offer Refunded');
           setInProgress(false);
         }
-      }
+      },
     );
   };
 
@@ -82,7 +82,7 @@ const Deposit = ({ shareWorth }: Props) => {
         <div
           className={clsx(
             'text-gray-500 text-sm mt-1',
-            isMaxExceeded && 'text-red-500'
+            isMaxExceeded && 'text-red-500',
           )}
         >
           <span className="font-medium">Purse Balance:</span>{' '}
@@ -95,7 +95,7 @@ const Deposit = ({ shareWorth }: Props) => {
         className={clsx(
           'w-full flex flex-row items-center justify-center bg-agoric-red p-2 px-3 h-12 rounded-lg text-white hover:bg-opacity-85 active:bg-opacity-70 active:scale-95 transition-all outline-none ring-offset-2 focus:ring-2',
           (isDisabled || inProgress) && 'cursor-not-allowed',
-          isDisabled && 'bg-gray-300'
+          isDisabled && 'bg-gray-300',
         )}
       >
         {inProgress ? (
diff --git a/src/components/FAQ.tsx b/src/components/FAQ.tsx
new file mode 100644
index 0000000..be2f32e
--- /dev/null
+++ b/src/components/FAQ.tsx
@@ -0,0 +1,298 @@
+import { useState } from 'react';
+
+type FAQSection = {
+  title: string;
+  content: React.ReactNode;
+};
+
+const FAQ = () => {
+  const [expandedSections, setExpandedSections] = useState<Set<number>>(
+    new Set(),
+  );
+
+  const toggleSection = (index: number) => {
+    setExpandedSections(prevExpanded => {
+      const newExpanded = new Set(prevExpanded);
+      if (newExpanded.has(index)) {
+        newExpanded.delete(index);
+      } else {
+        newExpanded.add(index);
+      }
+      return newExpanded;
+    });
+  };
+
+  const faqSections: FAQSection[] = [
+    {
+      title: 'What does Fast USDC do?',
+      content: (
+        <p>
+          <b>Fast USDC</b> is a product built with Agoric's on-chain
+          Orchestration API. It helps speed up USDC transfers between chains by
+          reducing wait times for users coming from EVM chains.
+          <br />
+          <br />
+          Instead of waiting for the standard CCTP bridge to finalize, a{' '}
+          <b>Liquidity Provider</b> can front the USDC to the user right away,
+          knowing the official transfer is on its way. LPs can earn fees from
+          users that are willing to pay to get their funds quickly to the End
+          destination.
+        </p>
+      ),
+    },
+    {
+      title: 'Dashboard Overview',
+      content: (
+        <div>
+          <p>
+            <strong>What information can I see on my Dashboard?</strong>
+          </p>
+          <p>Your Dashboard displays four key metrics:</p>
+          <ul className="list-disc pl-6 mt-2">
+            <li>Total Pool Balance: The total amount of USDC in the pool</li>
+            <li>
+              Your Pool Share: Your share amount and percentage of the total
+              pool
+            </li>
+            <li>
+              Awaiting Settlement: Amount of your USDC currently being processed
+            </li>
+            <li>
+              Pool Fees Earned: Total earned fees by the entire pool in USDC
+            </li>
+          </ul>
+        </div>
+      ),
+    },
+    {
+      title: 'Wallet Connection',
+      content: (
+        <div>
+          <p>
+            <strong>How do I connect my wallet?</strong>
+          </p>
+          <p>
+            Use the wallet button in the top right corner of the interface. Your
+            wallet address will be displayed once connected.
+          </p>
+
+          <p className="mt-4">
+            <strong>Why does my balance show as $0.00?</strong>
+          </p>
+          <p>If all your balances show as $0.00, please verify:</p>
+          <ul className="list-disc pl-6 mt-2">
+            <li>Your wallet is properly connected</li>
+            <li>You're on the correct network</li>
+            <li>You have USDC in your wallet</li>
+          </ul>
+        </div>
+      ),
+    },
+    {
+      title: 'Deposits',
+      content: (
+        <div>
+          <p>
+            <strong>How do I deposit USDC to the pool?</strong>
+          </p>
+          <ol className="list-decimal pl-6 mt-2">
+            <li>Check your Purse Balance in the Deposit USDC section</li>
+            <li>Enter an amount up to your available purse balance</li>
+            <li>
+              If you don't have any USDC, you may source it from
+              https://www.usdc.com/
+            </li>
+            <li>Click the "Deposit" button</li>
+            <li>Confirm the transaction in your wallet</li>
+          </ol>
+
+          <p className="mt-4">
+            <strong>What's the minimum deposit amount?</strong>
+          </p>
+          <p>
+            You can deposit any amount up to your Purse Balance, but always
+            consider gas fees especially when making smaller deposits.
+          </p>
+
+          <p className="mt-4">
+            <strong>
+              What happens if I enter an amount greater than my balance?
+            </strong>
+          </p>
+          <p>
+            The system will prevent you from depositing more than your available
+            Purse Balance.
+          </p>
+        </div>
+      ),
+    },
+    {
+      title: 'Withdrawals',
+      content: (
+        <div>
+          <p>
+            <strong>How do I withdraw my USDC?</strong>
+          </p>
+          <ol className="list-decimal pl-6 mt-2">
+            <li>Go to the "Withdraw USDC" section</li>
+            <li>Enter the amount you wish to withdraw</li>
+            <li>Verify it's within your maximum withdrawable amount</li>
+            <li>Click the "Withdraw" button to process</li>
+          </ol>
+
+          <p className="mt-4">
+            <strong>What is "Max Withdrawable"?</strong>
+          </p>
+          <p>
+            The Max Withdrawable amount shown below the withdrawal input field
+            indicates the maximum USDC you can currently withdraw based on your
+            pool share and available liquidity.
+          </p>
+
+          <p className="mt-4">
+            <strong>
+              Why might my max withdrawable amount be different from my pool
+              share?
+            </strong>
+          </p>
+          <p>
+            Your max withdrawable amount might be lower than your total pool
+            share if:
+          </p>
+          <ul className="list-disc pl-6 mt-2">
+            <li>There are pending settlements</li>
+            <li>There are active bridge transactions being processed</li>
+          </ul>
+        </div>
+      ),
+    },
+    {
+      title: 'Pool Metrics',
+      content: (
+        <div>
+          <p>
+            <strong>How can I track my position in the pool?</strong>
+          </p>
+          <p>
+            Your Dashboard currently only shows your percentage share of the
+            total pool. It is your responsibility to track the USDC amounts you
+            have deposited, and you may use the percentage share displayed in
+            your Dashboard to calculate the value of your position.
+          </p>
+
+          <p className="mt-4">
+            <strong>Why is my pool share shown as a percentage?</strong>
+          </p>
+          <p>
+            The percentage helps you understand your proportional ownership of
+            the pool, which determines your share of fees and withdrawal rights.
+          </p>
+
+          <p className="mt-4">
+            <strong>How do I start earning fees?</strong>
+          </p>
+          <p>To start earning fees:</p>
+          <ol className="list-decimal pl-6 mt-2">
+            <li>
+              Deposit some of your available USDC from the Purse Balance into
+              the pool
+            </li>
+            <li>
+              Maintain your position while the pool facilitates bridging
+              transactions
+            </li>
+            <li>Fees will accumulate based on your pool share percentage</li>
+          </ol>
+        </div>
+      ),
+    },
+    {
+      title: 'Support',
+      content: (
+        <div>
+          <p>
+            <strong>What should I do if I encounter issues?</strong>
+          </p>
+          <p>If you experience any issues:</p>
+          <ol className="list-decimal pl-6 mt-2">
+            <li>
+              Join the Agoric Discord:{' '}
+              <a
+                href="https://agoric.com/discord"
+                target="_blank"
+                rel="noopener noreferrer"
+                className="text-blue-500 hover:text-blue-700 underline"
+              >
+                https://agoric.com/discord
+              </a>
+            </li>
+            <li>Select the community role in the onboarding page</li>
+            <li>
+              Go to #get-roles channel (unlocked for server members) and select
+              the Fast USDC LP role
+            </li>
+            <li>Go to #fast-usdc-lp-support channel</li>
+          </ol>
+        </div>
+      ),
+    },
+  ];
+
+  return (
+    <div className="container mx-auto px-4 py-8 max-w-4xl">
+      <h1 className="text-3xl font-bold text-center mb-8 text-gray-800">
+        Fast USDC Liquidity Pool - Frequently Asked Questions
+      </h1>
+
+      <div className="space-y-4">
+        {faqSections.map((section, index) => (
+          <div
+            key={index}
+            className="border border-gray-200 rounded-lg overflow-hidden shadow-sm"
+          >
+            <button
+              className="w-full px-6 py-4 text-left bg-white hover:bg-gray-50 flex justify-between items-center focus:outline-none"
+              onClick={() => toggleSection(index)}
+            >
+              <h3 className="text-lg font-medium text-gray-800">
+                {section.title}
+              </h3>
+              <span className="text-gray-600">
+                <svg
+                  xmlns="http://www.w3.org/2000/svg"
+                  className={`h-5 w-5 transition-transform duration-300 ${
+                    expandedSections.has(index) ? 'rotate-180' : ''
+                  }`}
+                  viewBox="0 0 20 20"
+                  fill="currentColor"
+                >
+                  <path
+                    fillRule="evenodd"
+                    d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 011.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
+                    clipRule="evenodd"
+                  />
+                </svg>
+              </span>
+            </button>
+
+            <div
+              className={`grid transition-all duration-300 ease-in-out ${
+                expandedSections.has(index)
+                  ? 'grid-rows-[1fr]'
+                  : 'grid-rows-[0fr]'
+              }`}
+            >
+              <div className="overflow-hidden">
+                <div className="px-6 py-4 bg-white border-t border-gray-200">
+                  {section.content}
+                </div>
+              </div>
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+};
+
+export default FAQ;
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 1e45a21..870bf7d 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,13 +1,93 @@
 import { ConnectWalletButton, NetworkDropdown } from '@agoric/react-components';
+import { NavLink, useLocation } from 'react-router-dom';
+import { useEffect, useRef, useState } from 'react';
 
 const Header = () => {
+  const location = useLocation();
+  const [activeIndex, setActiveIndex] = useState(0);
+  const homeRef = useRef<HTMLAnchorElement | null>(null);
+  const faqRef = useRef<HTMLAnchorElement | null>(null);
+  const [indicatorStyle, setIndicatorStyle] = useState({
+    left: '0',
+    width: '0',
+  });
+
+  // Update active link based on current route
+  useEffect(() => {
+    if (location.pathname === '/') {
+      setActiveIndex(0);
+    } else if (location.pathname === '/faq') {
+      setActiveIndex(1);
+    }
+  }, [location]);
+
+  // Update underline position and width based on active link
+  useEffect(() => {
+    const updateIndicator = () => {
+      const activeRef = activeIndex === 0 ? homeRef.current : faqRef.current;
+
+      if (activeRef) {
+        const { offsetLeft, offsetWidth } = activeRef;
+        setIndicatorStyle({
+          left: `${offsetLeft}px`,
+          width: `${offsetWidth}px`,
+        });
+      }
+    };
+
+    updateIndicator();
+    // Update on window resize
+    window.addEventListener('resize', updateIndicator);
+    return () => {
+      window.removeEventListener('resize', updateIndicator);
+    };
+  }, [activeIndex]);
+
   return (
-    <div className="w-full sticky top-0 sm:h-16 bg-white flex items-center justify-center md:justify-between flex-row shadow-md px-4">
-      <div className="hidden md:flex flex-row justify-start items-center gap-2">
+    <div className="w-full sticky top-0 sm:h-16 bg-white flex items-center justify-center lg:justify-between flex-row shadow-md px-4">
+      <div className="hidden lg:flex flex-row justify-start items-center gap-2">
         <img className="h-12" src="Agoric-logo-color.svg" />
         <h1 className="text-2xl text-gray-800">Fast USDC</h1>
       </div>
       <div className="flex flex-col m-2 sm:m-0 sm:flex-row justify-start sm:items-center gap-2">
+        <nav className="mr-4 relative">
+          <div className="flex space-x-6 items-center">
+            <NavLink
+              ref={homeRef}
+              to="/"
+              className={({ isActive }) =>
+                `text-lg py-2 transition-colors duration-200 ${
+                  isActive
+                    ? 'text-agoric-red font-medium'
+                    : 'text-gray-800 hover:text-agoric-red'
+                }`
+              }
+            >
+              Home
+            </NavLink>
+            <NavLink
+              ref={faqRef}
+              to="/faq"
+              className={({ isActive }) =>
+                `text-lg py-2 transition-colors duration-200 ${
+                  isActive
+                    ? 'text-agoric-red font-medium'
+                    : 'text-gray-800 hover:text-agoric-red'
+                }`
+              }
+            >
+              FAQ
+            </NavLink>
+          </div>
+          {/* Animated underline indicator */}
+          <div
+            className="absolute bottom-0 h-0.5 bg-agoric-red transition-all duration-300 ease-in-out"
+            style={{
+              left: indicatorStyle.left,
+              width: indicatorStyle.width,
+            }}
+          />
+        </nav>
         <NetworkDropdown />
         <ConnectWalletButton className="bg-agoric-red p-2 px-3 h-12 rounded-[4px] text-white hover:bg-opacity-85 active:bg-opacity-70 active:scale-95 transition-all outline-none ring-offset-2 focus:ring-2" />
       </div>
diff --git a/src/components/Withdraw.tsx b/src/components/Withdraw.tsx
index 0bf50bf..163d271 100644
--- a/src/components/Withdraw.tsx
+++ b/src/components/Withdraw.tsx
@@ -17,7 +17,7 @@ const Withdraw = ({ availableToWithdraw, shareWorth }: Props) => {
   const [inProgress, setInProgress] = useState(false);
   const { makeOffer, purses } = useAgoric();
   const usdcPurseAmount = purses?.find(
-    ({ pursePetname }) => pursePetname === 'USDC'
+    ({ pursePetname }) => pursePetname === 'USDC',
   )?.currentAmount as Amount<'nat'>;
 
   const isMaxExceeded = !!value && value > availableToWithdraw;
@@ -62,7 +62,7 @@ const Withdraw = ({ availableToWithdraw, shareWorth }: Props) => {
           toast.warning('Offer Refunded');
           setInProgress(false);
         }
-      }
+      },
     );
   };
 
@@ -82,7 +82,7 @@ const Withdraw = ({ availableToWithdraw, shareWorth }: Props) => {
         <div
           className={clsx(
             'text-gray-500 text-sm mt-1',
-            isMaxExceeded && 'text-red-500'
+            isMaxExceeded && 'text-red-500',
           )}
         >
           <span className="font-medium">Max Withdrawable:</span>{' '}
@@ -95,7 +95,7 @@ const Withdraw = ({ availableToWithdraw, shareWorth }: Props) => {
         className={clsx(
           'w-full flex flex-row items-center justify-center bg-agoric-red p-2 px-3 h-12 rounded-lg text-white hover:bg-opacity-85 active:bg-opacity-70 active:scale-95 transition-all outline-none ring-offset-2 focus:ring-2',
           (isDisabled || inProgress) && 'cursor-not-allowed',
-          isDisabled && 'bg-gray-300'
+          isDisabled && 'bg-gray-300',
         )}
       >
         {inProgress ? (
diff --git a/src/main.tsx b/src/main.tsx
index ed1de58..ab8f9df 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -9,5 +9,5 @@ createRoot(document.getElementById('root')!).render(
   <StrictMode>
     <App />
     <ToastContainer className="mt-16" />
-  </StrictMode>
+  </StrictMode>,
 );
diff --git a/yarn.lock b/yarn.lock
index 996fa3c..3b4fc23 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5013,6 +5013,11 @@
     "@react-types/overlays" "^3.8.11"
     "@react-types/shared" "^3.26.0"
 
+"@remix-run/router@1.15.2":
+  version "1.15.2"
+  resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.2.tgz#35726510d332ba5349c6398d13259d5da184553d"
+  integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==
+
 "@rollup/plugin-commonjs@^19.0.0":
   version "19.0.2"
   resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.2.tgz#1ccc3d63878d1bc9846f8969f09dd3b3e4ecc244"
@@ -5543,6 +5548,11 @@
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
   integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
 
+"@types/history@^4.7.11":
+  version "4.7.11"
+  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
+  integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
+
 "@types/json-schema@^7.0.15":
   version "7.0.15"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
@@ -5580,6 +5590,30 @@
   resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.5.tgz#45f9f87398c5dcea085b715c58ddcf1faf65f716"
   integrity sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==
 
+"@types/react-router-dom@^5.3.3":
+  version "5.3.3"
+  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
+  integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
+  dependencies:
+    "@types/history" "^4.7.11"
+    "@types/react" "*"
+    "@types/react-router" "*"
+
+"@types/react-router@*":
+  version "5.1.20"
+  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c"
+  integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==
+  dependencies:
+    "@types/history" "^4.7.11"
+    "@types/react" "*"
+
+"@types/react@*":
+  version "19.0.10"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-19.0.10.tgz#d0c66dafd862474190fe95ce11a68de69ed2b0eb"
+  integrity sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==
+  dependencies:
+    csstype "^3.0.2"
+
 "@types/react@^18.3.18":
   version "18.3.18"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.18.tgz#9b382c4cd32e13e463f97df07c2ee3bbcd26904b"
@@ -9709,6 +9743,11 @@ prelude-ls@^1.2.1:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
   integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
 
+prettier@^3.5.3:
+  version "3.5.3"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5"
+  integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==
+
 process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -10005,6 +10044,21 @@ react-remove-scroll@2.5.4:
     use-callback-ref "^1.3.0"
     use-sidecar "^1.1.2"
 
+react-router-dom@6.22.2:
+  version "6.22.2"
+  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.2.tgz#8233968a8a576f3006e5549c80f3527d2598fc9c"
+  integrity sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==
+  dependencies:
+    "@remix-run/router" "1.15.2"
+    react-router "6.22.2"
+
+react-router@6.22.2:
+  version "6.22.2"
+  resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.2.tgz#27e77e4c635a5697693b922d131d773451c98a5b"
+  integrity sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==
+  dependencies:
+    "@remix-run/router" "1.15.2"
+
 react-stately@^3.27.1:
   version "3.34.0"
   resolved "https://registry.yarnpkg.com/react-stately/-/react-stately-3.34.0.tgz#981713fa00bb2ef07b981dbce466d9a5774101d4"


Reviews:
user: login: turadg
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T23:14:31Z
user: login: samsiegart
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T03:30:16Z

Review comments:
diff_hunk: @@ -7,11 +7,15 @@
     "dev": "vite",
     "build": "tsc && NODE_OPTIONS=--max-old-space-size=4096 vite build",
     "lint": "eslint .",
+    "lint:fix": "eslint --fix .",
path: package.json
user: login: turadg
body: consider removing. It's easy enough to run `eslint --fix .` and it's never something CI has to do.

I'd like to get rid of it everywhere
created_at: 2025-03-07T23:13:21Z
author_association: MEMBER
diff_hunk: @@ -7,11 +7,15 @@
     "dev": "vite",
     "build": "tsc && NODE_OPTIONS=--max-old-space-size=4096 vite build",
     "lint": "eslint .",
+    "lint:fix": "eslint --fix .",
path: package.json
user: login: samsiegart
body: I think it'd have to be `yarn run eslint --fix .` right? Removed though.
created_at: 2025-03-08T03:30:16Z
author_association: COLLABORATOR

PR comments:
user: login: cloudflare-workers-and-pages[bot]
created_at: 2025-03-06T08:16:08Z
body: ## Deploying fast-usdc-lp-ui with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>bbd80fa</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://6bff7cb0.fast-usdc-lp-ui.pages.dev'>https://6bff7cb0.fast-usdc-lp-ui.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://faq.fast-usdc-lp-ui.pages.dev'>https://faq.fast-usdc-lp-ui.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/0c4635effffcd7f36d1b9f0425a4367a/pages/view/fast-usdc-lp-ui/6bff7cb0-59f5-4642-a19f-985baaf0059c)

author_association: NONE
user: login: samsiegart
created_at: 2025-03-08T03:35:18Z
body: Just updated the FAQ content again as requested by product, will go ahead and merge
author_association: COLLABORATOR